private static void AddRequestHeaders( SafeWinHttpHandle requestHandle, HttpRequestMessage requestMessage, CookieContainer cookies) { var requestHeadersBuffer = new StringBuilder(); // Manually add cookies. if (cookies != null) { string cookieHeader = WinHttpCookieContainerAdapter.GetCookieHeader(requestMessage.RequestUri, cookies); if (!string.IsNullOrEmpty(cookieHeader)) { requestHeadersBuffer.AppendLine(cookieHeader); } } // Serialize general request headers. requestHeadersBuffer.AppendLine(requestMessage.Headers.ToString()); // Serialize entity-body (content) headers. if (requestMessage.Content != null) { // TODO: Content-Length header isn't getting correctly placed using ToString() // This is a bug in HttpContentHeaders that needs to be fixed. if (requestMessage.Content.Headers.ContentLength.HasValue) { long contentLength = requestMessage.Content.Headers.ContentLength.Value; requestMessage.Content.Headers.ContentLength = null; requestMessage.Content.Headers.ContentLength = contentLength; } requestHeadersBuffer.AppendLine(requestMessage.Content.Headers.ToString()); } // Add request headers to WinHTTP request handle. if (!Interop.WinHttp.WinHttpAddRequestHeaders( requestHandle, requestHeadersBuffer, (uint)requestHeadersBuffer.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_ADD)) { WinHttpException.ThrowExceptionUsingLastError(); } }
private static void OnRequestRedirect(WinHttpRequestState state, Uri redirectUri) { Debug.Assert(state != null, "OnRequestRedirect: state is null"); Debug.Assert(state.Handler != null, "OnRequestRedirect: state.Handler is null"); Debug.Assert(state.RequestMessage != null, "OnRequestRedirect: state.RequestMessage is null"); Debug.Assert(redirectUri != null, "OnRequestRedirect: redirectUri is null"); // If we're manually handling cookies, we need to reset them based on the new URI. if (state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer) { // Add any cookies that may have arrived with redirect response. WinHttpCookieContainerAdapter.AddResponseCookiesToContainer(state); // Reset cookie request headers based on redirectUri. WinHttpCookieContainerAdapter.ResetCookieRequestHeaders(state, redirectUri); } state.RequestMessage.RequestUri = redirectUri; // Redirection to a new uri may require a new connection through a potentially different proxy. // If so, we will need to respond to additional 407 proxy auth demands and re-attach any // proxy credentials. The ProcessResponse() method looks at the state.LastStatusCode // before attaching proxy credentials and marking the HTTP request to be re-submitted. // So we need to reset the LastStatusCode remembered. Otherwise, it will see additional 407 // responses as an indication that proxy auth failed and won't retry the HTTP request. if (state.LastStatusCode == HttpStatusCode.ProxyAuthenticationRequired) { state.LastStatusCode = 0; } // For security reasons, we drop the server credential if it is a // NetworkCredential. But we allow credentials in a CredentialCache // since they are specifically tied to URI's. if (!(state.ServerCredentials is CredentialCache)) { state.ServerCredentials = null; } // Similarly, we need to clear any Auth headers that were added to the request manually or // through the default headers. ResetAuthRequestHeaders(state); }
private async void StartRequest(object obj) { WinHttpRequestState state = (WinHttpRequestState)obj; bool secureConnection = false; HttpResponseMessage responseMessage = null; Exception savedException = null; SafeWinHttpHandle connectHandle = null; if (state.CancellationToken.IsCancellationRequested) { state.Tcs.TrySetCanceled(state.CancellationToken); return; } try { EnsureSessionHandleExists(state); SetSessionHandleOptions(); // Specify an HTTP server. connectHandle = Interop.WinHttp.WinHttpConnect( _sessionHandle, state.RequestMessage.RequestUri.Host, (ushort)state.RequestMessage.RequestUri.Port, 0); ThrowOnInvalidHandle(connectHandle); connectHandle.SetParentHandle(_sessionHandle); if (state.RequestMessage.RequestUri.Scheme == UriScheme.Https) { secureConnection = true; } else { secureConnection = false; } // Try to use the requested version if a known/supported version was explicitly requested. // Otherwise, we simply use winhttp's default. string httpVersion = null; if (state.RequestMessage.Version == HttpVersion.Version10) { httpVersion = "HTTP/1.0"; } else if (state.RequestMessage.Version == HttpVersion.Version11) { httpVersion = "HTTP/1.1"; } // Create an HTTP request handle. state.RequestHandle = Interop.WinHttp.WinHttpOpenRequest( connectHandle, state.RequestMessage.Method.Method, state.RequestMessage.RequestUri.PathAndQuery, httpVersion, Interop.WinHttp.WINHTTP_NO_REFERER, Interop.WinHttp.WINHTTP_DEFAULT_ACCEPT_TYPES, secureConnection ? Interop.WinHttp.WINHTTP_FLAG_SECURE : 0); ThrowOnInvalidHandle(state.RequestHandle); state.RequestHandle.SetParentHandle(connectHandle); // Set callback function. SetStatusCallback(state.RequestHandle, WinHttpRequestCallback.StaticCallbackDelegate); // Set needed options on the request handle. SetRequestHandleOptions(state); bool chunkedModeForSend = IsChunkedModeForSend(state.RequestMessage); AddRequestHeaders( state.RequestHandle, state.RequestMessage, _cookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer ? _cookieContainer : null); uint proxyAuthScheme = 0; uint serverAuthScheme = 0; state.RetryRequest = false; // The only way to abort pending async operations in WinHTTP is to close the WinHTTP handle. // We will detect a cancellation request on the cancellation token by registering a callback. // If the callback is invoked, then we begin the abort process by disposing the handle. This // will have the side-effect of WinHTTP cancelling any pending I/O and accelerating its callbacks // on the handle and thus releasing the awaiting tasks in the loop below. This helps to provide // a more timely, cooperative, cancellation pattern. using (state.CancellationToken.Register(s => ((WinHttpRequestState)s).RequestHandle.Dispose(), state)) { do { _authHelper.PreAuthenticateRequest(state, proxyAuthScheme); await InternalSendRequestAsync(state).ConfigureAwait(false); if (state.RequestMessage.Content != null) { await InternalSendRequestBodyAsync(state, chunkedModeForSend).ConfigureAwait(false); } bool receivedResponse = await InternalReceiveResponseHeadersAsync(state).ConfigureAwait(false); if (receivedResponse) { // If we're manually handling cookies, we need to add them to the container after // each response has been received. if (state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer) { WinHttpCookieContainerAdapter.AddResponseCookiesToContainer(state); } _authHelper.CheckResponseForAuthentication( state, ref proxyAuthScheme, ref serverAuthScheme); } } while (state.RetryRequest); } state.CancellationToken.ThrowIfCancellationRequested(); responseMessage = WinHttpResponseParser.CreateResponseMessage(state, _doManualDecompressionCheck); // Since the headers have been read, set the "receive" timeout to be based on each read // call of the response body data. WINHTTP_OPTION_RECEIVE_TIMEOUT sets a timeout on each // lower layer winsock read. uint optionData = (uint)_receiveDataTimeout.TotalMilliseconds; SetWinHttpOption(state.RequestHandle, Interop.WinHttp.WINHTTP_OPTION_RECEIVE_TIMEOUT, ref optionData); } catch (Exception ex) { if (state.SavedException != null) { savedException = state.SavedException; } else { savedException = ex; } } finally { SafeWinHttpHandle.DisposeAndClearHandle(ref connectHandle); } // Move the main task to a terminal state. This releases any callers of SendAsync() that are awaiting. if (responseMessage != null) { state.Tcs.TrySetResult(responseMessage); } else { HandleAsyncException(state, savedException); } }