Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/update settings #225

Merged
merged 3 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CryptoExchange.Net/Clients/BaseApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,17 @@ public void SetApiCredentials<T>(T credentials) where T : ApiCredentials
AuthenticationProvider = CreateAuthenticationProvider(credentials.Copy());
}

/// <inheritdoc />
public virtual void SetOptions<T>(UpdateOptions<T> options) where T : ApiCredentials
{
ClientOptions.Proxy = options.Proxy;
ClientOptions.RequestTimeout = options.RequestTimeout ?? ClientOptions.RequestTimeout;

ApiOptions.ApiCredentials = options.ApiCredentials ?? ClientOptions.ApiCredentials;
if (options.ApiCredentials != null)
AuthenticationProvider = CreateAuthenticationProvider(options.ApiCredentials.Copy());
}

/// <summary>
/// Dispose
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions CryptoExchange.Net/Clients/RestApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,14 @@ protected internal IDictionary<string, object> CreateParameterDictionary(IDictio
/// <returns>Server time</returns>
protected virtual Task<WebCallResult<DateTime>> GetServerTimestampAsync() => throw new NotImplementedException();

/// <inheritdoc />
public override void SetOptions<T>(UpdateOptions<T> options)
{
base.SetOptions(options);

RequestFactory.UpdateSettings(options.Proxy, options.RequestTimeout ?? ClientOptions.RequestTimeout);
}

internal async Task<WebCallResult<bool>> SyncTimeAsync()
{
var timeSyncParams = GetTimeSyncInfo();
Expand Down
24 changes: 22 additions & 2 deletions CryptoExchange.Net/Clients/SocketApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ protected virtual void SetDedicatedConnection(string url, bool auth)
/// <param name="interval"></param>
/// <param name="queryDelegate"></param>
/// <param name="callback"></param>
protected virtual void RegisterPeriodicQuery(string identifier, TimeSpan interval, Func<SocketConnection, Query> queryDelegate, Action<CallResult>? callback)
protected virtual void RegisterPeriodicQuery(string identifier, TimeSpan interval, Func<SocketConnection, Query> queryDelegate, Action<SocketConnection, CallResult>? callback)
{
PeriodicTaskRegistrations.Add(new PeriodicTaskRegistration
{
Expand Down Expand Up @@ -422,9 +422,10 @@ public virtual async Task<CallResult> AuthenticateSocketAsync(SocketConnection s
result.Error!.Message = "Authentication failed: " + result.Error.Message;
return new CallResult(result.Error)!;
}

_logger.Authenticated(socket.SocketId);
}

_logger.Authenticated(socket.SocketId);
socket.Authenticated = true;
return new CallResult(null);
}
Expand Down Expand Up @@ -710,6 +711,25 @@ public virtual async Task<CallResult> PrepareConnectionsAsync()
return new CallResult(null);
}

/// <inheritdoc />
public override void SetOptions<T>(UpdateOptions<T> options)
{
var previousProxyIsSet = ClientOptions.Proxy != null;
base.SetOptions(options);

if ((!previousProxyIsSet && options.Proxy == null)
|| !socketConnections.Any())
{
return;
}

_logger.LogInformation("Reconnecting websockets to apply proxy");

// Update proxy, also triggers reconnect
foreach (var connection in socketConnections)
_ = connection.Value.UpdateProxy(options.Proxy);
}

/// <summary>
/// Log the current state of connections and subscriptions
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions CryptoExchange.Net/Interfaces/IBaseApiClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.SharedApis;
using System;

Expand Down Expand Up @@ -31,5 +32,12 @@ public interface IBaseApiClient
/// <typeparam name="T"></typeparam>
/// <param name="credentials"></param>
void SetApiCredentials<T>(T credentials) where T : ApiCredentials;

/// <summary>
/// Set new options. Note that when using a proxy this should be provided in the options even when already set before or it will be reset.
/// </summary>
/// <typeparam name="T">Api crentials type</typeparam>
/// <param name="options">Options to set</param>
void SetOptions<T>(UpdateOptions<T> options) where T : ApiCredentials;
}
}
9 changes: 8 additions & 1 deletion CryptoExchange.Net/Interfaces/IRequestFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ public interface IRequestFactory
/// <param name="requestTimeout">Request timeout to use</param>
/// <param name="httpClient">Optional shared http client instance</param>
/// <param name="proxy">Optional proxy to use when no http client is provided</param>
void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? httpClient=null);
void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? httpClient = null);

