diff --git a/CryptoExchange.Net/BaseClient.cs b/CryptoExchange.Net/BaseClient.cs
index 0fc9953..f725c61 100644
--- a/CryptoExchange.Net/BaseClient.cs
+++ b/CryptoExchange.Net/BaseClient.cs
@@ -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;
}
+ ///
+ /// Deserialize a string into an object
+ ///
+ /// The type to deserialize into
+ /// The data to deserialize
+ /// Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)
+ /// A specific serializer to use
+ ///
protected CallResult Deserialize(string data, bool checkObject = true, JsonSerializer serializer = null)
{
- var obj = JToken.Parse(data);
- return Deserialize(obj, checkObject, serializer);
+ return Deserialize(JToken.Parse(data), checkObject, serializer);
}
+ ///
+ /// Deserialize a JToken into an object
+ ///
+ /// The type to deserialize into
+ /// The data to deserialize
+ /// Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)
+ /// A specific serializer to use
+ ///
protected CallResult Deserialize(JToken obj, bool checkObject = true, JsonSerializer serializer = null)
{
if (serializer == null)
@@ -218,6 +233,10 @@ namespace CryptoExchange.Net
|| type == typeof(decimal);
}
+ ///
+ /// Generate a unique id
+ ///
+ ///
protected int NextId()
{
lock (idLock)
@@ -227,18 +246,24 @@ namespace CryptoExchange.Net
}
}
- protected static string FillPathParameter(string endpoint, params string[] values)
+ ///
+ /// Fill parameters in a path. Parameters are specified by '{}' and should be specified in occuring sequence
+ ///
+ /// The total path string
+ /// The values to fill
+ ///
+ 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()
diff --git a/CryptoExchange.Net/ExtensionMethods.cs b/CryptoExchange.Net/ExtensionMethods.cs
index b66bd70..3b407f3 100644
--- a/CryptoExchange.Net/ExtensionMethods.cs
+++ b/CryptoExchange.Net/ExtensionMethods.cs
@@ -31,6 +31,12 @@ namespace CryptoExchange.Net
parameters.Add(key, value);
}
+ ///
+ /// Create a query string of the specified parameters
+ ///
+ /// The parameters to use
+ /// Whether or not the values should be url encoded
+ ///
public static string CreateParamString(this Dictionary parameters, bool urlEncodeValues)
{
var uriString = "";
@@ -45,6 +51,11 @@ namespace CryptoExchange.Net
return uriString;
}
+ ///
+ /// Get the string the secure string is representing
+ ///
+ /// The source secure string
+ ///
public static string GetString(this SecureString source)
{
lock (source)
diff --git a/CryptoExchange.Net/Objects/Error.cs b/CryptoExchange.Net/Objects/Error.cs
index 7a59bb1..f692762 100644
--- a/CryptoExchange.Net/Objects/Error.cs
+++ b/CryptoExchange.Net/Objects/Error.cs
@@ -2,7 +2,13 @@
{
public abstract class Error
{
+ ///
+ /// The error code
+ ///
public int Code { get; set; }
+ ///
+ /// The message for the error that occured
+ ///
public string Message { get; set; }
protected Error(int code, string message)
diff --git a/CryptoExchange.Net/RestClient.cs b/CryptoExchange.Net/RestClient.cs
index 55565aa..3a8a46d 100644
--- a/CryptoExchange.Net/RestClient.cs
+++ b/CryptoExchange.Net/RestClient.cs
@@ -21,6 +21,9 @@ namespace CryptoExchange.Net
{
public abstract class RestClient: BaseClient
{
+ ///
+ /// The factory for creating requests. Used for unit testing
+ ///
public IRequestFactory RequestFactory { get; set; } = new RequestFactory();
protected RateLimitingBehaviour rateLimitBehaviour;
@@ -30,11 +33,6 @@ namespace CryptoExchange.Net
private List 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(0, new CantConnectError() { Message = "Ping failed: " + reply.Status });
}
+ ///
+ /// Execute a request
+ ///
+ /// The expected result type
+ /// 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
+ /// Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)
+ ///
protected virtual async Task> ExecuteRequest(Uri uri, string method = Constants.GetMethod, Dictionary 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(null, result.Error) : Deserialize(result.Data, checkResult);
}
+ ///
+ /// 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
+ ///
protected virtual IRequest ConstructRequest(Uri uri, string method, Dictionary parameters, bool signed)
{
if (parameters == null)
@@ -184,6 +200,11 @@ namespace CryptoExchange.Net
return request;
}
+ ///
+ /// Writes the string data of the paramters to the request body stream
+ ///
+ ///
+ ///
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);
}
+ ///
+ /// Writes the parameters of the request to the request object, either in the query string or the request body
+ ///
+ ///
+ ///
protected virtual void WriteParamBody(IRequest request, Dictionary parameters)
{
if (requestBodyFormat == RequestBodyFormat.Json)
@@ -210,6 +236,11 @@ namespace CryptoExchange.Net
}
}
+ ///
+ /// Executes the request and returns the string result
+ ///
+ /// The request object to execute
+ ///
private async Task> ExecuteRequest(IRequest request)
{
var returnedData = "";
@@ -258,6 +289,11 @@ namespace CryptoExchange.Net
}
}
+ ///
+ /// Parse an error response from the server. Only used when server returns a status other than Success(200)
+ ///
+ /// The string the request returned
+ ///
protected virtual Error ParseErrorResponse(string error)
{
return new ServerError(error);
diff --git a/CryptoExchange.Net/SocketClient.cs b/CryptoExchange.Net/SocketClient.cs
index 487bb14..fe9ffb2 100644
--- a/CryptoExchange.Net/SocketClient.cs
+++ b/CryptoExchange.Net/SocketClient.cs
@@ -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
+ ///
+ /// The factory for creating sockets. Used for unit testing
+ ///
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;
}
+ ///
+ /// Create a socket for an address
+ ///
+ /// The address the socket should connect to
+ ///
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);
+ ///
+ /// 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.
+ ///
+ /// The socket subscription that was reconnected
+ /// The time the socket was disconnected
+ ///
+ protected abstract bool SocketReconnect(SocketSubscription subscription, TimeSpan disconnectedTime);
+ ///
+ /// Connect a socket
+ ///
+ /// The subscription to connect
+ ///
protected virtual async Task> ConnectSocket(SocketSubscription socketSubscription)
{
socketSubscription.Socket.OnMessage += data => ProcessMessage(socketSubscription, data);
@@ -97,13 +116,22 @@ namespace CryptoExchange.Net
return new CallResult(false, new CantConnectError());
}
- protected virtual void ProcessMessage(SocketSubscription sub, string data)
+ ///
+ /// The message handler. Normally distributes the received data to all data handlers
+ ///
+ /// The subscription that received the data
+ /// The data received
+ 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));
}
+ ///
+ /// Handler for a socket closing. Reconnects the socket if needed, or removes it from the active socket list if not
+ ///
+ /// The socket that was closed
protected virtual void SocketOnClose(IWebsocket socket)
{
if (socket.ShouldReconnect)
@@ -148,22 +176,43 @@ namespace CryptoExchange.Net
}
}
+ ///
+ /// Send data to the websocket
+ ///
+ /// The type of the object to send
+ /// The socket to send to
+ /// The object to send
+ /// How null values should be serialized
protected virtual void Send(IWebsocket socket, T obj, NullValueHandling nullValueHandling = NullValueHandling.Ignore)
{
Send(socket, JsonConvert.SerializeObject(obj, Formatting.None, new JsonSerializerSettings { NullValueHandling = nullValueHandling }));
}
+ ///
+ /// Send string data to the websocket
+ ///
+ /// The socket to send to
+ /// The data to send
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)
+ ///
+ /// Unsubscribe from a stream
+ ///
+ /// The subscription to unsubscribe
+ ///
+ public virtual async Task Unsubscribe(UpdateSubscription subscription)
{
- await sub.Close();
+ await subscription.Close();
}
+ ///
+ /// Unsubscribe all subscriptions
+ ///
+ ///
public virtual async Task UnsubscribeAll()
{
await Task.Run(() =>