/// <inheritdoc /> public async Task <CallResult <UpdateSubscription> > SubscribeToOrderUpdatesAsync(IEnumerable <string> symbols, Action <DataEvent <CoinExSocketOrderUpdate> > onMessage, CancellationToken ct = default) { var internalHandler = new Action <DataEvent <JToken[]> >(data => { if (data.Data.Length != 2) { _log.Write(LogLevel.Warning, $"Received unexpected data format for order update. Expected 2 objects, received {data.Data.Length}. Data: [{string.Join(",", data.Data.Select(s => s.ToString()))}]"); return; } var updateResult = JsonConvert.DeserializeObject <UpdateType>(data.Data[0].ToString(), new UpdateTypeConverter(false)); var desResult = _baseClient.DeserializeInternal <CoinExSocketOrder>(data.Data[1]); if (!desResult) { _log.Write(LogLevel.Warning, "Received invalid order update: " + desResult.Error); return; } var result = new CoinExSocketOrderUpdate() { UpdateType = updateResult, Order = desResult.Data }; onMessage(data.As(result, result.Order.Symbol)); }); var request = new CoinExSocketRequest(_baseClient.NextIdInternal(), OrderSubject, SubscribeAction, symbols.ToArray()); return(await _baseClient.SubscribeInternalAsync(this, request, null, true, internalHandler, ct).ConfigureAwait(false)); }
private async Task <CallResult <T> > QueryNewSocket <T>(CoinExSocketRequest request, bool authenticated) { var con = ConnectNewSocket(); if (!con.Success) { return(new CallResult <T>(default(T), con.Error)); } if (authenticated) { var auth = await Authenticate(con.Data).ConfigureAwait(false); if (!auth.Success) { return(new CallResult <T>(default(T), auth.Error)); } } var result = await Query <T>(con.Data, request).ConfigureAwait(false); var closeTask = con.Data.Close().ConfigureAwait(false); // let it close in background return(result); }
public void SubscribingToAuthenticatedStream_Should_SendAuthentication() { // arrange var client = TestHelpers.PrepareSocketClient(() => Construct(new CoinExSocketClientOptions() { ApiCredentials = new ApiCredentials("TestKey", "test"), SubscriptionResponseTimeout = TimeSpan.FromMilliseconds(100) })); var expected = new CoinExSocketRequest("server", "sign", "TestKey", "", 1); CoinExSocketRequest actual = null; // act var sendWait = TestHelpers.WaitForSend(client); var sub = client.SubscribeToBalanceUpdates(data => { }); var result = sendWait.Result; var invocations = Mock.Get(client.sockets[0].Socket).Invocations.Where(s => s.Method == typeof(IWebsocket).GetMethod("Send")); foreach (var invocation in invocations) { var msg = (string)invocation.Arguments[0]; if (msg.Contains("sign")) { actual = JsonConvert.DeserializeObject <CoinExSocketRequest>(msg); } } // assert Assert.IsTrue(result); Assert.IsTrue(actual != null); Assert.IsTrue(expected.Method == actual.Method); Assert.IsTrue((string)expected.Parameters[0] == (string)actual.Parameters[0]);; }
/// <inheritdoc /> protected override async Task <CallResult <bool> > AuthenticateSocketAsync(SocketConnection s) { if (s.ApiClient.AuthenticationProvider == null) { return(new CallResult <bool>(new NoApiCredentialsError())); } var request = new CoinExSocketRequest(NextId(), ServerSubject, AuthenticateAction, GetAuthParameters(s.ApiClient)); var result = new CallResult <bool>(new ServerError("No response from server")); await s.SendAndWaitAsync(request, ClientOptions.SocketResponseTimeout, data => { var idField = data["id"]; if (idField == null) { return(false); } if ((int)idField != request.Id) { return(false); // Not for this request } var authResponse = Deserialize <CoinExSocketRequestResponse <CoinExSocketRequestResponseMessage> >(data); if (!authResponse) { log.Write(LogLevel.Warning, "Authorization failed: " + authResponse.Error); result = new CallResult <bool>(authResponse.Error !); return(true); }
private async Task <CallResult <bool> > Authenticate(SocketSubscription subscription) { if (authProvider == null) { return(new CallResult <bool>(false, new NoApiCredentialsError())); } var request = new CoinExSocketRequest(ServerSubject, AuthenticateAction, true, GetAuthParameters()) { Id = NextId() }; var waitTask = subscription.WaitForEvent(AuthenticationEvent, request.Id.ToString(), subResponseTimeout); Send(subscription.Socket, request); var authResult = await waitTask.ConfigureAwait(false); if (!authResult.Success) { var closeTask = subscription.Close(); return(new CallResult <bool>(false, authResult.Error)); } return(new CallResult <bool>(true, null)); }
private async Task <CallResult <UpdateSubscription> > Subscribe(CoinExSocketRequest request, Action <JToken[]> onData) { var connectResult = await CreateAndConnectSocket(request.Signed, true, onData).ConfigureAwait(false); if (!connectResult.Success) { return(new CallResult <UpdateSubscription>(null, connectResult.Error)); } return(await Subscribe(connectResult.Data, request).ConfigureAwait(false)); }
protected override async Task <CallResult <bool> > AuthenticateSocket(SocketConnection s) { if (authProvider == null) { return(new CallResult <bool>(false, new NoApiCredentialsError())); } var request = new CoinExSocketRequest(NextId(), ServerSubject, AuthenticateAction, GetAuthParameters()); CallResult <bool> result = new CallResult <bool>(false, new ServerError("No response from server")); await s.SendAndWait(request, ResponseTimeout, data => { var idField = data["id"]; if (idField == null) { return(false); } if ((int)idField != request.Id) { return(false); // Not for this request } var authResponse = Deserialize <CoinExSocketRequestResponse <CoinExSocketRequestResponseMessage> >(data, false); if (!authResponse.Success) { log.Write(LogVerbosity.Warning, "Authorization failed: " + authResponse.Error); result = new CallResult <bool>(false, authResponse.Error); return(true); } if (authResponse.Data.Error != null) { var error = new ServerError(authResponse.Data.Error.Code, authResponse.Data.Error.Message); log.Write(LogVerbosity.Debug, "Failed to authenticate: " + error); result = new CallResult <bool>(false, error); return(true); } if (authResponse.Data.Result.Status != SuccessString) { log.Write(LogVerbosity.Debug, "Failed to authenticate: " + authResponse.Data.Result.Status); result = new CallResult <bool>(false, new ServerError(authResponse.Data.Result.Status)); return(true); } log.Write(LogVerbosity.Debug, "Authorization completed"); result = new CallResult <bool>(true, null); return(true); }); return(result); }
private async Task <CallResult <T> > Query <T>(CoinExSocketRequest request) { CallResult <T> result = null; var internalHandler = new Action <JToken[]>(data => result = Deserialize <T>(data[0])); var subscription = GetBackgroundSocket(request.Signed); if (subscription == null) { // We don't have a background socket to query, create a new one var connectResult = await CreateAndConnectSocket(request.Signed, false, internalHandler).ConfigureAwait(false); if (!connectResult.Success) { return(new CallResult <T>(default(T), connectResult.Error)); } subscription = connectResult.Data; subscription.Type = request.Signed ? SocketType.BackgroundAuthenticated : SocketType.Background; } else { // Use earlier created background socket to query without having to connect again subscription.Events.Single(s => s.Name == DataEvent).Reset(); subscription.MessageHandlers[DataHandlerName] = (subs, data) => DataHandlerQuery(subs, data, internalHandler); } request.Id = NextId(); var waitTask = subscription.WaitForEvent(DataEvent, request.Id.ToString(), subResponseTimeout); Send(subscription.Socket, request); var dataResult = await waitTask.ConfigureAwait(false); return(!dataResult.Success ? new CallResult <T>(default(T), dataResult.Error) : result); }
private async Task <CallResult <CoinExStreamSubscription> > Subscribe(CoinExStream stream, CoinExSocketRequest request, bool resubscribing) { if (stream.Authenticated) { var auth = await Authenticate(stream).ConfigureAwait(false); if (!auth.Success) { return(new CallResult <CoinExStreamSubscription>(null, auth.Error)); } } if (!resubscribing) // Only add the message handler once, is already done if resubscribing { stream.Socket.OnMessage += (msg) => OnMessage(stream.StreamResult.StreamId, msg); } var subConfirm = await Query <CoinExSocketRequestResponseMessage>(stream, request).ConfigureAwait(false); if (subConfirm.Success) { stream.Request = request; stream.Subscription.StreamId = stream.StreamResult.StreamId; stream.TryReconnect = true; lock (subscriptions) if (!subscriptions.Contains(stream.Subscription)) { subscriptions.Add(stream.Subscription); } log.Write(LogVerbosity.Info, $"Subscription {stream.Subscription.StreamId} successful"); } else { log.Write(LogVerbosity.Info, $"Failed to subscribe {stream.Subscription.StreamId}: {subConfirm.Error}"); if (!resubscribing) // If we're just trying to initialy subscribe we dont need to reconnect if we failed subbing, so close it here { await stream.Close().ConfigureAwait(false); } } return(new CallResult <CoinExStreamSubscription>(stream.StreamResult, subConfirm.Error)); }
private async Task <CallResult <CoinExStreamSubscription> > Subscribe(CoinExSubscription subscription, CoinExSocketRequest request, bool authenticated = false) { log.Write(LogVerbosity.Debug, $"Starting new subscription for {request.Method}"); var con = ConnectNewSocket(); if (!con.Success) { return(new CallResult <CoinExStreamSubscription>(null, con.Error)); } con.Data.Authenticated = authenticated; con.Data.Subscription = subscription; return(await Subscribe(con.Data, request, false).ConfigureAwait(false)); }
private async Task <CallResult <T> > Query <T>(CoinExStream stream, CoinExSocketRequest request) { return(await Task.Run(() => { log.Write(LogVerbosity.Debug, $"Querying socket {stream.Subscription.StreamId} for {request.Method}"); ManualResetEvent evnt = new ManualResetEvent(false); CallResult <CoinExSocketRequestResponse <T> > result = null; request.Id = NextRequestId(); var onMessageAction = new Action <string>((msg) => { log.Write(LogVerbosity.Debug, "Socket received query response: " + msg); var token = JToken.Parse(msg); if ((int?)token["id"] != request.Id) { return; } if (token["error"].Type == JTokenType.Null) { result = Deserialize <CoinExSocketRequestResponse <T> >(msg); } else { var errorResult = Deserialize <CoinExSocketError>(token["error"].ToString()); if (!errorResult.Success) { result = new CallResult <CoinExSocketRequestResponse <T> >(null, new ServerError("Unknown error: " + token["error"])); } else { result = new CallResult <CoinExSocketRequestResponse <T> >(null, new ServerError(errorResult.Data.Code, errorResult.Data.Message)); } } evnt?.Set(); }); stream.Socket.OnMessage += onMessageAction; var data = JsonConvert.SerializeObject(request); log.Write(LogVerbosity.Debug, "Sending data: " + data); stream.Socket.Send(data); evnt.WaitOne(subResponseTimeout); stream.Socket.OnMessage -= onMessageAction; evnt.Dispose(); evnt = null; if (result == null) { return new CallResult <T>(default(T), new ServerError("No response from server")); } if (!result.Success) { return new CallResult <T>(default(T), result.Error); } if (result.Data.Error != null) { return new CallResult <T>(default(T), new ServerError(result.Data.Error.Code, result.Data.Error.Message)); } return new CallResult <T>(result.Data.Result, null); }).ConfigureAwait(false)); }
private async Task <CallResult <UpdateSubscription> > Subscribe(SocketSubscription subscription, CoinExSocketRequest request) { request.Id = NextId(); var waitTask = subscription.WaitForEvent(SubscriptionEvent, request.Id.ToString(), subResponseTimeout); Send(subscription.Socket, request); var subResult = await waitTask.ConfigureAwait(false); if (!subResult.Success) { await subscription.Close().ConfigureAwait(false); return(new CallResult <UpdateSubscription>(null, subResult.Error)); } subscription.Request = request; subscription.Socket.ShouldReconnect = true; return(new CallResult <UpdateSubscription>(new UpdateSubscription(subscription), null)); }