1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-06-08 00:16:27 +00:00

Cleaning/docs

This commit is contained in:
JKorf 2018-11-26 16:15:10 +01:00
parent 60f3696db1
commit 009ae3a45d
5 changed files with 149 additions and 22 deletions

View File

@ -8,7 +8,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace CryptoExchange.Net
{
@ -21,13 +20,14 @@ namespace CryptoExchange.Net
protected static int lastId;
protected static object idLock = new object();
public static int LastId { get => lastId; }
private static readonly JsonSerializer defaultSerializer = JsonSerializer.Create(new JsonSerializerSettings()
{
DateTimeZoneHandling = DateTimeZoneHandling.Utc
});
public static int LastId { get => lastId; }
public BaseClient(ExchangeOptions options, AuthenticationProvider authenticationProvider)
{
log = new Log();
@ -60,12 +60,27 @@ namespace CryptoExchange.Net
authProvider = authentictationProvider;
}
/// <summary>
/// Deserialize a string into an object
/// </summary>
/// <typeparam name="T">The type to deserialize into</typeparam>
/// <param name="data">The data to deserialize</param>
/// <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
/// <param name="serializer">A specific serializer to use</param>
/// <returns></returns>
protected CallResult<T> Deserialize<T>(string data, bool checkObject = true, JsonSerializer serializer = null)
{
var obj = JToken.Parse(data);
return Deserialize<T>(obj, checkObject, serializer);
return Deserialize<T>(JToken.Parse(data), checkObject, serializer);
}
/// <summary>
/// Deserialize a JToken into an object
/// </summary>
/// <typeparam name="T">The type to deserialize into</typeparam>
/// <param name="obj">The data to deserialize</param>
/// <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
/// <param name="serializer">A specific serializer to use</param>
/// <returns></returns>
protected CallResult<T> Deserialize<T>(JToken obj, bool checkObject = true, JsonSerializer serializer = null)
{
if (serializer == null)
@ -218,6 +233,10 @@ namespace CryptoExchange.Net
|| type == typeof(decimal);
}
/// <summary>
/// Generate a unique id
/// </summary>
/// <returns></returns>
protected int NextId()
{
lock (idLock)
@ -227,18 +246,24 @@ namespace CryptoExchange.Net
}
}
protected static string FillPathParameter(string endpoint, params string[] values)
/// <summary>
/// Fill parameters in a path. Parameters are specified by '{}' and should be specified in occuring sequence
/// </summary>
/// <param name="path">The total path string</param>
/// <param name="values">The values to fill</param>
/// <returns></returns>
protected static string FillPathParameter(string path, params string[] values)
{
foreach (var value in values)
{
int index = endpoint.IndexOf("{}", StringComparison.Ordinal);
int index = path.IndexOf("{}", StringComparison.Ordinal);
if (index >= 0)
{
endpoint = endpoint.Remove(index, 2);
endpoint = endpoint.Insert(index, value);
path = path.Remove(index, 2);
path = path.Insert(index, value);
}
}
return endpoint;
return path;
}
public virtual void Dispose()

View File

