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