/// <summary>
/// Update settings
/// </summary>
/// <param name="proxy">Proxy to use</param>
/// <param name="requestTimeout">Request timeout to use</param>
void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout);
}
}
5 changes: 5 additions & 0 deletions CryptoExchange.Net/Interfaces/IWebsocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,10 @@ public interface IWebsocket: IDisposable
/// </summary>
/// <returns></returns>
Task CloseAsync();

/// <summary>
/// Update proxy setting
/// </summary>
void UpdateProxy(ApiProxy? proxy);
}
}
29 changes: 29 additions & 0 deletions CryptoExchange.Net/Objects/Options/UpdateOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using CryptoExchange.Net.Authentication;
using System;
using System.Collections.Generic;
using System.Text;

namespace CryptoExchange.Net.Objects.Options
{
/// <summary>
/// Options to update
/// </summary>
public class UpdateOptions<T> where T : ApiCredentials
{
/// <summary>
/// Proxy setting. Note that if this is not provided any previously set proxy will be reset
/// </summary>
public ApiProxy? Proxy { get; set; }
/// <summary>
/// Api credentials
/// </summary>
public T? ApiCredentials { get; set; }
/// <summary>
/// Request timeout
/// </summary>
public TimeSpan? RequestTimeout { get; set; }
}

/// <inheritdoc />
public class UpdateOptions : UpdateOptions<ApiCredentials> { }
}
55 changes: 33 additions & 22 deletions CryptoExchange.Net/Requests/RequestFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,7 @@ public class RequestFactory : IRequestFactory
public void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? client = null)
{
if (client == null)
{
var handler = new HttpClientHandler();
try
{
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
}
catch (PlatformNotSupportedException) { }

if (proxy != null)
{
handler.Proxy = new WebProxy
{
Address = new Uri($"{proxy.Host}:{proxy.Port}"),
Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
};
}

client = new HttpClient(handler)
{
Timeout = requestTimeout
};
}
client = CreateClient(proxy, requestTimeout);

_httpClient = client;
}
Expand All @@ -51,5 +30,37 @@ public IRequest Create(HttpMethod method, Uri uri, int requestId)

return new Request(new HttpRequestMessage(method, uri), _httpClient, requestId);
}

/// <inheritdoc />
public void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout)
{
_httpClient = CreateClient(proxy, requestTimeout);
}

private HttpClient CreateClient(ApiProxy? proxy, TimeSpan requestTimeout)
{
var handler = new HttpClientHandler();
try
{
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
handler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
}
catch (PlatformNotSupportedException) { }

if (proxy != null)
{
handler.Proxy = new WebProxy
{
Address = new Uri($"{proxy.Host}:{proxy.Port}"),
Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
};
}

var client = new HttpClient(handler)
{
Timeout = requestTimeout
};
return client;
}
}
}
10 changes: 8 additions & 2 deletions CryptoExchange.Net/Sockets/CryptoExchangeWebSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ public CryptoExchangeWebSocketClient(ILogger logger, WebSocketParameters websock
_baseAddress = $"{Uri.Scheme}://{Uri.Host}";
}

/// <inheritdoc />
public void UpdateProxy(ApiProxy? proxy)
{
Parameters.Proxy = proxy;
}