@ -31,6 +31,12 @@ namespace CryptoExchange.Net
parameters.Add(key, value);
}
/// <summary>
/// Create a query string of the specified parameters
/// </summary>
/// <param name="parameters">The parameters to use</param>
/// <param name="urlEncodeValues">Whether or not the values should be url encoded</param>
/// <returns></returns>
public static string CreateParamString(this Dictionary<string, object> parameters, bool urlEncodeValues)
{
var uriString = "";
@ -45,6 +51,11 @@ namespace CryptoExchange.Net
return uriString;
}
/// <summary>
/// Get the string the secure string is representing
/// </summary>
/// <param name="source">The source secure string</param>
/// <returns></returns>
public static string GetString(this SecureString source)
{
lock (source)

View File

@ -2,7 +2,13 @@
{
public abstract class Error
{
/// <summary>
/// The error code
/// </summary>
public int Code { get; set; }
/// <summary>
/// The message for the error that occured
/// </summary>
public string Message { get; set; }
protected Error(int code, string message)

View File

@ -21,6 +21,9 @@ namespace CryptoExchange.Net
{
public abstract class RestClient: BaseClient
{
/// <summary>
/// The factory for creating requests. Used for unit testing
/// </summary>
public IRequestFactory RequestFactory { get; set; } = new RequestFactory();
protected RateLimitingBehaviour rateLimitBehaviour;
@ -30,11 +33,6 @@ namespace CryptoExchange.Net
private List<IRateLimiter> rateLimiters;
private static readonly JsonSerializer defaultSerializer = JsonSerializer.Create(new JsonSerializerSettings()
{
DateTimeZoneHandling = DateTimeZoneHandling.Utc
});
protected RestClient(ClientOptions exchangeOptions, AuthenticationProvider authenticationProvider): base(exchangeOptions, authenticationProvider)
{
Configure(exchangeOptions);
@ -103,6 +101,16 @@ namespace CryptoExchange.Net
return new CallResult<long>(0, new CantConnectError() { Message = "Ping failed: " + reply.Status });
}
/// <summary>
/// Execute a request
/// </summary>
/// <typeparam name="T">The expected result type</typeparam>
/// <param name="uri">The uri to send the request to</param>
/// <param name="method">The method of the request</param>
/// <param name="parameters">The parameters of the request</param>
/// <param name="signed">Whether or not the request should be authenticated</param>
/// <param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param>
/// <returns></returns>
protected virtual async Task<CallResult<T>> ExecuteRequest<T>(Uri uri, string method = Constants.GetMethod, Dictionary<string, object> parameters = null, bool signed = false, bool checkResult = true) where T : class
{
log.Write(LogVerbosity.Debug, $"Creating request for " + uri);
@ -149,6 +157,14 @@ namespace CryptoExchange.Net
return result.Error != null ? new CallResult<T>(null, result.Error) : Deserialize<T>(result.Data, checkResult);
}
/// <summary>
/// Creates a request object
/// </summary>
/// <param name="uri">The uri to send the request to</param>
/// <param name="method">The method of the request</param>
/// <param name="parameters">The parameters of the request</param>
/// <param name="signed">Whether or not the request should be authenticated</param>
/// <returns></returns>
protected virtual IRequest ConstructRequest(Uri uri, string method, Dictionary<string, object> parameters, bool signed)
{
if (parameters == null)
@ -184,6 +200,11 @@ namespace CryptoExchange.Net
return request;
}
/// <summary>
/// Writes the string data of the paramters to the request body stream
/// </summary>
/// <param name="request"></param>
/// <param name="stringData"></param>
protected virtual void WriteParamBody(IRequest request, string stringData)
{
var data = Encoding.UTF8.GetBytes(stringData);
@ -193,6 +214,11 @@ namespace CryptoExchange.Net
stream.Write(data, 0, data.Length);
}
/// <summary>
/// Writes the parameters of the request to the request object, either in the query string or the request body
/// </summary>
/// <param name="request"></param>
/// <param name="parameters"></param>
protected virtual void WriteParamBody(IRequest request, Dictionary<string, object> parameters)
{
if (requestBodyFormat == RequestBodyFormat.Json)
@ -210,6 +236,11 @@ namespace CryptoExchange.Net
}
}
/// <summary>
/// Executes the request and returns the string result
/// </summary>
/// <param name="request">The request object to execute</param>
/// <returns></returns>
private async Task<CallResult<string>> ExecuteRequest(IRequest request)
{
var returnedData = "";
@ -258,6 +289,11 @@ namespace CryptoExchange.Net
}
}
/// <summary>
/// Parse an error response from the server. Only used when server returns a status other than Success(200)
/// </summary>
/// <param name="error">The string the request returned</param>
/// <returns></returns>
protected virtual Error ParseErrorResponse(string error)
{
return new ServerError(error);

View File

@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Authentication;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CryptoExchange.Net.Authentication;
@ -18,6 +17,9 @@ namespace CryptoExchange.Net
public abstract class SocketClient: BaseClient
{
#region fields
/// <summary>
/// The factory for creating sockets. Used for unit testing
/// </summary>
public IWebsocketFactory SocketFactory { get; set; } = new WebsocketFactory();
private const SslProtocols protocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;
@ -51,6 +53,11 @@ namespace CryptoExchange.Net
dataInterpreter = handler;
}
/// <summary>
/// Create a socket for an address
/// </summary>
/// <param name="address">The address the socket should connect to</param>
/// <returns></returns>
protected virtual IWebsocket CreateSocket(string address)
{
var socket = SocketFactory.CreateWebsocket(log, address);
@ -80,8 +87,20 @@ namespace CryptoExchange.Net
protected virtual void SocketOpened(IWebsocket socket) { }
protected virtual void SocketClosed(IWebsocket socket) { }
protected virtual void SocketError(IWebsocket socket, Exception ex) { }
protected abstract bool SocketReconnect(SocketSubscription socket, TimeSpan disconnectedTime);
/// <summary>
/// Handler for when a socket reconnects. Should return true if reconnection handling was successful or false if not ( will try to reconnect again ). The handler should
/// handle functionality like resubscribing and re-authenticating the socket.
/// </summary>
/// <param name="subscription">The socket subscription that was reconnected</param>
/// <param name="disconnectedTime">The time the socket was disconnected</param>
/// <returns></returns>
protected abstract bool SocketReconnect(SocketSubscription subscription, TimeSpan disconnectedTime);
/// <summary>
/// Connect a socket
/// </summary>
/// <param name="socketSubscription">The subscription to connect</param>
/// <returns></returns>
protected virtual async Task<CallResult<bool>> ConnectSocket(SocketSubscription socketSubscription)
{
socketSubscription.Socket.OnMessage += data => ProcessMessage(socketSubscription, data);
@ -97,13 +116,22 @@ namespace CryptoExchange.Net
return new CallResult<bool>(false, new CantConnectError());
}
protected virtual void ProcessMessage(SocketSubscription sub, string data)
/// <summary>
/// The message handler. Normally distributes the received data to all data handlers
/// </summary>
/// <param name="subscription">The subscription that received the data</param>
/// <param name="data">The data received</param>
protected virtual void ProcessMessage(SocketSubscription subscription, string data)
{
log.Write(LogVerbosity.Debug, $"Socket {sub.Socket.Id} received data: " + data);
foreach (var handler in sub.DataHandlers)
handler(sub, JToken.Parse(data));
log.Write(LogVerbosity.Debug, $"Socket {subscription.Socket.Id} received data: " + data);
foreach (var handler in subscription.DataHandlers)
handler(subscription, JToken.Parse(data));
}
/// <summary>
/// Handler for a socket closing. Reconnects the socket if needed, or removes it from the active socket list if not
/// </summary>
/// <param name="socket">The socket that was closed</param>
protected virtual void SocketOnClose(IWebsocket socket)
{
if (socket.ShouldReconnect)
@ -148,22 +176,43 @@ namespace CryptoExchange.Net
}
}
/// <summary>
/// Send data to the websocket
/// </summary>
/// <typeparam name="T">The type of the object to send</typeparam>
/// <param name="socket">The socket to send to</param>
/// <param name="obj">The object to send</param>
/// <param name="nullValueHandling">How null values should be serialized</param>
protected virtual void Send<T>(IWebsocket socket, T obj, NullValueHandling nullValueHandling = NullValueHandling.Ignore)
{
Send(socket, JsonConvert.SerializeObject(obj, Formatting.None, new JsonSerializerSettings { NullValueHandling = nullValueHandling }));
}
/// <summary>
/// Send string data to the websocket
/// </summary>
/// <param name="socket">The socket to send to</param>
/// <param name="data">The data to send</param>
protected virtual void Send(IWebsocket socket, string data)
{
log.Write(LogVerbosity.Debug, $"Socket {socket.Id} sending data: {data}");
socket.Send(data);
}
public virtual async Task Unsubscribe(UpdateSubscription sub)
/// <summary>
/// Unsubscribe from a stream
/// </summary>
/// <param name="subscription">The subscription to unsubscribe</param>
/// <returns></returns>
public virtual async Task Unsubscribe(UpdateSubscription subscription)
{
await sub.Close();
await subscription.Close();
}
/// <summary>
/// Unsubscribe all subscriptions
/// </summary>
/// <returns></returns>
public virtual async Task UnsubscribeAll()
{
await Task.Run(() =>