diff --git a/CryptoExchange.Net/Clients/RestApiClient.cs b/CryptoExchange.Net/Clients/RestApiClient.cs index 3c22555..4d4504a 100644 --- a/CryptoExchange.Net/Clients/RestApiClient.cs +++ b/CryptoExchange.Net/Clients/RestApiClient.cs @@ -437,215 +437,6 @@ namespace CryptoExchange.Net.Clients return request; } - /// - /// Execute a request to the uri and returns if it was successful - /// - /// The uri to send the request to - /// The method of the request - /// Cancellation token - /// The parameters of the request - /// Whether or not the request should be authenticated - /// The format of the body content - /// Where the parameters should be placed, overwrites the value set in the client - /// How array parameters should be serialized, overwrites the value set in the client - /// Credits used for the request - /// Additional headers to send with the request - /// The ratelimit gate to use - /// - [return: NotNull] - protected virtual async Task SendRequestAsync( - Uri uri, - HttpMethod method, - CancellationToken cancellationToken, - Dictionary? parameters = null, - bool signed = false, - RequestBodyFormat? requestBodyFormat = null, - HttpMethodParameterPosition? parameterPosition = null, - ArrayParametersSerialization? arraySerialization = null, - int requestWeight = 1, - Dictionary? additionalHeaders = null, - IRateLimitGate? gate = null) - { - int currentTry = 0; - while (true) - { - currentTry++; - var request = await PrepareRequestAsync(uri, method, cancellationToken, parameters, signed, requestBodyFormat, parameterPosition, arraySerialization, requestWeight, additionalHeaders, gate).ConfigureAwait(false); - if (!request) - return new WebCallResult(request.Error!); - - var result = await GetResponseAsync(request.Data, gate, cancellationToken).ConfigureAwait(false); - if (!result) - _logger.RestApiErrorReceived(result.RequestId, result.ResponseStatusCode, (long)Math.Floor(result.ResponseTime!.Value.TotalMilliseconds), result.Error?.ToString()); - else - _logger.RestApiResponseReceived(result.RequestId, result.ResponseStatusCode, (long)Math.Floor(result.ResponseTime!.Value.TotalMilliseconds), OutputOriginalData ? result.OriginalData : "[Data only available when OutputOriginal = true]"); - - if (await ShouldRetryRequestAsync(gate, result, currentTry).ConfigureAwait(false)) - continue; - - return result.AsDataless(); - } - } - - /// - /// Execute a request to the uri and deserialize the response into the provided type parameter - /// - /// The type to deserialize into - /// The uri to send the request to - /// The method of the request - /// Cancellation token - /// The parameters of the request - /// Whether or not the request should be authenticated - /// The format of the body content - /// Where the parameters should be placed, overwrites the value set in the client - /// How array parameters should be serialized, overwrites the value set in the client - /// Credits used for the request - /// Additional headers to send with the request - /// The ratelimit gate to use - /// Whether caching should be prevented for this request - /// - [return: NotNull] - protected virtual async Task> SendRequestAsync( - Uri uri, - HttpMethod method, - CancellationToken cancellationToken, - Dictionary? parameters = null, - bool signed = false, - RequestBodyFormat? requestBodyFormat = null, - HttpMethodParameterPosition? parameterPosition = null, - ArrayParametersSerialization? arraySerialization = null, - int requestWeight = 1, - Dictionary? additionalHeaders = null, - IRateLimitGate? gate = null, - bool preventCaching = false - ) where T : class - { - var key = uri.ToString() + method + signed + parameters?.ToFormData(); - if (ShouldCache(method) && !preventCaching) - { - _logger.CheckingCache(key); - var cachedValue = _cache.Get(key, ClientOptions.CachingMaxAge); - if (cachedValue != null) - { - _logger.CacheHit(key); - var original = (WebCallResult)cachedValue; - return original.Cached(); - } - - _logger.CacheNotHit(key); - } - - int currentTry = 0; - while (true) - { - currentTry++; - var request = await PrepareRequestAsync(uri, method, cancellationToken, parameters, signed, requestBodyFormat, parameterPosition, arraySerialization, requestWeight, additionalHeaders, gate).ConfigureAwait(false); - if (!request) - return new WebCallResult(request.Error!); - - var result = await GetResponseAsync(request.Data, gate, cancellationToken).ConfigureAwait(false); - if (!result) - _logger.RestApiErrorReceived(result.RequestId, result.ResponseStatusCode, (long)Math.Floor(result.ResponseTime!.Value.TotalMilliseconds), result.Error?.ToString()); - else - _logger.RestApiResponseReceived(result.RequestId, result.ResponseStatusCode, (long)Math.Floor(result.ResponseTime!.Value.TotalMilliseconds), OutputOriginalData ? result.OriginalData : "[Data only available when OutputOriginal = true]"); - - if (await ShouldRetryRequestAsync(gate, result, currentTry).ConfigureAwait(false)) - continue; - - if (result.Success && - ShouldCache(method) && - !preventCaching) - { - _cache.Add(key, result); - } - - return result; - } - } - - /// - /// Prepares a request to be sent to the server - /// - /// The uri to send the request to - /// The method of the request - /// Cancellation token - /// The parameters of the request - /// Whether or not the request should be authenticated - /// The format of the body content - /// Where the parameters should be placed, overwrites the value set in the client - /// How array parameters should be serialized, overwrites the value set in the client - /// Credits used for the request - /// Additional headers to send with the request - /// The rate limit gate to use - /// - protected virtual async Task> PrepareRequestAsync( - Uri uri, - HttpMethod method, - CancellationToken cancellationToken, - Dictionary? parameters = null, - bool signed = false, - RequestBodyFormat? requestBodyFormat = null, - HttpMethodParameterPosition? parameterPosition = null, - ArrayParametersSerialization? arraySerialization = null, - int requestWeight = 1, - Dictionary? additionalHeaders = null, - IRateLimitGate? gate = null) - { - var requestId = ExchangeHelpers.NextId(); - - if (signed) - { - if (AuthenticationProvider == null) - { - _logger.RestApiNoApiCredentials(requestId, uri.AbsolutePath); - return new CallResult(new NoApiCredentialsError()); - } - - var syncTask = SyncTimeAsync(); - var timeSyncInfo = GetTimeSyncInfo(); - - if (timeSyncInfo != null && timeSyncInfo.TimeSyncState.LastSyncTime == default) - { - // Initially with first request we'll need to wait for the time syncing, if it's not the first request we can just continue - var syncTimeResult = await syncTask.ConfigureAwait(false); - if (!syncTimeResult) - { - _logger.RestApiFailedToSyncTime(requestId, syncTimeResult.Error!.ToString()); - return syncTimeResult.As(default); - } - } - } - - if (requestWeight != 0) - { - if (gate == null) - throw new Exception("Ratelimit gate not set when request weight is not 0"); - - if (ClientOptions.RateLimiterEnabled) - { - var limitResult = await gate.ProcessAsync(_logger, requestId, RateLimitItemType.Request, new RequestDefinition(uri.AbsolutePath.TrimStart('/'), method) { Authenticated = signed }, uri.Host, AuthenticationProvider?._credentials.Key, requestWeight, ClientOptions.RateLimitingBehaviour, null, cancellationToken).ConfigureAwait(false); - if (!limitResult) - return new CallResult(limitResult.Error!); - } - } - - _logger.RestApiCreatingRequest(requestId, uri); - var paramsPosition = parameterPosition ?? ParameterPositions[method]; - var request = ConstructRequest(uri, method, parameters?.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value), signed, paramsPosition, arraySerialization ?? ArraySerialization, requestBodyFormat ?? RequestBodyFormat, requestId, additionalHeaders); - - string? paramString = ""; - if (paramsPosition == HttpMethodParameterPosition.InBody) - paramString = $" with request body '{request.Content}'"; - - var headers = request.GetHeaders(); - if (headers.Length != 0) - paramString += " with headers " + string.Join(", ", headers.Select(h => h.Key + $"=[{string.Join(",", h.Value)}]")); - - TotalRequestsMade++; - _logger.RestApiSendingRequest(requestId, method, signed ? "signed": "", request.Uri, paramString); - return new CallResult(request); - } - /// /// Executes the request and returns the result deserialized into the type parameter class /// @@ -804,112 +595,6 @@ namespace CryptoExchange.Net.Clients return false; } - /// - /// Creates a request object - /// - /// The uri to send the request to - /// The method of the request - /// The parameters of the request - /// Whether or not the request should be authenticated - /// Where the parameters should be placed - /// How array parameters should be serialized - /// Format of the body content - /// Unique id of a request - /// Additional headers to send with the request - /// - protected virtual IRequest ConstructRequest( - Uri uri, - HttpMethod method, - Dictionary? parameters, - bool signed, - HttpMethodParameterPosition parameterPosition, - ArrayParametersSerialization arraySerialization, - RequestBodyFormat bodyFormat, - int requestId, - Dictionary? additionalHeaders) - { - parameters ??= new Dictionary(); - - for (var i = 0; i < parameters.Count; i++) - { - var kvp = parameters.ElementAt(i); - if (kvp.Value is Func delegateValue) - parameters[kvp.Key] = delegateValue(); - } - - if (parameterPosition == HttpMethodParameterPosition.InUri) - { - foreach (var parameter in parameters) - uri = uri.AddQueryParameter(parameter.Key, parameter.Value.ToString()!); - } - - var headers = new Dictionary(); - var uriParameters = parameterPosition == HttpMethodParameterPosition.InUri ? CreateParameterDictionary(parameters) : null; - var bodyParameters = parameterPosition == HttpMethodParameterPosition.InBody ? CreateParameterDictionary(parameters) : null; - if (AuthenticationProvider != null) - { - try - { - AuthenticationProvider.AuthenticateRequest( - this, - uri, - method, - ref uriParameters, - ref bodyParameters, - ref headers, - signed, - arraySerialization, - parameterPosition, - bodyFormat - ); - } - catch (Exception ex) - { - throw new Exception("Failed to authenticate request, make sure your API credentials are correct", ex); - } - } - - // Add the auth parameters to the uri, start with a new URI to be able to sort the parameters including the auth parameters - if (uriParameters != null) - uri = uri.SetParameters(uriParameters, arraySerialization); - - var request = RequestFactory.Create(method, uri, requestId); - request.Accept = Constants.JsonContentHeader; - - if (headers != null) - { - foreach (var header in headers) - request.AddHeader(header.Key, header.Value); - } - - if (additionalHeaders != null) - { - foreach (var header in additionalHeaders) - request.AddHeader(header.Key, header.Value); - } - - if (StandardRequestHeaders != null) - { - foreach (var header in StandardRequestHeaders) - { - // Only add it if it isn't overwritten - if (additionalHeaders?.ContainsKey(header.Key) != true) - request.AddHeader(header.Key, header.Value); - } - } - - if (parameterPosition == HttpMethodParameterPosition.InBody) - { - var contentType = bodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader; - if (bodyParameters?.Any() == true) - WriteParamBody(request, bodyParameters, contentType); - else - request.SetContent(RequestBodyEmptyContent, contentType); - } - - return request; - } - /// /// Writes the parameters of the request to the request object body /// @@ -1049,9 +734,5 @@ namespace CryptoExchange.Net.Clients => ClientOptions.CachingEnabled && definition.Method == HttpMethod.Get && !definition.PreventCaching; - - private bool ShouldCache(HttpMethod method) - => ClientOptions.CachingEnabled - && method == HttpMethod.Get; } }