Esempio n. 1
0
        public async ValueTask ConnectAsync(Socket socket, EndPoint endpoint, CancellationToken cancel)
        {
            // HTTP connect request
            string addr = endpoint.ToString() !;
            var    sb   = new System.Text.StringBuilder();

            sb.Append("CONNECT ");
            sb.Append(addr);
            sb.Append(" HTTP/1.1\r\nHost: ");
            sb.Append(addr);
            sb.Append("\r\n\r\n");

            // Send the connect request.
            await socket.SendAsync(System.Text.Encoding.ASCII.GetBytes(sb.ToString()),
                                   SocketFlags.None,
                                   cancel).ConfigureAwait(false);

            // Read the HTTP response, reserve enough space for reading at least HTTP1.1
            byte[] buffer   = new byte[256];
            int    received = 0;

            while (true)
            {
                received += await socket.ReceiveAsync(buffer.AsMemory(received),
                                                      SocketFlags.None,
                                                      cancel).ConfigureAwait(false);

                // Check if we received the full HTTP response, if not, continue reading otherwise we're done.
                int end = HttpParser.IsCompleteMessage(buffer.AsSpan(0, received));
                if (end < 0 && received == buffer.Length)
                {
                    // We need to allocate a new buffer
                    byte[] newBuffer = new byte[buffer.Length * 2];
                    Buffer.BlockCopy(buffer, 0, newBuffer, 0, received);
                    buffer = newBuffer;
                }
                else
                {
                    break;
                }
            }

            var parser = new HttpParser();

            parser.Parse(buffer);
            if (parser.Status() != 200)
            {
                // TODO the retry always use the same proxy address
                throw new ConnectFailedException(RetryPolicy.AfterDelay(TimeSpan.Zero));
            }
        }
Esempio n. 2
0
        public async ValueTask InitializeAsync(CancellationToken cancel)
        {
            await _underlying.InitializeAsync(cancel).ConfigureAwait(false);

            try
            {
                // The server waits for the client's upgrade request, the client sends the upgrade request.
                if (!_incoming)
                {
                    // Compose the upgrade request.
                    var sb = new StringBuilder();
                    sb.Append("GET " + _resource + " HTTP/1.1\r\n");
                    sb.Append("Host: " + _host + "\r\n");
                    sb.Append("Upgrade: websocket\r\n");
                    sb.Append("Connection: Upgrade\r\n");
                    sb.Append("Sec-WebSocket-Protocol: " + IceProtocol + "\r\n");
                    sb.Append("Sec-WebSocket-Version: 13\r\n");
                    sb.Append("Sec-WebSocket-Key: ");

                    // The value for Sec-WebSocket-Key is a 16-byte random number, encoded with Base64.
                    byte[] key = new byte[16];
                    _rand.NextBytes(key);
                    _key = Convert.ToBase64String(key);
                    sb.Append(_key + "\r\n\r\n"); // EOM
                    byte[] data = _utf8.GetBytes(sb.ToString());
                    _sendBuffer.Add(data);

                    await _underlying.SendAsync(_sendBuffer, cancel).ConfigureAwait(false);
                }
                _sendBuffer.Clear();

                // Try to read the client's upgrade request or the server's response.
                var httpBuffer = new ArraySegment <byte>();
                while (true)
                {
                    ReadOnlyMemory <byte> buffer = await _underlying.ReceiveAsync(0, cancel).ConfigureAwait(false);

                    if (httpBuffer.Count + buffer.Length > _communicator.IncomingFrameSizeMax)
                    {
                        throw new InvalidDataException(
                                  "WebSocket frame size is greater than the configured IncomingFrameSizeMax value");
                    }

                    ArraySegment <byte> tmpBuffer = new byte[httpBuffer.Count + buffer.Length];
                    if (httpBuffer.Count > 0)
                    {
                        httpBuffer.CopyTo(tmpBuffer);
                    }
                    buffer.CopyTo(tmpBuffer.Slice(httpBuffer.Count));
                    httpBuffer = tmpBuffer;

                    // Check if we have enough data for a complete frame.
                    int endPos = HttpParser.IsCompleteMessage(httpBuffer);
                    if (endPos != -1)
                    {
                        // Add back the un-consumed data to the buffer.
                        _underlying.Rewind(httpBuffer.Count - endPos);
                        httpBuffer = httpBuffer.Slice(0, endPos);
                        break; // Done
                    }
                }

                try
                {
                    if (_parser.Parse(httpBuffer))
                    {
                        if (_incoming)
                        {
                            (bool addProtocol, string key) = ReadUpgradeRequest();

                            // Compose the response.
                            var sb = new StringBuilder();
                            sb.Append("HTTP/1.1 101 Switching Protocols\r\n");
                            sb.Append("Upgrade: websocket\r\n");
                            sb.Append("Connection: Upgrade\r\n");
                            if (addProtocol)
                            {
                                sb.Append($"Sec-WebSocket-Protocol: {IceProtocol}\r\n");
                            }

                            // The response includes:
                            //
                            // "A |Sec-WebSocket-Accept| header field.  The value of this header field is constructed
                            // by concatenating /key/, defined above in step 4 in Section 4.2.2, with the string
                            // "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this concatenated value
                            // to obtain a 20-byte value and base64-encoding (see Section 4 of [RFC4648]) this 20-byte
                            // hash.
                            sb.Append("Sec-WebSocket-Accept: ");
                            string input = key + WsUUID;
#pragma warning disable CA5350 // Do Not Use Weak Cryptographic Algorithms
                            using var sha1 = SHA1.Create();
                            byte[] hash = sha1.ComputeHash(_utf8.GetBytes(input));
#pragma warning restore CA5350                                                         // Do Not Use Weak Cryptographic Algorithms
                            sb.Append(Convert.ToBase64String(hash) + "\r\n" + "\r\n"); // EOM

                            Debug.Assert(_sendBuffer.Count == 0);
                            byte[] data = _utf8.GetBytes(sb.ToString());
                            _sendBuffer.Add(data);
                            await _underlying.SendAsync(_sendBuffer, cancel).ConfigureAwait(false);

                            _sendBuffer.Clear();
                        }
                        else
                        {
                            ReadUpgradeResponse();
                        }
                    }
                    else
                    {
                        throw new InvalidDataException("incomplete WebSocket request frame");
                    }
                }
                catch (WebSocketException ex)
                {
                    throw new InvalidDataException(ex.Message, ex);
                }
            }
            catch (Exception ex)
            {
                if (_communicator.TraceLevels.Transport >= 2)
                {
                    _communicator.Logger.Trace(TraceLevels.TransportCategory,
                                               $"{_transportName} connection HTTP upgrade request failed\n{this}\n{ex}");
                }
                throw;
            }

            if (_communicator.TraceLevels.Transport >= 1)
            {
                if (_incoming)
                {
                    _communicator.Logger.Trace(TraceLevels.TransportCategory,
                                               $"accepted {_transportName} connection HTTP upgrade request\n{this}");
                }
                else
                {
                    _communicator.Logger.Trace(TraceLevels.TransportCategory,
                                               $"{_transportName} connection HTTP upgrade request accepted\n{this}");
                }
            }
        }