コード例 #1
0
        /// <summary>
        /// Add a handler for a subscription
        /// </summary>
        /// <typeparam name="T">The type of data the subscription expects</typeparam>
        /// <param name="request">The request of the subscription</param>
        /// <param name="identifier">The identifier of the subscription (can be null if request param is used)</param>
        /// <param name="userSubscription">Whether or not this is a user subscription (counts towards the max amount of handlers on a socket)</param>
        /// <param name="connection">The socket connection the handler is on</param>
        /// <param name="dataHandler">The handler of the data received</param>
        /// <returns></returns>
        protected virtual SocketSubscription AddHandler <T>(object request, string identifier, bool userSubscription, SocketConnection connection, Action <T> dataHandler)
        {
            Action <SocketConnection, JToken> internalHandler = (socketWrapper, data) =>
            {
                if (typeof(T) == typeof(string))
                {
                    dataHandler((T)Convert.ChangeType(data.ToString(), typeof(T)));
                    return;
                }

                var desResult = Deserialize <T>(data, false);
                if (!desResult.Success)
                {
                    log.Write(LogVerbosity.Warning, $"Failed to deserialize data into type {typeof(T)}: {desResult.Error}");
                    return;
                }

                dataHandler(desResult.Data);
            };

            if (request != null)
            {
                return(connection.AddHandler(request, userSubscription, internalHandler));
            }
            return(connection.AddHandler(identifier, userSubscription, internalHandler));
        }
コード例 #2
0
        public void SocketMessages_Should_BeProcessedInDataHandlers()
        {
            // arrange
            var client = new TestSocketClient(new SocketClientOptions("")
            {
                ReconnectInterval = TimeSpan.Zero, LogVerbosity = LogVerbosity.Debug
            });
            var socket = client.CreateSocket();

            socket.ShouldReconnect = true;
            socket.CanConnect      = true;
            socket.DisconnectTime  = DateTime.UtcNow;
            var    sub      = new SocketConnection(client, socket);
            var    rstEvent = new ManualResetEvent(false);
            JToken result   = null;

            sub.AddHandler(SocketSubscription.CreateForIdentifier("TestHandler", true, (connection, data) =>
            {
                result = data;
                rstEvent.Set();
            }));
            client.ConnectSocketSub(sub);

            // act
            socket.InvokeMessage("{\"property\": 123}");
            rstEvent.WaitOne(1000);

            // assert
            Assert.IsTrue((int)result["property"] == 123);
        }
コード例 #3
0
        protected virtual SocketConnection BitMaxGetWebsocket(string address, bool authenticated)
        {
            address = address.TrimEnd('/');
            var socketResult = sockets.Where(s =>
                                             s.Value.Socket.Url.TrimEnd('/') == address.TrimEnd('/') &&
                                             (s.Value.Authenticated == authenticated || !authenticated) &&
                                             s.Value.Connected).OrderBy(s => s.Value.HandlerCount).FirstOrDefault();
            var result = socketResult.Equals(default(KeyValuePair <int, SocketConnection>)) ? null : socketResult.Value;

            if (result != null)
            {
                if (result.HandlerCount < SocketCombineTarget || (sockets.Count >= MaxSocketConnections && sockets.All(s => s.Value.HandlerCount >= SocketCombineTarget)))
                {
                    // Use existing socket if it has less than target connections OR it has the least connections and we can't make new
                    return(result);
                }
            }

            // Create new socket
            var socket        = CreateSocket(address);
            var socketWrapper = new SocketConnection(this, socket);

            foreach (var kvp in genericHandlers)
            {
                var handler = SocketSubscription.CreateForIdentifier(kvp.Key, false, kvp.Value);
                socketWrapper.AddHandler(handler);
            }

            return(socketWrapper);
        }
コード例 #4
0
        /// <inheritdoc />
        protected override SocketConnection GetWebsocket(string address, bool authenticated)
        {
            // Override because signalr puts `/signalr/` add the end of the url
            var socketResult = sockets.Where(s => s.Value.Socket.Url == address + "/signalr/" && (s.Value.Authenticated == authenticated || !authenticated) && s.Value.Connected).OrderBy(s => s.Value.HandlerCount).FirstOrDefault();
            var result       = socketResult.Equals(default(KeyValuePair <int, SocketConnection>)) ? null : socketResult.Value;

            if (result != null)
            {
                if (result.HandlerCount < SocketCombineTarget || (sockets.Count >= MaxSocketConnections && sockets.All(s => s.Value.HandlerCount >= SocketCombineTarget)))
                {
                    // Use existing socket if it has less than target connections OR it has the least connections and we can't make new
                    return(result);
                }
            }

            // Create new socket
            var socket        = CreateSocket(address);
            var socketWrapper = new SocketConnection(this, socket);

            foreach (var kvp in genericHandlers)
            {
                socketWrapper.AddHandler(SocketSubscription.CreateForIdentifier(kvp.Key, false, kvp.Value));
            }
            return(socketWrapper);
        }