/// <inheritdoc />
public virtual async Task<CallResult> ConnectAsync()
{
Expand Down Expand Up @@ -435,8 +441,8 @@ private async Task CloseInternalAsync()
{
// Wait until we receive close confirmation
await Task.Delay(10).ConfigureAwait(false);
if (DateTime.UtcNow - startWait > TimeSpan.FromSeconds(5))
break; // Wait for max 5 seconds, then just abort the connection
if (DateTime.UtcNow - startWait > TimeSpan.FromSeconds(1))
break; // Wait for max 1 second, then just abort the connection
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion CryptoExchange.Net/Sockets/PeriodicTaskRegistration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ public class PeriodicTaskRegistration
/// <summary>
/// Callback after query
/// </summary>
public Action<CallResult>? Callback { get; set; }
public Action<SocketConnection, CallResult>? Callback { get; set; }
}
}
5 changes: 5 additions & 0 deletions CryptoExchange.Net/Sockets/Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ public abstract class Query : IMessageProcessor
/// </summary>
public bool Completed { get; set; }

/// <summary>
/// Timeout for the request
/// </summary>
public TimeSpan? RequestTimeout { get; set; }

/// <summary>
/// The number of required responses. Can be more than 1 when for example subscribing multiple symbols streams in a single request,
/// and each symbol receives it's own confirmation response
Expand Down
18 changes: 15 additions & 3 deletions CryptoExchange.Net/Sockets/SocketConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
using CryptoExchange.Net.Clients;
using CryptoExchange.Net.Logging.Extensions;
using System.Threading;
using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.Authentication;

namespace CryptoExchange.Net.Sockets
{
Expand Down Expand Up @@ -437,7 +439,7 @@ protected virtual Task HandleRequestSentAsync(int requestId)
return Task.CompletedTask;
}

query.IsSend(ApiClient.ClientOptions.RequestTimeout);
query.IsSend(query.RequestTimeout ?? ApiClient.ClientOptions.RequestTimeout);
return Task.CompletedTask;
}

Expand Down Expand Up @@ -583,6 +585,16 @@ protected virtual async Task HandleStreamMessage(WebSocketMessageType type, Read
/// <returns></returns>
public async Task TriggerReconnectAsync() => await _socket.ReconnectAsync().ConfigureAwait(false);

/// <summary>
/// Update the proxy setting and reconnect
/// </summary>
/// <param name="proxy">New proxy setting</param>
public async Task UpdateProxy(ApiProxy? proxy)
{
_socket.UpdateProxy(proxy);
await TriggerReconnectAsync().ConfigureAwait(false);
}

/// <summary>
/// Close the connection
/// </summary>
Expand Down Expand Up @@ -988,7 +1000,7 @@ internal async Task<CallResult> ResubscribeAsync(Subscription subscription)
/// <param name="interval">How often</param>
/// <param name="queryDelegate">Method returning the query to send</param>
/// <param name="callback">The callback for processing the response</param>
public virtual void QueryPeriodic(string identifier, TimeSpan interval, Func<SocketConnection, Query> queryDelegate, Action<CallResult>? callback)
public virtual void QueryPeriodic(string identifier, TimeSpan interval, Func<SocketConnection, Query> queryDelegate, Action<SocketConnection, CallResult>? callback)
{
if (queryDelegate == null)
throw new ArgumentNullException(nameof(queryDelegate));
Expand Down Expand Up @@ -1020,7 +1032,7 @@ public virtual void QueryPeriodic(string identifier, TimeSpan interval, Func<Soc
try
{
var result = await SendAndWaitQueryAsync(query).ConfigureAwait(false);
callback?.Invoke(result);
callback?.Invoke(this, result);
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@ public IRequest Create(HttpMethod method, Uri uri, int requestId)
_request.RequestId = requestId;
return _request;
}

public void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout) {}
}
}
2 changes: 2 additions & 0 deletions CryptoExchange.Net/Testing/Implementations/TestSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,7 @@ public void InvokeMessage<T>(T data)

public Task ReconnectAsync() => throw new NotImplementedException();
public void Dispose() { }

public void UpdateProxy(ApiProxy? proxy) => throw new NotImplementedException();
}
}
3 changes: 2 additions & 1 deletion CryptoExchange.Net/Trackers/Trades/TradeTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,8 @@ protected void SetInitialData(IEnumerable<SharedTrade> data)
_data.Add(item);
}

_firstTimestamp = _data.Min(v => v.Timestamp);
if (_data.Any())
_firstTimestamp = _data.Min(v => v.Timestamp);

ApplyWindow(false);
}
Expand Down
Loading