private async Task <HttpActionStatus> ReadResponseBodyAsync(Request request, WebRequestState state, CancellationToken cancellationToken) { try { var contentLength = (int)state.Response.ContentLength; if (contentLength > 0) { state.BodyBuffer = Settings.BufferFactory(contentLength); var totalBytesRead = 0; // If a contentLength-sized buffer won't end up in LOH, it can be used directly to work with socket. // Otherwise, it's better to use a small temporary buffer from a pool because the passed reference will be stored for a long time due to Keep-Alive. if (contentLength < Constants.LOHObjectSizeThreshold) { while (totalBytesRead < contentLength) { var bytesToRead = Math.Min(contentLength - totalBytesRead, Constants.BufferSize); var bytesRead = await state.ResponseStream.ReadAsync(state.BodyBuffer, totalBytesRead, bytesToRead, cancellationToken) .ConfigureAwait(false); if (bytesRead == 0) { break; } totalBytesRead += bytesRead; } } else { using (BufferPool.Default.Rent(Constants.BufferSize, out var buffer)) { while (totalBytesRead < contentLength) { var bytesToRead = Math.Min(contentLength - totalBytesRead, buffer.Length); var bytesRead = await state.ResponseStream.ReadAsync(buffer, 0, bytesToRead).ConfigureAwait(false); if (bytesRead == 0) { break; } Buffer.BlockCopy(buffer, 0, state.BodyBuffer, totalBytesRead, bytesRead); totalBytesRead += bytesRead; } } } if (totalBytesRead < contentLength) { throw new EndOfStreamException($"Response stream ended prematurely. Read only {totalBytesRead} byte(s), but Content-Length specified {contentLength}."); } state.BodyBufferLength = totalBytesRead; } else { state.BodyStream = new MemoryStream(); using (BufferPool.Default.Rent(Constants.BufferSize, out var buffer)) { while (true) { var bytesRead = await state.ResponseStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); if (bytesRead == 0) { break; } state.BodyStream.Write(buffer, 0, bytesRead); if (ResponseBodyIsTooLarge(state)) { state.CancelRequestAttempt(); state.BodyStream = null; return(HttpActionStatus.InsufficientStorage); } } } } return(HttpActionStatus.Success); } catch (Exception error) { if (cancellationToken.IsCancellationRequested) { return(HttpActionStatus.RequestCanceled); } LogReceiveBodyFailure(request, error); return(HttpActionStatus.ReceiveFailure); } }
private async Task <Response> SendInternalAsync(Request request, WebRequestState state, TimeSpan?connectionTimeout, CancellationToken cancellationToken) { CancellationTokenRegistration registration; try { registration = cancellationToken.Register(state.CancelRequest); } catch (ObjectDisposedException) { return(Responses.Canceled); } using (registration) using (state) { if (state.RequestCanceled) { return(Responses.Canceled); } state.Request = WebRequestFactory.Create(request, state.TimeRemaining, Settings, log); HttpActionStatus status; // Step 1: send request body if it was supplied in request. if (state.RequestCanceled) { return(Responses.Canceled); } if (request.HasBody) { status = await connectTimeLimiter .LimitConnectTime(SendRequestBodyAsync(request, state, cancellationToken), request, state, connectionTimeout) .ConfigureAwait(false); if (status != HttpActionStatus.Success) { return(responseFactory.BuildFailureResponse(status, state)); } } // Step 2: receive response from server. if (state.RequestCanceled) { return(Responses.Canceled); } status = request.HasBody ? await GetResponseAsync(request, state).ConfigureAwait(false) : await connectTimeLimiter.LimitConnectTime(GetResponseAsync(request, state), request, state, connectionTimeout).ConfigureAwait(false); if (status != HttpActionStatus.Success) { return(responseFactory.BuildFailureResponse(status, state)); } // Step 3 - download request body if it exists. if (!NeedToReadResponseBody(request, state)) { return(responseFactory.BuildSuccessResponse(state)); } if (ResponseBodyIsTooLarge(state)) { state.CancelRequestAttempt(); return(responseFactory.BuildResponse(ResponseCode.InsufficientStorage, state)); } if (state.RequestCanceled) { return(Responses.Canceled); } if (NeedToStreamResponseBody(state)) { state.ReturnStreamDirectly = true; state.PreventNextDispose(); return(responseFactory.BuildSuccessResponse(state)); } status = await ReadResponseBodyAsync(request, state, cancellationToken) .ConfigureAwait(false); return(status == HttpActionStatus.Success ? responseFactory.BuildSuccessResponse(state) : responseFactory.BuildFailureResponse(status, state)); } }