Пример #1
0
        private async Task SendAsyncImpl <T>(WebSocketsFrame.OpCodes opCode, T message, WebSocketsFrame.FrameFlags flags) where T : struct, IMessageWriter
        {
            //TODO: come up with a better way of getting ordered access to the socket
            var  writeLock = GetWriteSemaphore();
            bool haveLock  = writeLock.Wait(0);

            if (!haveLock)
            {   // try to acquire asynchronously instead, then
                await writeLock.WaitAsync();
            }
            try
            {
                WebSocketServer.WriteStatus(ConnectionType, $"Writing {opCode} message ({message.GetPayloadLength()} bytes)...");
                await WebSocketProtocol.WriteAsync(this, opCode, flags, ref message);

                if (opCode == WebSocketsFrame.OpCodes.Close)
                {
                    connection.Output.Complete();
                }
            }
            finally
            {
                writeLock.Release();
            }
        }
Пример #2
0
        private async Task SendAsyncImpl <T>(WebSocketsFrame.OpCodes opCode, T message) where T : struct, IMessageWriter
        {
            //TODO: come up with a better way of getting ordered access to the socket
            var  writeLock = GetWriteSemaphore();
            bool haveLock  = writeLock.Wait(0);

            if (!haveLock)
            {   // try to acquire asynchronously instead, then
                await writeLock.WaitAsync();
            }
            try
            {
                WebSocketServer.WriteStatus($"Writing {opCode} message...");
                await WebSocketProtocol.WriteAsync(this, opCode, ref message);

                if (opCode == WebSocketsFrame.OpCodes.Close)
                {
                    Close();
                }
            }
            finally
            {
                writeLock.Release();
            }
        }
        private async void OnConnection(IChannel connection)
        {
            using (connection)
            {
                WebSocketConnection socket = null;
                try
                {
                    WriteStatus(ConnectionType.Server, "Connected");

                    WriteStatus(ConnectionType.Server, "Parsing http request...");
                    var request = await ParseHttpRequest(connection.Input);

                    try
                    {
                        WriteStatus(ConnectionType.Server, "Identifying protocol...");
                        socket = GetProtocol(connection, ref request);
                        WriteStatus(ConnectionType.Server, $"Protocol: {WebSocketProtocol.Name}");
                        WriteStatus(ConnectionType.Server, "Authenticating...");
                        if (!await OnAuthenticateAsync(socket, ref request.Headers))
                        {
                            throw new InvalidOperationException("Authentication refused");
                        }
                        WriteStatus(ConnectionType.Server, "Completing handshake...");
                        await WebSocketProtocol.CompleteServerHandshakeAsync(ref request, socket);
                    }
                    finally
                    {
                        request.Dispose(); // can't use "ref request" or "ref headers" otherwise
                    }
                    WriteStatus(ConnectionType.Server, "Handshake complete hook...");
                    await OnHandshakeCompleteAsync(socket);

                    connections.TryAdd(socket, socket);
                    WriteStatus(ConnectionType.Server, "Processing incoming frames...");
                    await socket.ProcessIncomingFramesAsync(this);

                    WriteStatus(ConnectionType.Server, "Exiting...");
                    await socket.CloseAsync();
                }
                catch (Exception ex)
                {// meh, bye bye broken connection
                    try { socket?.Dispose(); } catch { }
                    WriteStatus(ConnectionType.Server, ex.StackTrace);
                    WriteStatus(ConnectionType.Server, ex.GetType().Name);
                    WriteStatus(ConnectionType.Server, ex.Message);
                }
                finally
                {
                    WebSocketConnection tmp;
                    if (socket != null)
                    {
                        connections.TryRemove(socket, out tmp);
                    }
                    try { connection.Output.Complete(); } catch { }
                    try { connection.Input.Complete(); } catch { }
                }
            }
        }
Пример #4
0
        internal async Task ProcessIncomingFramesAsync(WebSocketServer server)
        {
            while (true)
            {
                var buffer = await connection.Input.ReadAsync();

                try
                {
                    if (buffer.IsEmpty && connection.Input.Reading.IsCompleted)
                    {
                        break; // that's all, folks
                    }
                    WebSocketsFrame frame;
                    if (WebSocketProtocol.TryReadFrameHeader(ref buffer, out frame))
                    {
                        int payloadLength = frame.PayloadLength;
                        // buffer now points to the payload

                        if (connectionType == ConnectionType.Server)
                        {
                            if (!frame.IsMasked)
                            {
                                throw new InvalidOperationException("Client-to-server frames should be masked");
                            }
                        }
                        else
                        {
                            if (frame.IsMasked)
                            {
                                throw new InvalidOperationException("Server-to-client frames should not be masked");
                            }
                        }


                        if (frame.IsControlFrame && !frame.IsFinal)
                        {
                            throw new InvalidOperationException("Control frames cannot be fragmented");
                        }
                        await OnFrameReceivedAsync(ref frame, ref buffer, server);

                        // and finally, progress past the frame
                        if (payloadLength != 0)
                        {
                            buffer = buffer.Slice(payloadLength);
                        }
                    }
                }
                finally
                {
                    connection.Input.Advance(buffer.Start, buffer.End);
                }
            }
        }
