Example #1
0
        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);
            }
        }
Example #2
0
        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;
            }
        }
        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;
        }
        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);
            }
        }