public static async Task <byte[]> ReadAllContentAsync(this ValueHttpRequest request)
        {
            var memoryStream = new MemoryStream();

            var contentStream = new HttpContentStream(request, ownsRequest: false);

            await using (contentStream.ConfigureAwait(false))
            {
                await contentStream.CopyToAsync(memoryStream).ConfigureAwait(false);
            }

            return(memoryStream.ToArray());
        }
        /// <inheritdoc/>
        public override async ValueTask <Connection> ConnectAsync(EndPoint endPoint, IConnectionProperties?options = null, CancellationToken cancellationToken = default)
        {
            string authority = endPoint switch
            {
                DnsEndPoint dns => Tools.EscapeIdnHost(dns.Host) + ":" + dns.Port.ToString(CultureInfo.InvariantCulture),
                IPEndPoint ip4 when ip4.AddressFamily == AddressFamily.InterNetwork => ip4.Address.ToString() + ":" + ip4.Port.ToString(CultureInfo.InvariantCulture),
                IPEndPoint ip6 when ip6.AddressFamily == AddressFamily.InterNetworkV6 => "[" + ip6.Address.ToString() + "]:" + ip6.Port.ToString(CultureInfo.InvariantCulture),
                null => throw new ArgumentNullException(nameof(endPoint)),
                      _ => throw new ArgumentException($"{nameof(EndPoint)} is of an unsupported type. Must be one of {nameof(DnsEndPoint)} or {nameof(IPEndPoint)}", nameof(endPoint))
            };

            byte[] authorityBytes = Encoding.ASCII.GetBytes(authority);

            ValueHttpRequest request = (await _httpConnection.CreateNewRequestAsync(_httpVersion, _httpVersionPolicy, cancellationToken).ConfigureAwait(false))
                                       ?? throw new Exception($"{nameof(HttpConnection)} in use by {nameof(HttpTunnelConnectionFactory)} has been closed by peer.");

            try
            {
                request.ConfigureRequest(contentLength: null, hasTrailingHeaders: false);
                request.WriteConnectRequest(authorityBytes);
                await request.FlushHeadersAsync(cancellationToken).ConfigureAwait(false);

                bool hasResponse = await request.ReadToFinalResponseAsync(cancellationToken).ConfigureAwait(false);

                Debug.Assert(hasResponse);

                if ((int)request.StatusCode > 299)
                {
                    throw new Exception($"Connect to HTTP tunnel failed; received status code {request.StatusCode}.");
                }

                var localEndPoint = new TunnelEndPoint(request.LocalEndPoint, request.RemoteEndPoint);
                var stream        = new HttpContentStream(request, ownsRequest: true);
                return(new HttpTunnelConnection(localEndPoint, endPoint, stream));
            }
            catch
            {
                await request.DisposeAsync(cancellationToken).ConfigureAwait(false);

                throw;
            }
        }
Example #3
0
            int Read(HttpContentStream _, ArraySegment <byte> destination)
            {
                var result = 0;

                loop : switch (_state)
                {
                case State.Eoi :
                    {
                        Free();
                        return(result);
                    }

                case State.CopyAll:
                case State.CopyChunk:
                {
                    while (_remainingLength > 0)
                    {
                        var read =
                            _input.Read(destination.Array, destination.Offset,
                                        (int)Math.Min(Math.Min(int.MaxValue, _remainingLength), destination.Count));

                        result           += read;
                        _remainingLength -= read;
                        destination       = destination.Slice(read);

                        if (destination.Count == 0)
                        {
                            return(result);
                        }
                    }

                    if (_state == State.CopyChunk)
                    {
                        if (ReadLine().Length > 0)
                        {
                            throw new Exception("Invalid HTTP chunked transfer encoding.");
                        }
                        goto case State.ReadChunkSize;
                    }

                    if (_state == State.CopyAll)
                    {
                        _state = State.Eoi;
                        break;
                    }

                    throw new Exception("Internal implementation error.");
                }

                case State.ReadChunkSize:
                {
                    var chunkSize = _remainingLength =
                        int.Parse(ReadLine(), NumberStyles.HexNumber);

                    _state = chunkSize == 0 ? State.Eoi : State.CopyChunk;
                    break;
                }
                }

                goto loop;
            }