Пример #5
0
        public static async Task <WebSocketConnection> ConnectAsync(
            string location, string protocol       = null, string origin = null,
            Action <HttpRequestHeaders> addHeaders = null,
            ChannelFactory channelFactory          = null)
        {
            WebSocketServer.WriteStatus(ConnectionType.Client, $"Connecting to {location}...");
            Uri uri;

            if (!Uri.TryCreate(location, UriKind.Absolute, out uri) ||
                uri.Scheme != "ws")
            {
                throw new ArgumentException(nameof(location));
            }
            IPAddress ip;

            if (!IPAddress.TryParse(uri.Host, out ip))
            {
                throw new NotImplementedException("host must be an IP address at the moment, sorry");
            }
            WebSocketServer.WriteStatus(ConnectionType.Client, $"Opening socket to {ip}:{uri.Port}...");
            var socket = await SocketConnection.ConnectAsync(new IPEndPoint(ip, uri.Port), channelFactory);

            return(await WebSocketProtocol.ClientHandshake(socket, uri, origin, protocol));
        }
Пример #6
0
        internal static async Task <WebSocketConnection> ClientHandshake(IChannel channel, Uri uri, string origin, string protocol)
        {
            WebSocketServer.WriteStatus(ConnectionType.Client, "Writing client handshake...");
            // write the outbound request portion of the handshake
            var output = channel.Output.Alloc();

            output.Write(GET);
            output.WriteAsciiString(uri.PathAndQuery);
            output.Write(HTTP_Host);
            output.WriteAsciiString(uri.Host);
            output.Write(UpgradeConnectionKey);

            byte[] challengeKey = new byte[WebSocketProtocol.SecResponseLength];

            GetRandomBytes(challengeKey); // we only want the first 16 for entropy, but... meh
            output.Ensure(WebSocketProtocol.SecRequestLength);
            int bytesWritten = Base64.Encode(new ReadOnlySpan <byte>(challengeKey, 0, 16), output.Memory);

            // now cheekily use that data we just wrote the the output buffer
            // as a source to compute the expected bytes, and store them back
            // into challengeKey; sneaky!
            WebSocketProtocol.ComputeReply(
                output.Memory.Slice(0, WebSocketProtocol.SecRequestLength).Span,
                challengeKey);
            output.Advance(bytesWritten);
            output.Write(CRLF);

            if (!string.IsNullOrWhiteSpace(origin))
            {
                output.WriteAsciiString("Origin: ");
                output.WriteAsciiString(origin);
                output.Write(CRLF);
            }
            if (!string.IsNullOrWhiteSpace(protocol))
            {
                output.WriteAsciiString("Sec-WebSocket-Protocol: ");
                output.WriteAsciiString(protocol);
                output.Write(CRLF);
            }
            output.Write(WebSocketVersion);
            output.Write(CRLF); // final CRLF to end the HTTP request
            await output.FlushAsync();

            WebSocketServer.WriteStatus(ConnectionType.Client, "Parsing response to client handshake...");
            using (var resp = await WebSocketServer.ParseHttpResponse(channel.Input))
            {
                if (!resp.HttpVersion.Equals(ExpectedHttpVersion) ||
                    !resp.StatusCode.Equals(ExpectedStatusCode) ||
                    !resp.StatusText.Equals(ExpectedStatusText) ||
                    !resp.Headers.GetRaw("Upgrade").Equals(ExpectedUpgrade) ||
                    !resp.Headers.GetRaw("Connection").Equals(ExpectedConnection))
                {
                    throw new InvalidOperationException("Not a web-socket server");
                }

                var accept = resp.Headers.GetRaw("Sec-WebSocket-Accept");
                if (!accept.Equals(challengeKey))
                {
                    throw new InvalidOperationException("Sec-WebSocket-Accept mismatch");
                }

                protocol = resp.Headers.GetAsciiString("Sec-WebSocket-Protocol");
            }

            var webSocket = new WebSocketConnection(channel, ConnectionType.Client)
            {
                Host        = uri.Host,
                RequestLine = uri.OriginalString,
                Origin      = origin,
                Protocol    = protocol
            };

            webSocket.StartProcessingIncomingFrames();
            return(webSocket);
        }