public async ValueTask <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.Version.Major != 1 || request.Version.Minor != 1) { throw new PlatformNotSupportedException($"Only HTTP 1.1 supported -- request.Version was {request.Version}"); } HttpContent requestContent = request.Content; // Add headers to define content transfer, if not present if (requestContent != null && request.Headers.TransferEncodingChunked != true && requestContent.Headers.ContentLength == null) { // We have content, but neither Transfer-Encoding or Content-Length is set. // TODO: Tests expect Transfer-Encoding here always. // This seems wrong to me; if we can compute the content length, // why not use it instead of falling back to Transfer-Encoding? #if false if (requestContent.TryComputeLength(out contentLength)) { // We know the content length, so set the header requestContent.Headers.ContentLength = contentLength; } else #endif { request.Headers.TransferEncodingChunked = true; } } // Add Host header, if not present if (request.Headers.Host == null) { Uri uri = request.RequestUri; string hostString = uri.Host; if (!uri.IsDefaultPort) { hostString += ":" + uri.Port.ToString(); } request.Headers.Host = hostString; } // Write request line await WriteStringAsync(request.Method.Method, cancellationToken).ConfigureAwait(false); await WriteCharAsync(' ', cancellationToken).ConfigureAwait(false); if (_usingProxy) { await WriteStringAsync(request.RequestUri.AbsoluteUri, cancellationToken).ConfigureAwait(false); } else { await WriteStringAsync(request.RequestUri.PathAndQuery, cancellationToken).ConfigureAwait(false); } await WriteCharAsync(' ', cancellationToken).ConfigureAwait(false); await WriteCharAsync('H', cancellationToken).ConfigureAwait(false); await WriteCharAsync('T', cancellationToken).ConfigureAwait(false); await WriteCharAsync('T', cancellationToken).ConfigureAwait(false); await WriteCharAsync('P', cancellationToken).ConfigureAwait(false); await WriteCharAsync('/', cancellationToken).ConfigureAwait(false); await WriteCharAsync('1', cancellationToken).ConfigureAwait(false); await WriteCharAsync('.', cancellationToken).ConfigureAwait(false); await WriteCharAsync('1', cancellationToken).ConfigureAwait(false); await WriteCharAsync('\r', cancellationToken).ConfigureAwait(false); await WriteCharAsync('\n', cancellationToken).ConfigureAwait(false); // Write request headers await WriteHeadersAsync(request.Headers, cancellationToken).ConfigureAwait(false); if (requestContent == null) { // Write out Content-Length: 0 header to indicate no body, // unless this is a method that never has a body. if (request.Method != HttpMethod.Get && request.Method != HttpMethod.Head) { await WriteStringAsync("Content-Length: 0\r\n", cancellationToken).ConfigureAwait(false); } } else { // Write content headers await WriteHeadersAsync(requestContent.Headers, cancellationToken).ConfigureAwait(false); } // CRLF for end of headers. await WriteCharAsync('\r', cancellationToken).ConfigureAwait(false); await WriteCharAsync('\n', cancellationToken).ConfigureAwait(false); // Write body, if any if (requestContent != null) { HttpContentWriteStream stream = (request.Headers.TransferEncodingChunked == true ? (HttpContentWriteStream) new ChunkedEncodingWriteStream(this) : (HttpContentWriteStream) new ContentLengthWriteStream(this)); // TODO: CopyToAsync doesn't take a CancellationToken, how do we deal with Cancellation here? await request.Content.CopyToAsync(stream, _transportContext).ConfigureAwait(false); await stream.FinishAsync(cancellationToken).ConfigureAwait(false); } await FlushAsync(cancellationToken).ConfigureAwait(false); return(await ParseResponseAsync(request, cancellationToken).ConfigureAwait(false)); }