Exemplo n.º 1
0
        /// <summary>
        /// Copies bytes from the stream provided in our constructor into the target <paramref name="stream"/>.
        /// </summary>
        /// <remarks>
        /// This is used internally by HttpClient.SendAsync to send the request body.
        /// Here's the sequence of events as of commit 17300169760c61a90cab8d913636c1058a30a8c1 (https://github.com/dotnet/corefx -- tag v3.1.1).
        ///
        /// <code>
        /// HttpClient.SendAsync -->
        /// HttpMessageInvoker.SendAsync -->
        /// HttpClientHandler.SendAsync -->
        /// SocketsHttpHandler.SendAsync -->
        /// HttpConnectionHandler.SendAsync -->
        /// HttpConnectionPoolManager.SendAsync -->
        /// HttpConnectionPool.SendAsync --> ... -->
        /// {
        ///     HTTP/1.1: HttpConnection.SendAsync -->
        ///               HttpConnection.SendAsyncCore -->
        ///               HttpConnection.SendRequestContentAsync -->
        ///               HttpContent.CopyToAsync
        ///
        ///     HTTP/2:   Http2Connection.SendAsync -->
        ///               Http2Stream.SendRequestBodyAsync -->
        ///               HttpContent.CopyToAsync
        ///
        ///     /* Only in .NET 5:
        ///     HTTP/3:   Http3Connection.SendAsync -->
        ///               Http3Connection.SendWithoutWaitingAsync -->
        ///               Http3RequestStream.SendAsync -->
        ///               Http3RequestStream.SendContentAsync -->
        ///               HttpContent.CopyToAsync
        ///     */
        /// }
        ///
        /// HttpContent.CopyToAsync -->
        /// HttpContent.SerializeToStreamAsync (bingo!)
        /// </code>
        ///
        /// Conclusion: by overriding HttpContent.SerializeToStreamAsync,
        /// we have full control over pumping bytes to the target stream for all protocols
        /// (except Web Sockets, which is handled separately).
        /// </remarks>
        protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
        {
            if (Started)
            {
                throw new InvalidOperationException("Stream was already consumed.");
            }

            Started = true;
            try
            {
                if (_autoFlushHttpClientOutgoingStream)
                {
                    // HttpClient's machinery keeps an internal buffer that doesn't get flushed to the socket on every write.
                    // Some protocols (e.g. gRPC) may rely on specific bytes being sent, and HttpClient's buffering would prevent it.
                    // AutoFlushingStream delegates to the provided stream, adding calls to FlushAsync on every WriteAsync.
                    // Note that HttpClient does NOT call Flush on the underlying socket, so the perf impact of this is expected to be small.
                    // This statement is based on current knowledge as of .NET Core 3.1.201.
                    stream = new AutoFlushingStream(stream);
                }

                // Immediately flush request stream to send headers
                // https://github.com/dotnet/corefx/issues/39586#issuecomment-516210081
                await stream.FlushAsync();

                await _streamCopier.CopyAsync(_source, stream, _cancellation);

                _tcs.TrySetResult(true);
            }
            catch (Exception ex)
            {
                _tcs.TrySetException(ex);
                throw;
            }
        }
        /// <summary>
        /// Copies bytes from the stream provided in our constructor into the target <paramref name="stream"/>.
        /// </summary>
        /// <remarks>
        /// This is used internally by HttpClient.SendAsync to send the request body.
        /// Here's the sequence of events as of commit 17300169760c61a90cab8d913636c1058a30a8c1 (https://github.com/dotnet/corefx -- tag v3.1.1).
        ///
        /// <code>
        /// HttpClient.SendAsync -->
        /// HttpMessageInvoker.SendAsync -->
        /// HttpClientHandler.SendAsync -->
        /// SocketsHttpHandler.SendAsync -->
        /// HttpConnectionHandler.SendAsync -->
        /// HttpConnectionPoolManager.SendAsync -->
        /// HttpConnectionPool.SendAsync --> ... -->
        /// {
        ///     HTTP/1.1: HttpConnection.SendAsync -->
        ///               HttpConnection.SendAsyncCore -->
        ///               HttpConnection.SendRequestContentAsync -->
        ///               HttpContent.CopyToAsync
        ///
        ///     HTTP/2:   Http2Connection.SendAsync -->
        ///               Http2Stream.SendRequestBodyAsync -->
        ///               HttpContent.CopyToAsync
        ///
        ///     /* Only in .NET 5:
        ///     HTTP/3:   Http3Connection.SendAsync -->
        ///               Http3Connection.SendWithoutWaitingAsync -->
        ///               Http3RequestStream.SendAsync -->
        ///               Http3RequestStream.SendContentAsync -->
        ///               HttpContent.CopyToAsync
        ///     */
        /// }
        ///
        /// HttpContent.CopyToAsync -->
        /// HttpContent.SerializeToStreamAsync (bingo!)
        /// </code>
        ///
        /// Conclusion: by overriding HttpContent.SerializeToStreamAsync,
        /// we have full control over pumping bytes to the target stream for all protocols
        /// (except Web Sockets, which is handled separately).
        /// </remarks>
        protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
        {
            if (Started)
            {
                throw new InvalidOperationException("Stream was already consumed.");
            }

            Started = true;
            try
            {
                // Immediately flush request stream to send headers
                // https://github.com/dotnet/corefx/issues/39586#issuecomment-516210081
                await stream.FlushAsync();

                await _streamCopier.CopyAsync(_source, stream, _cancellation);

                _tcs.TrySetResult(true);
            }
            catch (Exception ex)
            {
                _tcs.TrySetException(ex);
                throw;
            }
        }