public static void AddResponseCookiesToContainer(WinHttpRequestState state) { HttpRequestMessage request = state.RequestMessage; SafeWinHttpHandle requestHandle = state.RequestHandle; CookieContainer cookieContainer = state.Handler.CookieContainer; Debug.Assert(state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer); Debug.Assert(cookieContainer != null); // Get 'Set-Cookie' headers from response. char[] buffer = null; uint index = 0; string cookieHeader; while (WinHttpResponseParser.GetResponseHeader( requestHandle, Interop.WinHttp.WINHTTP_QUERY_SET_COOKIE, ref buffer, ref index, out cookieHeader)) { try { cookieContainer.SetCookies(request.RequestUri, cookieHeader); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(cookieContainer, $"Added cookie: {cookieHeader}"); } } catch (CookieException) { // We ignore malformed cookies in the response. if (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(cookieContainer, $"Ignoring invalid cookie: {cookieHeader}"); } } } }
public static void AddResponseCookiesToContainer(WinHttpRequestState state) { HttpRequestMessage request = state.RequestMessage; SafeWinHttpHandle requestHandle = state.RequestHandle; CookieContainer cookieContainer = state.Handler.CookieContainer; Debug.Assert(state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer); Debug.Assert(cookieContainer != null); // Get 'Set-Cookie' headers from response. List <string> cookieHeaders = WinHttpResponseParser.GetResponseHeaders(requestHandle, Interop.WinHttp.WINHTTP_QUERY_SET_COOKIE); WinHttpTraceHelper.Trace("WINHTTP_QUERY_SET_COOKIE"); foreach (string cookieHeader in cookieHeaders) { WinHttpTraceHelper.Trace(cookieHeader); try { cookieContainer.SetCookies(request.RequestUri, cookieHeader); WinHttpTraceHelper.Trace(cookieHeader); } catch (CookieException) { // We ignore malformed cookies in the response. WinHttpTraceHelper.Trace("Ignoring invalid cookie: {0}", cookieHeader); } } }
private void ReadResponseTrailers() { // Only load response trailers if: // 1. WINHTTP_QUERY_FLAG_TRAILERS is supported by the OS // 2. HTTP/2 or later (WINHTTP_QUERY_FLAG_TRAILERS does not work with HTTP/1.1) // 3. Response trailers not already loaded if (!WinHttpTrailersHelper.OsSupportsTrailers || _responseMessage.Version < WinHttpHandler.HttpVersion20 || _readTrailingHeaders) { return; } _readTrailingHeaders = true; var bufferLength = WinHttpResponseParser.GetResponseHeaderCharBufferLength(_requestHandle, isTrailingHeaders: true); if (bufferLength != 0) { char[] trailersBuffer = ArrayPool <char> .Shared.Rent(bufferLength); try { WinHttpResponseParser.ParseResponseTrailers(_requestHandle, _responseMessage, trailersBuffer); } finally { ArrayPool <char> .Shared.Return(trailersBuffer); } } }
public void CheckResponseForAuthentication( WinHttpRequestState state, ref uint proxyAuthScheme, ref uint serverAuthScheme) { uint supportedSchemes = 0; uint firstSchemeIgnored = 0; uint authTarget = 0; Uri uri = state.RequestMessage.RequestUri; state.RetryRequest = false; // Check the status code and retry the request applying credentials if needed. var statusCode = (HttpStatusCode)WinHttpResponseParser.GetResponseHeaderNumberInfo( state.RequestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE); switch (statusCode) { case HttpStatusCode.Unauthorized: if (state.ServerCredentials == null || state.LastStatusCode == HttpStatusCode.Unauthorized) { // Either we don't have server credentials or we already tried // to set the credentials and it failed before. // So we will let the 401 be the final status code returned. break; } state.LastStatusCode = statusCode; // Determine authorization scheme to use. We ignore the firstScheme // parameter which is included in the supportedSchemes flags already. // We pass the schemes to ChooseAuthScheme which will pick the scheme // based on most secure scheme to least secure scheme ordering. if (!Interop.WinHttp.WinHttpQueryAuthSchemes( state.RequestHandle, out supportedSchemes, out firstSchemeIgnored, out authTarget)) { WinHttpException.ThrowExceptionUsingLastError(); } // WinHTTP returns the proper authTarget based on the status code (401, 407). // But we can validate with assert. Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER); serverAuthScheme = ChooseAuthScheme(supportedSchemes); if (serverAuthScheme != 0) { SetWinHttpCredential( state.RequestHandle, state.ServerCredentials, uri, serverAuthScheme, authTarget); state.RetryRequest = true; } break; case HttpStatusCode.ProxyAuthenticationRequired: if (state.LastStatusCode == HttpStatusCode.ProxyAuthenticationRequired) { // We tried already to set the credentials. break; } state.LastStatusCode = statusCode; // If we don't have any proxy credentials to try, then we end up with 407. ICredentials proxyCreds = state.Proxy == null ? state.DefaultProxyCredentials : state.Proxy.Credentials; if (proxyCreds == null) { break; } // Determine authorization scheme to use. We ignore the firstScheme // parameter which is included in the supportedSchemes flags already. // We pass the schemes to ChooseAuthScheme which will pick the scheme // based on most secure scheme to least secure scheme ordering. if (!Interop.WinHttp.WinHttpQueryAuthSchemes( state.RequestHandle, out supportedSchemes, out firstSchemeIgnored, out authTarget)) { WinHttpException.ThrowExceptionUsingLastError(); } // WinHTTP returns the proper authTarget based on the status code (401, 407). // But we can validate with assert. Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY); proxyAuthScheme = ChooseAuthScheme(supportedSchemes); state.RetryRequest = true; break; default: if (state.PreAuthenticate && serverAuthScheme != 0) { SaveServerCredentialsToCache(uri, serverAuthScheme, state.ServerCredentials); } break; } }
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; } // Create an HTTP request handle. state.RequestHandle = Interop.WinHttp.WinHttpOpenRequest( connectHandle, state.RequestMessage.Method.Method, state.RequestMessage.RequestUri.PathAndQuery, null, 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) { _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); } }
public void CheckResponseForAuthentication( WinHttpRequestState state, ref uint proxyAuthScheme, ref uint serverAuthScheme) { uint supportedSchemes = 0; uint firstSchemeIgnored = 0; uint authTarget = 0; Uri uri = state.RequestMessage.RequestUri; state.RetryRequest = false; // Check the status code and retry the request applying credentials if needed. var statusCode = (HttpStatusCode)WinHttpResponseParser.GetResponseHeaderNumberInfo( state.RequestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE); switch (statusCode) { case HttpStatusCode.Unauthorized: if (state.ServerCredentials == null || state.LastStatusCode == HttpStatusCode.Unauthorized) { // Either we don't have server credentials or we already tried // to set the credentials and it failed before. // So we will let the 401 be the final status code returned. break; } state.LastStatusCode = statusCode; // Determine authorization scheme to use. We ignore the firstScheme // parameter which is included in the supportedSchemes flags already. // We pass the schemes to ChooseAuthScheme which will pick the scheme // based on most secure scheme to least secure scheme ordering. if (!Interop.WinHttp.WinHttpQueryAuthSchemes( state.RequestHandle, out supportedSchemes, out firstSchemeIgnored, out authTarget)) { // WinHTTP returns an error for schemes it doesn't handle. // So, we need to ignore the error and just let it stay at 401. break; } // WinHTTP returns the proper authTarget based on the status code (401, 407). // But we can validate with assert. Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER); serverAuthScheme = ChooseAuthScheme(supportedSchemes, state.RequestMessage.RequestUri, state.ServerCredentials); if (serverAuthScheme != 0) { if (SetWinHttpCredential( state.RequestHandle, state.ServerCredentials, uri, serverAuthScheme, authTarget)) { state.RetryRequest = true; } } break; case HttpStatusCode.ProxyAuthenticationRequired: if (state.LastStatusCode == HttpStatusCode.ProxyAuthenticationRequired) { // We tried already to set the credentials. break; } state.LastStatusCode = statusCode; // If we don't have any proxy credentials to try, then we end up with 407. ICredentials proxyCreds = state.Proxy == null ? state.DefaultProxyCredentials : state.Proxy.Credentials; if (proxyCreds == null) { break; } // Determine authorization scheme to use. We ignore the firstScheme // parameter which is included in the supportedSchemes flags already. // We pass the schemes to ChooseAuthScheme which will pick the scheme // based on most secure scheme to least secure scheme ordering. if (!Interop.WinHttp.WinHttpQueryAuthSchemes( state.RequestHandle, out supportedSchemes, out firstSchemeIgnored, out authTarget)) { // WinHTTP returns an error for schemes it doesn't handle. // So, we need to ignore the error and just let it stay at 407. break; } // WinHTTP returns the proper authTarget based on the status code (401, 407). // But we can validate with assert. Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY); proxyAuthScheme = ChooseAuthScheme( supportedSchemes, // https://github.com/dotnet/runtime/issues/16737. If Proxy==null, we're using the system proxy which is possibly // discovered/calculated with a PAC file. So, we can't determine the actual proxy uri at // this point since it is calculated internally in WinHTTP. For now, pass in null for the uri. state.Proxy?.GetProxy(state.RequestMessage.RequestUri), proxyCreds); state.RetryRequest = true; break; default: if (state.PreAuthenticate && serverAuthScheme != 0) { SaveServerCredentialsToCache(uri, serverAuthScheme, state.ServerCredentials); } break; } }