コード例 #5
0
        /// <inheritdoc/>
        protected override SocketSubscription AddHandler <T>(object?request, string?identifier, bool userSubscription, SocketConnection connection, Action <T> dataHandler)
        {
            void InternalHandler(SocketConnection socketWrapper, JToken data)
            {
                T response = GetWsMessage <T>(data);

                if (response == null)
                {
                    var m = GetWsResponse(data);
                    log.Write(LogVerbosity.Warning, $"Failed to deserialize data into type {typeof(T)} ResponseType:{m?.Meta?.ResponseType}, Token:{m?.Meta?.Token} Msg.Length:{m?.Msg?.Length}");
                    return;
                }
                dataHandler(response);
            }

            var handler = request == null
                                ? SocketSubscription.CreateForIdentifier(identifier !, userSubscription, InternalHandler)
                                : SocketSubscription.CreateForRequest(request, userSubscription, InternalHandler);

            connection.AddHandler(handler);
            return(handler);
        }
コード例 #6
0
        /// <inheritdoc />
        protected override async Task <CallResult <UpdateSubscription> > Subscribe <T>(string url, object?request, string?identifier, bool authenticated, Action <T> dataHandler)
        {
            SocketConnection?  socket;
            SocketSubscription handler;
            var released = false;
            await semaphoreSlim.WaitAsync().ConfigureAwait(false);

            try
            {
                socket = GetWebsocket(url, authenticated);
                if (socket == null)
                {
                    KucoinToken          token;
                    var                  clientOptions   = KucoinClient.DefaultOptions.Copy();
                    KucoinApiCredentials?thisCredentials = (KucoinApiCredentials?)authProvider?.Credentials;
                    if (thisCredentials != null)
                    {
                        clientOptions.ApiCredentials = new KucoinApiCredentials(thisCredentials.Key !.GetString(),
                                                                                thisCredentials.Secret !.GetString(), thisCredentials.PassPhrase.GetString());
                    }

                    using (var restClient = new KucoinClient(clientOptions))
                    {
                        var tokenResult = restClient.GetWebsocketToken(authenticated).Result;
                        if (!tokenResult)
                        {
                            return(new CallResult <UpdateSubscription>(null, tokenResult.Error));
                        }
                        token = tokenResult.Data;
                    }

                    // Create new socket
                    var s = CreateSocket(token.Servers.First().Endpoint + "?token=" + token.Token);
                    socket = new SocketConnection(this, s);
                    foreach (var kvp in genericHandlers)
                    {
                        socket.AddHandler(SocketSubscription.CreateForIdentifier(kvp.Key, false, kvp.Value));
                    }
                }

                handler = AddHandler(request, identifier, true, socket, dataHandler);
                if (SocketCombineTarget == 1)
                {
                    // Can release early when only a single sub per connection
                    semaphoreSlim.Release();
                    released = true;
                }

                var connectResult = await ConnectIfNeeded(socket, authenticated).ConfigureAwait(false);

                if (!connectResult)
                {
                    return(new CallResult <UpdateSubscription>(null, connectResult.Error));
                }
            }
            finally
            {
                //When the task is ready, release the semaphore. It is vital to ALWAYS release the semaphore when we are ready, or else we will end up with a Semaphore that is forever locked.
                //This is why it is important to do the Release within a try...finally clause; program execution may crash or take a different path, this way you are guaranteed execution
                if (!released)
                {
                    semaphoreSlim.Release();
                }
            }


            if (request != null)
            {
                var subResult = await SubscribeAndWait(socket, request, handler).ConfigureAwait(false);

                if (!subResult)
                {
                    await socket.Close(handler).ConfigureAwait(false);

                    return(new CallResult <UpdateSubscription>(null, subResult.Error));
                }
            }
            else
            {
                handler.Confirmed = true;
            }

            socket.ShouldReconnect = true;
            return(new CallResult <UpdateSubscription>(new UpdateSubscription(socket, handler), null));
        }