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; } }
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; }
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); }
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; }