private static void OnRequestReadComplete(WinHttpRequestState state, uint bytesRead) { Debug.Assert(state != null, "OnRequestReadComplete: state is null"); Debug.Assert(state.TcsReadFromResponseStream != null, "TcsReadFromResponseStream is null"); Debug.Assert(!state.TcsReadFromResponseStream.Task.IsCompleted, "TcsReadFromResponseStream.Task is completed"); state.DisposeCtrReadFromResponseStream(); // If we read to the end of the stream and we're using 'Content-Length' semantics on the response body, // then verify we read at least the number of bytes required. if (bytesRead == 0 && state.ExpectedBytesToRead.HasValue && state.CurrentBytesRead < state.ExpectedBytesToRead.Value) { state.TcsReadFromResponseStream.TrySetException( new IOException(string.Format( SR.net_http_io_read_incomplete, state.ExpectedBytesToRead.Value, state.CurrentBytesRead)).InitializeStackTrace()); } else { state.CurrentBytesRead += (long)bytesRead; state.TcsReadFromResponseStream.TrySetResult((int)bytesRead); } }
private static void OnRequestError(WinHttpRequestState state, Interop.WinHttp.WINHTTP_ASYNC_RESULT asyncResult) { WinHttpTraceHelper.TraceAsyncError("OnRequestError", asyncResult); Debug.Assert(state != null, "OnRequestError: state is null"); var innerException = WinHttpException.CreateExceptionUsingError((int)asyncResult.dwError).InitializeStackTrace(); switch ((uint)asyncResult.dwResult.ToInt32()) { case Interop.WinHttp.API_SEND_REQUEST: state.TcsSendRequest.TrySetException(innerException); break; case Interop.WinHttp.API_RECEIVE_RESPONSE: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_RESEND_REQUEST) { state.RetryRequest = true; state.TcsReceiveResponseHeaders.TrySetResult(false); } else if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED) { // WinHttp will automatically drop any client SSL certificates that we // have pre-set into the request handle including the NULL certificate // (which means we have no certs to send). For security reasons, we don't // allow the certificate to be re-applied. But we need to tell WinHttp // explicitly that we don't have any certificate to send. Debug.Assert(state.RequestHandle != null, "OnRequestError: state.RequestHandle is null"); WinHttpHandler.SetNoClientCertificate(state.RequestHandle); state.RetryRequest = true; state.TcsReceiveResponseHeaders.TrySetResult(false); } else if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { state.TcsReceiveResponseHeaders.TrySetCanceled(state.CancellationToken); } else { state.TcsReceiveResponseHeaders.TrySetException(innerException); } break; case Interop.WinHttp.API_QUERY_DATA_AVAILABLE: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { // TODO: Issue #2165. We need to pass in the cancellation token from the // user's ReadAsync() call into the TrySetCanceled(). Debug.WriteLine("RequestCallback: QUERY_DATA_AVAILABLE - ERROR_WINHTTP_OPERATION_CANCELLED"); state.TcsQueryDataAvailable.TrySetCanceled(); } else { state.TcsQueryDataAvailable.TrySetException( new IOException(SR.net_http_io_read, innerException)); } break; case Interop.WinHttp.API_READ_DATA: state.DisposeCtrReadFromResponseStream(); if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { // TODO: Issue #2165. We need to pass in the cancellation token from the // user's ReadAsync() call into the TrySetCanceled(). Debug.WriteLine("RequestCallback: API_READ_DATA - ERROR_WINHTTP_OPERATION_CANCELLED"); state.TcsReadFromResponseStream.TrySetCanceled(); } else { state.TcsReadFromResponseStream.TrySetException( new IOException(SR.net_http_io_read, innerException)); } break; case Interop.WinHttp.API_WRITE_DATA: if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED) { // TODO: Issue #2165. We need to pass in the cancellation token from the // user's WriteAsync() call into the TrySetCanceled(). Debug.WriteLine("RequestCallback: API_WRITE_DATA - ERROR_WINHTTP_OPERATION_CANCELLED"); state.TcsInternalWriteDataToRequestStream.TrySetCanceled(); } else { state.TcsInternalWriteDataToRequestStream.TrySetException( new IOException(SR.net_http_io_write, innerException)); } break; default: Debug.Fail( "OnRequestError: Result (" + asyncResult.dwResult + ") is not expected.", "Error code: " + asyncResult.dwError + " (" + innerException.Message + ")"); break; } }
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken token) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (offset < 0) { throw new ArgumentOutOfRangeException(nameof(offset)); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } if (count > buffer.Length - offset) { throw new ArgumentException(SR.net_http_buffer_insufficient_length, nameof(buffer)); } if (token.IsCancellationRequested) { return Task.FromCanceled<int>(token); } CheckDisposed(); if (_state.TcsReadFromResponseStream != null && !_state.TcsReadFromResponseStream.Task.IsCompleted) { throw new InvalidOperationException(SR.net_http_no_concurrent_io_allowed); } _state.PinReceiveBuffer(buffer); _state.TcsReadFromResponseStream = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously); _state.TcsQueryDataAvailable = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously); _state.TcsQueryDataAvailable.Task.ContinueWith((previousTask) => { if (previousTask.IsFaulted) { _state.DisposeCtrReadFromResponseStream(); _state.TcsReadFromResponseStream.TrySetException(previousTask.Exception.InnerException); } else if (previousTask.IsCanceled || token.IsCancellationRequested) { _state.DisposeCtrReadFromResponseStream(); _state.TcsReadFromResponseStream.TrySetCanceled(token); } else { int bytesToRead; int bytesAvailable = previousTask.Result; if (bytesAvailable > count) { bytesToRead = count; } else { bytesToRead = bytesAvailable; } lock (_state.Lock) { Debug.Assert(!_requestHandle.IsInvalid); if (!Interop.WinHttp.WinHttpReadData( _requestHandle, Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset), (uint)bytesToRead, IntPtr.Zero)) { _state.DisposeCtrReadFromResponseStream(); _state.TcsReadFromResponseStream.TrySetException( new IOException(SR.net_http_io_read, WinHttpException.CreateExceptionUsingLastError().InitializeStackTrace())); } } } }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default); // Register callback on cancellation token to cancel any pending WinHTTP operation. if (token.CanBeCanceled) { WinHttpTraceHelper.Trace("WinHttpResponseStream.ReadAsync: registering for cancellation token request"); _state.CtrReadFromResponseStream = token.Register(s => ((WinHttpResponseStream)s).CancelPendingResponseStreamReadOperation(), this); } else { WinHttpTraceHelper.Trace("WinHttpResponseStream.ReadAsync: received no cancellation token"); } lock (_state.Lock) { Debug.Assert(!_requestHandle.IsInvalid); if (!Interop.WinHttp.WinHttpQueryDataAvailable(_requestHandle, IntPtr.Zero)) { _state.DisposeCtrReadFromResponseStream(); _state.TcsReadFromResponseStream.TrySetException( new IOException(SR.net_http_io_read, WinHttpException.CreateExceptionUsingLastError().InitializeStackTrace())); } } return _state.TcsReadFromResponseStream.Task; }