Example #4
0
        public static HttpMessage Read(Stream stream)
        {
            Version        version              = null;
            string         requestMethod        = null;
            Uri            requestUrl           = null;
            HttpStatusCode responseStatusCode   = 0;
            string         responseReasonPhrase = null;
            long?          contentLength        = null;
            var            chunked              = false;
            var            headerList           = new List <KeyValuePair <string, string> >();

            void OnRequestLine(string method, string url, string protocolVersion)
            {
                version       = protocolVersion != null ? new Version(protocolVersion) : new Version(0, 9);
                requestMethod = method;
                requestUrl    = new Uri(url, UriKind.RelativeOrAbsolute);
            }

            void OnResponseLine(string protocolVersion, int statusCode, string reasonPhrase)
            {
                version              = new Version(protocolVersion);
                responseStatusCode   = (HttpStatusCode)statusCode;
                responseReasonPhrase = reasonPhrase;
            }

            void OnHeader(string name, string value)
            {
                headerList.Add(new KeyValuePair <string, string>(name, value));

                if (!string.IsNullOrEmpty(value))
                {
                    if ("Transfer-Encoding".Equals(name, StringComparison.OrdinalIgnoreCase))
                    {
                        if (value.IndexOf(',') < 0)
                        {
                            chunked = "chunked".Equals(value.Trim(), StringComparison.OrdinalIgnoreCase);
                        }
                        else
                        {
                            chunked = value.Split(CommaSingletonArray, StringSplitOptions.RemoveEmptyEntries)
                                      .Any(v => "chunked".Equals(v.Trim(), StringComparison.OrdinalIgnoreCase));
                        }
                    }
                    else if ("Content-Length".Equals(name, StringComparison.OrdinalIgnoreCase))
                    {
                        contentLength = long.Parse(value, NumberStyles.AllowLeadingWhite
                                                   | NumberStyles.AllowTrailingWhite,
                                                   CultureInfo.InvariantCulture);
                    }
                }
            }

            HttpMessagePrologueParser.Parse(stream,
                                            HttpMessagePrologueParser.CreateDelegatingSink(
                                                OnRequestLine,
                                                OnResponseLine,
                                                OnHeader));

            var trailingHeaders = chunked ? null : HttpMessage.EmptyKeyValuePairs;

            var initialState
                = "CONNECT".Equals(requestMethod, StringComparison.OrdinalIgnoreCase) ||
                  "GET".Equals(requestMethod, StringComparison.OrdinalIgnoreCase) && (contentLength ?? 0) == 0 ||
                  responseStatusCode == HttpStatusCode.SwitchingProtocols && contentLength is null && !chunked ||
                  contentLength == 0
                ? State.Eoi
                : chunked
                ? State.ReadChunkSize
                : State.CopyAll;

            var contentStream = new HttpContentStream(stream, initialState, contentLength);

            var headers = new ReadOnlyCollection <KeyValuePair <string, string> >(headerList);

            var message = requestMethod != null
                        ? (HttpMessage) new HttpRequest(requestMethod, requestUrl, version, headers, contentStream, trailingHeaders)
                        : new HttpResponse(version, responseStatusCode, responseReasonPhrase, headers, contentStream, trailingHeaders);

            if (chunked)
            {
                void OnTrailingHeadersRead(object _, IList <KeyValuePair <string, string> > hs)
                {
                    contentStream.TrailingHeadersRead -= OnTrailingHeadersRead;
                    message.InitializeTrailingHeaders(new ReadOnlyCollection <KeyValuePair <string, string> >(hs));
                }

                contentStream.TrailingHeadersRead += OnTrailingHeadersRead;
            }

            return(message);
        }
Example #5
0
            int Read(HttpContentStream _, ArraySegment <byte> destination)
            {
                var result = 0;

                loop : switch (_state)
                {
                case State.Eoi :
                    {
                        return(result);
                    }

                case State.CopyAll:
                case State.CopyChunk:
                {
                    while (_remainingLength > 0 || _remainingLength == null)
                    {
                        var count = (int)Math.Min(_remainingLength is long n
                                                      ? Math.Min(int.MaxValue, n)
                                                      : int.MaxValue,
                                                  destination.Count);

                        var read = _input.Read(destination.Array, destination.Offset, count);

                        result           += read;
                        _remainingLength -= read;
                        destination       = destination.Slice(read);

                        if (read == 0)
                        {
                            break;
                        }

                        if (destination.Count == 0)
                        {
                            return(result);
                        }
                    }

                    if (_state == State.CopyChunk)
                    {
                        if (ReadLine().Length > 0)
                        {
                            throw new Exception("Invalid HTTP chunked transfer encoding.");
                        }
                        goto case State.ReadChunkSize;
                    }

                    if (_state == State.CopyAll)
                    {
                        _state = State.Eoi;
                        break;
                    }

                    throw new Exception("Internal implementation error.");
                }

                case State.ReadChunkSize:
                {
                    // NOTE! Chunk extension is IGNORED; only the size is read and used.
                    //
                    //   chunk          = chunk-size [ chunk-extension ] CRLF
                    //                    chunk-data CRLF
                    //   chunk-size     = 1*HEX
                    //   chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
                    //   chunk-ext-name = token
                    //   chunk-ext-val  = token | quoted-string

                    var line      = ReadLine();
                    var i         = line.IndexOfAny(ChunkSizeDelimiters);
                    var chunkSize = int.Parse(i > 0 ? line.Substring(0, i) : line, NumberStyles.HexNumber);
                    _remainingLength = chunkSize;

                    ChunkSizeRead?.Invoke(this, chunkSize);

                    if (chunkSize > 0)
                    {
                        _state = State.CopyChunk;
                    }
                    else
                    {
                        List <KeyValuePair <string, string> > headers = null;
                        foreach (var header in HttpMessagePrologueParser.ReadHeaders(_input))
                        {
                            (headers ??= new List <KeyValuePair <string, string> >()).Add(header);
                        }

                        TrailingHeadersRead?.Invoke(this, headers ?? (IList <KeyValuePair <string, string> >)HttpMessage.EmptyKeyValuePairs);

                        _state = State.Eoi;
                    }

                    break;
                }
                }

                goto loop;
            }
 public HttpTunnelConnection(EndPoint localEndPoint, EndPoint remoteEndPoint, HttpContentStream stream) : base(stream)
 {
     LocalEndPoint  = localEndPoint;
     RemoteEndPoint = remoteEndPoint;
 }