private static void OnRequestSendRequestComplete(WinHttpRequestState state) { Debug.Assert(state != null, "OnRequestSendRequestComplete: state is null"); Debug.Assert(state.LifecycleAwaitable != null, "OnRequestSendRequestComplete: LifecycleAwaitable is null"); state.LifecycleAwaitable.SetResult(1); }
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 WinHttpCallback( IntPtr handle, IntPtr context, uint internetStatus, IntPtr statusInformation, uint statusInformationLength) { WinHttpTraceHelper.TraceCallbackStatus("WinHttpCallback", handle, context, internetStatus); if (Environment.HasShutdownStarted) { WinHttpTraceHelper.Trace("WinHttpCallback: Environment.HasShutdownStarted returned True"); return; } if (context == IntPtr.Zero) { return; } WinHttpRequestState state = WinHttpRequestState.FromIntPtr(context); Debug.Assert(state != null, "WinHttpCallback must have a non-null state object"); RequestCallback(handle, state, internetStatus, statusInformation, statusInformationLength); }
private static void OnRequestReceiveResponseHeadersComplete(WinHttpRequestState state) { Debug.Assert(state != null, "OnRequestReceiveResponseHeadersComplete: state is null"); Debug.Assert(state.LifecycleAwaitable != null, "LifecycleAwaitable is null"); state.LifecycleAwaitable.SetResult(1); }
private void SetRequestHandleCredentialsOptions(WinHttpRequestState state) { // Set WinHTTP to send/prevent default credentials for either proxy or server auth. bool useDefaultCredentials = false; if (state.ServerCredentials == CredentialCache.DefaultCredentials) { useDefaultCredentials = true; } else if (state.WindowsProxyUsePolicy != WindowsProxyUsePolicy.DoNotUseProxy) { if (state.Proxy == null && _defaultProxyCredentials == CredentialCache.DefaultCredentials) { useDefaultCredentials = true; } else if (state.Proxy != null && state.Proxy.Credentials == CredentialCache.DefaultCredentials) { useDefaultCredentials = true; } } uint optionData = useDefaultCredentials ? Interop.WinHttp.WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW : Interop.WinHttp.WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH; SetWinHttpOption(state.RequestHandle, Interop.WinHttp.WINHTTP_OPTION_AUTOLOGON_POLICY, ref optionData); }
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); } } }
public static void ResetCookieRequestHeaders(WinHttpRequestState state, Uri redirectUri) { SafeWinHttpHandle requestHandle = state.RequestHandle; Debug.Assert(state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer); // Clear cookies. if (!Interop.WinHttp.WinHttpAddRequestHeaders( requestHandle, CookieHeaderNameWithColon, (uint)CookieHeaderNameWithColon.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_REPLACE)) { int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { throw WinHttpException.CreateExceptionUsingError(lastError); } } // Re-add cookies. The GetCookieHeader() method will return the correct set of // cookies based on the redirectUri. string cookieHeader = GetCookieHeader(redirectUri, state.Handler.CookieContainer); if (!string.IsNullOrEmpty(cookieHeader)) { if (!Interop.WinHttp.WinHttpAddRequestHeaders( requestHandle, cookieHeader, (uint)cookieHeader.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_ADD)) { WinHttpException.ThrowExceptionUsingLastError(); } } }
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); } }
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 static void OnRequestReceiveResponseHeadersComplete(WinHttpRequestState state) { Debug.Assert(state != null, "OnRequestReceiveResponseHeadersComplete: state is null"); Debug.Assert(state.TcsReceiveResponseHeaders != null, "TcsReceiveResponseHeaders is null"); Debug.Assert(!state.TcsReceiveResponseHeaders.Task.IsCompleted, "TcsReceiveResponseHeaders.Task is completed"); state.TcsReceiveResponseHeaders.TrySetResult(true); }
private static void OnRequestDataAvailable(WinHttpRequestState state, int bytesAvailable) { Debug.Assert(state != null, "OnRequestDataAvailable: state is null"); Debug.Assert(state.TcsQueryDataAvailable != null, "TcsQueryDataAvailable is null"); Debug.Assert(!state.TcsQueryDataAvailable.Task.IsCompleted, "TcsQueryDataAvailable.Task is completed"); state.TcsQueryDataAvailable.TrySetResult(bytesAvailable); }
private static void OnRequestSendRequestComplete(WinHttpRequestState state) { Debug.Assert(state != null, "OnRequestSendRequestComplete: state is null"); Debug.Assert(state.TcsSendRequest != null, "OnRequestSendRequestComplete: TcsSendRequest is null"); Debug.Assert(!state.TcsSendRequest.Task.IsCompleted, "OnRequestSendRequestComplete: TcsSendRequest.Task is completed"); state.TcsSendRequest.TrySetResult(true); }
private static void OnRequestWriteComplete(WinHttpRequestState state) { Debug.Assert(state != null, "OnRequestWriteComplete: state is null"); Debug.Assert(state.TcsInternalWriteDataToRequestStream != null, "TcsInternalWriteDataToRequestStream is null"); Debug.Assert(!state.TcsInternalWriteDataToRequestStream.Task.IsCompleted, "TcsInternalWriteDataToRequestStream.Task is completed"); state.TcsInternalWriteDataToRequestStream.TrySetResult(true); }
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.TcsReadFromResponseStream.TrySetResult((int)bytesRead); }
public void PreAuthenticateRequest(WinHttpRequestState state, uint proxyAuthScheme) { // Set proxy credentials if we have them. // If a proxy authentication challenge was responded to, reset // those credentials before each SendRequest, because the proxy // may require re-authentication after responding to a 401 or // to a redirect. If you don't, you can get into a // 407-401-407-401- loop. if (proxyAuthScheme != 0) { ICredentials proxyCredentials; Uri proxyUri; if (state.Proxy != null) { proxyCredentials = state.Proxy.Credentials; proxyUri = state.Proxy.GetProxy(state.RequestMessage.RequestUri); } else { proxyCredentials = state.DefaultProxyCredentials; proxyUri = state.RequestMessage.RequestUri; } SetWinHttpCredential( state.RequestHandle, proxyCredentials, proxyUri, proxyAuthScheme, Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY); } // Apply pre-authentication headers for server authentication? if (state.PreAuthenticate) { uint authScheme; NetworkCredential serverCredentials; if (GetServerCredentialsFromCache( state.RequestMessage.RequestUri, out authScheme, out serverCredentials)) { SetWinHttpCredential( state.RequestHandle, serverCredentials, state.RequestMessage.RequestUri, authScheme, Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER); state.LastStatusCode = HttpStatusCode.Unauthorized; // Remember we already set the creds. } // No cached credential to use at this time. The request will first go out with no // 'Authorization' header. Later, if a 401 occurs, we will be able to cache the credential // since we will then know the proper auth scheme to use. // // TODO: Issue #2165. Adding logging to highlight the 'cache miss'. } }
private static void RequestCallback( IntPtr handle, WinHttpRequestState state, uint internetStatus, IntPtr statusInformation, uint statusInformationLength) { try { switch (internetStatus) { case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: OnRequestSendRequestComplete(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_READ_COMPLETE: OnRequestReadComplete(state, statusInformationLength); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: OnRequestWriteComplete(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: OnRequestReceiveResponseHeadersComplete(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_REDIRECT: string redirectUriString = Marshal.PtrToStringUni(statusInformation); var redirectUri = new Uri(redirectUriString); OnRequestRedirect(state, redirectUri); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: OnRequestSendingRequest(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: Debug.Assert( statusInformationLength == Marshal.SizeOf <Interop.WinHttp.WINHTTP_ASYNC_RESULT>(), "RequestCallback: statusInformationLength=" + statusInformationLength + " must be sizeof(WINHTTP_ASYNC_RESULT)=" + Marshal.SizeOf <Interop.WinHttp.WINHTTP_ASYNC_RESULT>()); var asyncResult = Marshal.PtrToStructure <Interop.WinHttp.WINHTTP_ASYNC_RESULT>(statusInformation); OnRequestError(state, asyncResult); return; default: return; } } catch (Exception ex) { Interop.WinHttp.WinHttpCloseHandle(handle); state.SavedException = ex; } }
private static void OnRequestHandleClosing(WinHttpRequestState state) { Debug.Assert(state != null, "OnRequestSendRequestComplete: state is null"); // This is the last notification callback that WinHTTP will send. Therefore, we can // now explicitly dispose the state object which will free its corresponding GCHandle. // This will then allow the state object to be garbage collected. state.Dispose(); }
internal WinHttpRequestStream(WinHttpRequestState state, WinHttpChunkMode chunkedMode) { _state = state; _chunkedMode = chunkedMode; // Take copy of handle from state. // The state's request handle will be set to null once the response stream starts. _requestHandle = _state.RequestHandle; }
private static void RequestCallback( IntPtr handle, WinHttpRequestState state, uint internetStatus, IntPtr statusInformation, uint statusInformationLength) { try { switch (internetStatus) { case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: OnRequestSendRequestComplete(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_READ_COMPLETE: OnRequestReadComplete(state, statusInformationLength); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: OnRequestWriteComplete(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: OnRequestReceiveResponseHeadersComplete(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_REDIRECT: string redirectUriString = Marshal.PtrToStringUni(statusInformation); var redirectUri = new Uri(redirectUriString); OnRequestRedirect(state, redirectUri); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: OnRequestSendingRequest(state); return; case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: Debug.Assert( statusInformationLength == Marshal.SizeOf<Interop.WinHttp.WINHTTP_ASYNC_RESULT>(), "RequestCallback: statusInformationLength=" + statusInformationLength + " must be sizeof(WINHTTP_ASYNC_RESULT)=" + Marshal.SizeOf<Interop.WinHttp.WINHTTP_ASYNC_RESULT>()); var asyncResult = Marshal.PtrToStructure<Interop.WinHttp.WINHTTP_ASYNC_RESULT>(statusInformation); OnRequestError(state, asyncResult); return; default: return; } } catch (Exception ex) { Interop.WinHttp.WinHttpCloseHandle(handle); state.SavedException = ex; } }
protected override Task <HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) #endif { if (request == null) { throw new ArgumentNullException("request", SR.net_http_handler_norequest); } // Check for invalid combinations of properties. if (_proxy != null && _windowsProxyUsePolicy != WindowsProxyUsePolicy.UseCustomProxy) { throw new InvalidOperationException(SR.net_http_invalid_proxyusepolicy); } if (_windowsProxyUsePolicy == WindowsProxyUsePolicy.UseCustomProxy && _proxy == null) { throw new InvalidOperationException(SR.net_http_invalid_proxy); } if (_cookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer && _cookieContainer == null) { throw new InvalidOperationException(SR.net_http_invalid_cookiecontainer); } CheckDisposed(); SetOperationStarted(); TaskCompletionSource <HttpResponseMessage> tcs = new TaskCompletionSource <HttpResponseMessage>(); // Create state object and save current values of handler settings. var state = new WinHttpRequestState(); state.Tcs = tcs; state.CancellationToken = cancellationToken; state.RequestMessage = request; state.Handler = this; state.CheckCertificateRevocationList = _checkCertificateRevocationList; state.ServerCertificateValidationCallback = _serverCertificateValidationCallback; state.WindowsProxyUsePolicy = _windowsProxyUsePolicy; state.Proxy = _proxy; state.ServerCredentials = _serverCredentials; state.DefaultProxyCredentials = _defaultProxyCredentials; state.PreAuthenticate = _preAuthenticate; Task.Factory.StartNew( s => ((WinHttpRequestState)s).Handler.StartRequest(s), state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); return(tcs.Task); }
private void SetRequestHandleOptions(WinHttpRequestState state) { SetRequestHandleProxyOptions(state); SetRequestHandleDecompressionOptions(state.RequestHandle); SetRequestHandleRedirectionOptions(state.RequestHandle); SetRequestHandleCookieOptions(state.RequestHandle); SetRequestHandleTlsOptions(state.RequestHandle); SetRequestHandleClientCertificateOptions(state.RequestHandle, state.RequestMessage.RequestUri); SetRequestHandleCredentialsOptions(state); SetRequestHandleBufferingOptions(state.RequestHandle); }
private async Task InternalSendRequestBodyAsync(WinHttpRequestState state, bool chunkedModeForSend) { using (var requestStream = new WinHttpRequestStream(state, chunkedModeForSend)) { await state.RequestMessage.Content.CopyToAsync( requestStream, state.TransportContext).ConfigureAwait(false); await requestStream.EndUploadAsync(state.CancellationToken).ConfigureAwait(false); } }
private Task <bool> InternalReceiveResponseHeadersAsync(WinHttpRequestState state) { state.TcsReceiveResponseHeaders = new TaskCompletionSource <bool>(); lock (state.Lock) { if (!Interop.WinHttp.WinHttpReceiveResponse(state.RequestHandle, IntPtr.Zero)) { throw WinHttpException.CreateExceptionUsingLastError(); } } return(state.TcsReceiveResponseHeaders.Task); }
private static void ResetAuthRequestHeaders(WinHttpRequestState state) { const string AuthHeaderNameWithColon = "Authorization:"; SafeWinHttpHandle requestHandle = state.RequestHandle; // Clear auth headers. if (!Interop.WinHttp.WinHttpAddRequestHeaders( requestHandle, AuthHeaderNameWithColon, (uint)AuthHeaderNameWithColon.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_REPLACE)) { int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { throw WinHttpException.CreateExceptionUsingError(lastError, "WINHTTP_CALLBACK_STATUS_REDIRECT/WinHttpAddRequestHeaders"); } } }
private void HandleAsyncException(WinHttpRequestState state, Exception ex) { if (state.CancellationToken.IsCancellationRequested) { // If the exception was due to the cancellation token being canceled, throw cancellation exception. state.Tcs.TrySetCanceled(state.CancellationToken); } else if (ex is WinHttpException || ex is IOException) { // Wrap expected exceptions as HttpRequestExceptions since this is considered an error during // execution. All other exception types, including ArgumentExceptions and ProtocolViolationExceptions // are 'unexpected' or caused by user error and should not be wrapped. state.Tcs.TrySetException(new HttpRequestException(SR.net_http_client_execution_error, ex)); } else { state.Tcs.TrySetException(ex); } }
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 static void OnRequestReadComplete(WinHttpRequestState state, uint bytesRead) { Debug.Assert(state != null, "OnRequestReadComplete: state is null"); // 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.LifecycleAwaitable.SetException(new IOException(string.Format( SR.net_http_io_read_incomplete, state.ExpectedBytesToRead.Value, state.CurrentBytesRead))); } else { state.CurrentBytesRead += (long)bytesRead; state.LifecycleAwaitable.SetResult((int)bytesRead); } }
private Task <bool> InternalSendRequestAsync(WinHttpRequestState state) { state.TcsSendRequest = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); lock (state.Lock) { if (!Interop.WinHttp.WinHttpSendRequest( state.RequestHandle, null, 0, IntPtr.Zero, 0, 0, state.ToIntPtr())) { WinHttpException.ThrowExceptionUsingLastError(); } } return(state.TcsSendRequest.Task); }
public static void ResetCookieRequestHeaders(WinHttpRequestState state, Uri redirectUri) { SafeWinHttpHandle?requestHandle = state.RequestHandle; Debug.Assert(state.Handler != null); Debug.Assert(state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer); Debug.Assert(state.Handler.CookieContainer != null); Debug.Assert(requestHandle != null); // Clear cookies. if (!Interop.WinHttp.WinHttpAddRequestHeaders( requestHandle, CookieHeaderNameWithColon, (uint)CookieHeaderNameWithColon.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_REPLACE)) { int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { throw WinHttpException.CreateExceptionUsingError(lastError, nameof(Interop.WinHttp.WinHttpAddRequestHeaders)); } } // Re-add cookies. The GetCookieHeader() method will return the correct set of // cookies based on the redirectUri. string?cookieHeader = GetCookieHeader(redirectUri, state.Handler.CookieContainer); if (!string.IsNullOrEmpty(cookieHeader)) { if (!Interop.WinHttp.WinHttpAddRequestHeaders( requestHandle, cookieHeader, (uint)cookieHeader.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_ADD)) { WinHttpException.ThrowExceptionUsingLastError(nameof(Interop.WinHttp.WinHttpAddRequestHeaders)); } } }
public static void WinHttpCallback( IntPtr handle, IntPtr context, uint internetStatus, IntPtr statusInformation, uint statusInformationLength) { bool invokeCallback = false; if (Environment.HasShutdownStarted) { return; } if (context == IntPtr.Zero) { return; } WinHttpRequestState state = WinHttpRequestState.FromIntPtr(context); if (state != null && state.RequestHandle != null && !state.RequestHandle.IsInvalid && state.RequestHandle.DangerousGetHandle() == handle) { invokeCallback = true; } WinHttpTraceHelper.TraceCallbackStatus("WinHttpCallback", handle, invokeCallback, internetStatus); if (invokeCallback) { RequestCallback(handle, state, internetStatus, statusInformation, statusInformationLength); } }
private void SetRequestHandleProxyOptions(WinHttpRequestState state) { // We've already set the proxy on the session handle if we're using no proxy or default proxy settings. // We only need to change it on the request handle if we have a specific IWebProxy or need to manually // implement Wininet-style auto proxy detection. if (state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseCustomProxy || state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseWinInetProxy) { var proxyInfo = new Interop.WinHttp.WINHTTP_PROXY_INFO(); bool updateProxySettings = false; Uri uri = state.RequestMessage.RequestUri; try { if (state.Proxy != null) { Debug.Assert(state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseCustomProxy); updateProxySettings = true; if (state.Proxy.IsBypassed(uri)) { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY; } else { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NAMED_PROXY; Uri proxyUri = state.Proxy.GetProxy(uri); string proxyString = proxyUri.Scheme + "://" + proxyUri.Authority; proxyInfo.Proxy = Marshal.StringToHGlobalUni(proxyString); } } else if (_proxyHelper != null && _proxyHelper.AutoSettingsUsed) { if (_proxyHelper.GetProxyForUrl(_sessionHandle, uri, out proxyInfo)) { updateProxySettings = true; } } if (updateProxySettings) { GCHandle pinnedHandle = GCHandle.Alloc(proxyInfo, GCHandleType.Pinned); try { SetWinHttpOption( state.RequestHandle, Interop.WinHttp.WINHTTP_OPTION_PROXY, pinnedHandle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(proxyInfo)); } finally { pinnedHandle.Free(); } } } finally { Marshal.FreeHGlobal(proxyInfo.Proxy); Marshal.FreeHGlobal(proxyInfo.ProxyBypass); } } }
private Task<bool> InternalReceiveResponseHeadersAsync(WinHttpRequestState state) { state.TcsReceiveResponseHeaders = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); lock (state.Lock) { if (!Interop.WinHttp.WinHttpReceiveResponse(state.RequestHandle, IntPtr.Zero)) { throw WinHttpException.CreateExceptionUsingLastError(); } } return state.TcsReceiveResponseHeaders.Task; }
private void SetRequestHandleOptions(WinHttpRequestState state) { SetRequestHandleProxyOptions(state); SetRequestHandleDecompressionOptions(state.RequestHandle); SetRequestHandleRedirectionOptions(state.RequestHandle); SetRequestHandleCookieOptions(state.RequestHandle); SetRequestHandleTlsOptions(state.RequestHandle); SetRequestHandleClientCertificateOptions(state.RequestHandle, state.RequestMessage.RequestUri); SetRequestHandleCredentialsOptions(state); SetRequestHandleBufferingOptions(state.RequestHandle); SetRequestHandleHttp2Options(state.RequestHandle, state.RequestMessage.Version); }
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); 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. 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_READ_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 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; } }
internal WinHttpResponseStream(SafeWinHttpHandle requestHandle, WinHttpRequestState state) { _state = state; _requestHandle = requestHandle; }
private RendezvousAwaitable<int> InternalReceiveResponseHeadersAsync(WinHttpRequestState state) { lock (state.Lock) { if (!Interop.WinHttp.WinHttpReceiveResponse(state.RequestHandle, IntPtr.Zero)) { throw WinHttpException.CreateExceptionUsingLastError(); } } return state.LifecycleAwaitable; }
private RendezvousAwaitable<int> InternalSendRequestAsync(WinHttpRequestState state) { lock (state.Lock) { state.Pin(); if (!Interop.WinHttp.WinHttpSendRequest( state.RequestHandle, null, 0, IntPtr.Zero, 0, 0, state.ToIntPtr())) { // Dispose (which will unpin) the state object. Since this failed, WinHTTP won't associate // our context value (state object) to the request handle. And thus we won't get HANDLE_CLOSING // notifications which would normally cause the state object to be unpinned and disposed. state.Dispose(); WinHttpException.ThrowExceptionUsingLastError(); } } return state.LifecycleAwaitable; }
private void SetRequestHandleCredentialsOptions(WinHttpRequestState state) { // By default, WinHTTP sets the default credentials policy such that it automatically sends default credentials // (current user's logged on Windows credentials) to a proxy when needed (407 response). It only sends // default credentials to a server (401 response) if the server is considered to be on the Intranet. // WinHttpHandler uses a more granual opt-in model for using default credentials that can be different between // proxy and server credentials. It will explicitly allow default credentials to be sent at a later stage in // the request processing (after getting a 401/407 response) when the proxy or server credential is set as // CredentialCache.DefaultNetworkCredential. For now, we set the policy to prevent any default credentials // from being automatically sent until we get a 401/407 response. _authHelper.ChangeDefaultCredentialsPolicy( state.RequestHandle, Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER, allowDefaultCredentials:false); }
private static void OnRequestRedirect(WinHttpRequestState state, Uri redirectUri) { const string EmptyCookieHeader = "Cookie:"; Debug.Assert(state != null, "OnRequestRedirect: state is null"); Debug.Assert(redirectUri != null, "OnRequestRedirect: redirectUri is null"); Debug.Assert(state.TcsReceiveResponseHeaders != null, "TcsReceiveResponseHeaders is null"); Debug.Assert(!state.TcsReceiveResponseHeaders.Task.IsCompleted, "TcsReceiveResponseHeaders.Task is completed"); // If we're manually handling cookies, we need to reset them based on the new URI. if (state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer) { // Clear cookies. if (!Interop.WinHttp.WinHttpAddRequestHeaders( state.RequestHandle, EmptyCookieHeader, (uint)EmptyCookieHeader.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_REPLACE)) { int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { throw WinHttpException.CreateExceptionUsingError(lastError); } } // Re-add cookies. The GetCookieHeader() method will return the correct set of // cookies based on the redirectUri. string cookieHeader = WinHttpHandler.GetCookieHeader(redirectUri, state.Handler.CookieContainer); if (!string.IsNullOrEmpty(cookieHeader)) { if (!Interop.WinHttp.WinHttpAddRequestHeaders( state.RequestHandle, cookieHeader, (uint)cookieHeader.Length, Interop.WinHttp.WINHTTP_ADDREQ_FLAG_ADD)) { WinHttpException.ThrowExceptionUsingLastError(); } } } 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; } }
private void SetRequestHandleProxyOptions(WinHttpRequestState state) { // We've already set the proxy on the session handle if we're using no proxy or default proxy settings. // We only need to change it on the request handle if we have a specific IWebProxy or need to manually // implement Wininet-style auto proxy detection. if (state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseCustomProxy || state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseWinInetProxy) { var proxyInfo = new Interop.WinHttp.WINHTTP_PROXY_INFO(); bool updateProxySettings = false; Uri uri = state.RequestMessage.RequestUri; try { if (state.Proxy != null) { Debug.Assert(state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseCustomProxy); updateProxySettings = true; if (state.Proxy.IsBypassed(uri)) { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY; } else { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NAMED_PROXY; Uri proxyUri = state.Proxy.GetProxy(uri); string proxyString = string.Format( CultureInfo.InvariantCulture, "{0}://{1}", proxyUri.Scheme, proxyUri.Authority); proxyInfo.Proxy = Marshal.StringToHGlobalUni(proxyString); } } else if (_proxyHelper != null && _proxyHelper.AutoSettingsUsed) { updateProxySettings = true; _proxyHelper.GetProxyForUrl(_sessionHandle, uri, out proxyInfo); } if (updateProxySettings) { GCHandle pinnedHandle = GCHandle.Alloc(proxyInfo, GCHandleType.Pinned); try { SetWinHttpOption( state.RequestHandle, Interop.WinHttp.WINHTTP_OPTION_PROXY, pinnedHandle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(proxyInfo)); } finally { pinnedHandle.Free(); } } } finally { Marshal.FreeHGlobal(proxyInfo.Proxy); Marshal.FreeHGlobal(proxyInfo.ProxyBypass); } } }
private static void OnRequestSendingRequest(WinHttpRequestState state) { Debug.Assert(state != null, "OnRequestSendingRequest: state is null"); if (state.RequestMessage.RequestUri.Scheme != UriScheme.Https) { // Not SSL/TLS. return; } // Grab the channel binding token (CBT) information from the request handle and put it into // the TransportContext object. state.TransportContext.SetChannelBinding(state.RequestHandle); if (state.ServerCertificateValidationCallback != null) { IntPtr certHandle = IntPtr.Zero; uint certHandleSize = (uint)IntPtr.Size; if (!Interop.WinHttp.WinHttpQueryOption( state.RequestHandle, Interop.WinHttp.WINHTTP_OPTION_SERVER_CERT_CONTEXT, ref certHandle, ref certHandleSize)) { int lastError = Marshal.GetLastWin32Error(); throw WinHttpException.CreateExceptionUsingError(lastError); } // Create a managed wrapper around the certificate handle. Since this results in duplicating // the handle, we will close the original handle after creating the wrapper. var serverCertificate = new X509Certificate2(certHandle); Interop.Crypt32.CertFreeCertificateContext(certHandle); X509Chain chain = null; SslPolicyErrors sslPolicyErrors; try { WinHttpCertificateHelper.BuildChain( serverCertificate, state.RequestMessage.RequestUri.Host, state.CheckCertificateRevocationList, out chain, out sslPolicyErrors); bool result = state.ServerCertificateValidationCallback( state.RequestMessage, serverCertificate, chain, sslPolicyErrors); if (!result) { throw WinHttpException.CreateExceptionUsingError( (int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE); } } finally { if (chain != null) { chain.Dispose(); } } } }
private Task<bool> InternalSendRequestAsync(WinHttpRequestState state) { state.TcsSendRequest = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); lock (state.Lock) { state.Pin(); if (!Interop.WinHttp.WinHttpSendRequest( state.RequestHandle, null, 0, IntPtr.Zero, 0, 0, state.ToIntPtr())) { // Dispose (which will unpin) the state object. Since this failed, WinHTTP won't associate // our context value (state object) to the request handle. And thus we won't get HANDLE_CLOSING // notifications which would normally cause the state object to be unpinned and disposed. state.Dispose(); WinHttpException.ThrowExceptionUsingLastError(); } } return state.TcsSendRequest.Task; }
internal WinHttpRequestStream(WinHttpRequestState state, bool chunkedMode) { _state = state; _chunkedMode = chunkedMode; }
internal WinHttpResponseStream(WinHttpRequestState state) { _state = state; }
private async void StartRequest(WinHttpRequestState state) { if (state.CancellationToken.IsCancellationRequested) { state.Tcs.TrySetCanceled(state.CancellationToken); state.ClearSendRequestState(); return; } SafeWinHttpHandle connectHandle = null; try { EnsureSessionHandleExists(state); // Specify an HTTP server. connectHandle = Interop.WinHttp.WinHttpConnect( _sessionHandle, state.RequestMessage.RequestUri.Host, (ushort)state.RequestMessage.RequestUri.Port, 0); ThrowOnInvalidHandle(connectHandle); connectHandle.SetParentHandle(_sessionHandle); // 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 == HttpVersionInternal.Version10) { httpVersion = "HTTP/1.0"; } else if (state.RequestMessage.Version == HttpVersionInternal.Version11) { httpVersion = "HTTP/1.1"; } // Turn off additional URI reserved character escaping (percent-encoding). This matches // .NET Framework behavior. System.Uri establishes the baseline rules for percent-encoding // of reserved characters. uint flags = Interop.WinHttp.WINHTTP_FLAG_ESCAPE_DISABLE; if (state.RequestMessage.RequestUri.Scheme == UriScheme.Https) { flags |= Interop.WinHttp.WINHTTP_FLAG_SECURE; } // 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, flags); 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); if (state.RequestMessage.Content != null) { await InternalSendRequestBodyAsync(state, chunkedModeForSend).ConfigureAwait(false); } bool receivedResponse = await InternalReceiveResponseHeadersAsync(state) != 0; 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(); // 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); HttpResponseMessage responseMessage = WinHttpResponseParser.CreateResponseMessage(state, _doManualDecompressionCheck); state.Tcs.TrySetResult(responseMessage); } catch (Exception ex) { HandleAsyncException(state, state.SavedException ?? ex); } finally { SafeWinHttpHandle.DisposeAndClearHandle(ref connectHandle); state.ClearSendRequestState(); } }
public static HttpResponseMessage CreateResponseMessage( WinHttpRequestState state, bool doManualDecompressionCheck) { HttpRequestMessage request = state.RequestMessage; SafeWinHttpHandle requestHandle = state.RequestHandle; CookieUsePolicy cookieUsePolicy = state.Handler.CookieUsePolicy; CookieContainer cookieContainer = state.Handler.CookieContainer; var response = new HttpResponseMessage(); bool stripEncodingHeaders = false; // Get HTTP version, status code, reason phrase from the response headers. string version = GetResponseHeaderStringInfo(requestHandle, Interop.WinHttp.WINHTTP_QUERY_VERSION); if (string.Compare("HTTP/1.1", version, StringComparison.OrdinalIgnoreCase) == 0) { response.Version = HttpVersion.Version11; } else if (string.Compare("HTTP/1.0", version, StringComparison.OrdinalIgnoreCase) == 0) { response.Version = HttpVersion.Version10; } else { response.Version = HttpVersion.Unknown; } response.StatusCode = (HttpStatusCode)GetResponseHeaderNumberInfo( requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE); response.ReasonPhrase = GetResponseHeaderStringInfo( requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_TEXT); // Create response stream and wrap it in a StreamContent object. var responseStream = new WinHttpResponseStream(state); Stream decompressedStream = responseStream; if (doManualDecompressionCheck) { string contentEncoding = GetResponseHeaderStringInfo( requestHandle, Interop.WinHttp.WINHTTP_QUERY_CONTENT_ENCODING); if (!string.IsNullOrEmpty(contentEncoding)) { if (contentEncoding.IndexOf(EncodingNameDeflate, StringComparison.OrdinalIgnoreCase) > -1) { decompressedStream = new DeflateStream(responseStream, CompressionMode.Decompress); stripEncodingHeaders = true; } else if (contentEncoding.IndexOf(EncodingNameGzip, StringComparison.OrdinalIgnoreCase) > -1) { decompressedStream = new GZipStream(responseStream, CompressionMode.Decompress); stripEncodingHeaders = true; } } } var content = new StreamContent(decompressedStream); response.Content = content; response.RequestMessage = request; // Parse raw response headers and place them into response message. ParseResponseHeaders(requestHandle, response, stripEncodingHeaders); // Store response header cookies into custom CookieContainer. if (cookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer) { Debug.Assert(cookieContainer != null); if (response.Headers.Contains(HeaderNameSetCookie)) { IEnumerable<string> cookieHeaders = response.Headers.GetValues(HeaderNameSetCookie); foreach (var cookieHeader in cookieHeaders) { try { cookieContainer.SetCookies(request.RequestUri, cookieHeader); } catch (CookieException) { // We ignore malformed cookies in the response. } } } } return response; }
protected override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) #endif { if (request == null) { throw new ArgumentNullException(nameof(request), SR.net_http_handler_norequest); } // Check for invalid combinations of properties. if (_proxy != null && _windowsProxyUsePolicy != WindowsProxyUsePolicy.UseCustomProxy) { throw new InvalidOperationException(SR.net_http_invalid_proxyusepolicy); } if (_windowsProxyUsePolicy == WindowsProxyUsePolicy.UseCustomProxy && _proxy == null) { throw new InvalidOperationException(SR.net_http_invalid_proxy); } if (_cookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer && _cookieContainer == null) { throw new InvalidOperationException(SR.net_http_invalid_cookiecontainer); } CheckDisposed(); Guid loggingRequestId = s_diagnosticListener.LogHttpRequest(request); SetOperationStarted(); TaskCompletionSource<HttpResponseMessage> tcs = new TaskCompletionSource<HttpResponseMessage>(); // Create state object and save current values of handler settings. var state = new WinHttpRequestState(); state.Tcs = tcs; state.CancellationToken = cancellationToken; state.RequestMessage = request; state.Handler = this; state.CheckCertificateRevocationList = _checkCertificateRevocationList; state.ServerCertificateValidationCallback = _serverCertificateValidationCallback; state.WindowsProxyUsePolicy = _windowsProxyUsePolicy; state.Proxy = _proxy; state.ServerCredentials = _serverCredentials; state.DefaultProxyCredentials = _defaultProxyCredentials; state.PreAuthenticate = _preAuthenticate; Task.Factory.StartNew( s => { var whrs = (WinHttpRequestState)s; whrs.Handler.StartRequest(whrs); }, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); s_diagnosticListener.LogHttpResponse(tcs.Task, loggingRequestId); return tcs.Task; }
public void PreAuthenticateRequest(WinHttpRequestState state, uint proxyAuthScheme) { // Set proxy credentials if we have them. // If a proxy authentication challenge was responded to, reset // those credentials before each SendRequest, because the proxy // may require re-authentication after responding to a 401 or // to a redirect. If you don't, you can get into a // 407-401-407-401- loop. if (proxyAuthScheme != 0) { SetWinHttpCredential( state.RequestHandle, state.Proxy == null ? state.DefaultProxyCredentials : state.Proxy.Credentials, state.RequestMessage.RequestUri, proxyAuthScheme, Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY); } // Apply pre-authentication headers for server authentication? if (state.PreAuthenticate) { uint authScheme; NetworkCredential serverCredentials; if (GetServerCredentialsFromCache( state.RequestMessage.RequestUri, out authScheme, out serverCredentials)) { SetWinHttpCredential( state.RequestHandle, serverCredentials, state.RequestMessage.RequestUri, authScheme, Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER); state.LastStatusCode = HttpStatusCode.Unauthorized; // Remember we already set the creds. } // No cached credential to use at this time. The request will first go out with no // 'Authorization' header. Later, if a 401 occurs, we will be able to cache the credential // since we will then know the proper auth scheme to use. // // TODO: Issue #2165. Adding logging to highlight the 'cache miss'. } }
private void EnsureSessionHandleExists(WinHttpRequestState state) { if (_sessionHandle == null) { lock (_lockObject) { if (_sessionHandle == null) { SafeWinHttpHandle sessionHandle; uint accessType; // If a custom proxy is specified and it is really the system web proxy // (initial WebRequest.DefaultWebProxy) then we need to update the settings // since that object is only a sentinel. if (state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseCustomProxy) { Debug.Assert(state.Proxy != null); try { state.Proxy.GetProxy(state.RequestMessage.RequestUri); } catch (PlatformNotSupportedException) { // This is the system web proxy. state.WindowsProxyUsePolicy = WindowsProxyUsePolicy.UseWinInetProxy; state.Proxy = null; } } if (state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.DoNotUseProxy || state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseCustomProxy) { // Either no proxy at all or a custom IWebProxy proxy is specified. // For a custom IWebProxy, we'll need to calculate and set the proxy // on a per request handle basis using the request Uri. For now, // we set the session handle to have no proxy. accessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY; } else if (state.WindowsProxyUsePolicy == WindowsProxyUsePolicy.UseWinHttpProxy) { // Use WinHTTP per-machine proxy settings which are set using the "netsh winhttp" command. accessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; } else { // Use WinInet per-user proxy settings. accessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY; } WinHttpTraceHelper.Trace("WinHttpHandler.EnsureSessionHandleExists: proxy accessType={0}", accessType); sessionHandle = Interop.WinHttp.WinHttpOpen( IntPtr.Zero, accessType, Interop.WinHttp.WINHTTP_NO_PROXY_NAME, Interop.WinHttp.WINHTTP_NO_PROXY_BYPASS, (int)Interop.WinHttp.WINHTTP_FLAG_ASYNC); if (sessionHandle.IsInvalid) { int lastError = Marshal.GetLastWin32Error(); WinHttpTraceHelper.Trace("WinHttpHandler.EnsureSessionHandleExists: error={0}", lastError); if (lastError != Interop.WinHttp.ERROR_INVALID_PARAMETER) { ThrowOnInvalidHandle(sessionHandle); } // We must be running on a platform earlier than Win8.1/Win2K12R2 which doesn't support // WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY. So, we'll need to read the Wininet style proxy // settings ourself using our WinInetProxyHelper object. _proxyHelper = new WinInetProxyHelper(); sessionHandle = Interop.WinHttp.WinHttpOpen( IntPtr.Zero, _proxyHelper.ManualSettingsOnly ? Interop.WinHttp.WINHTTP_ACCESS_TYPE_NAMED_PROXY : Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY, _proxyHelper.ManualSettingsOnly ? _proxyHelper.Proxy : Interop.WinHttp.WINHTTP_NO_PROXY_NAME, _proxyHelper.ManualSettingsOnly ? _proxyHelper.ProxyBypass : Interop.WinHttp.WINHTTP_NO_PROXY_BYPASS, (int)Interop.WinHttp.WINHTTP_FLAG_ASYNC); ThrowOnInvalidHandle(sessionHandle); } uint optionAssuredNonBlockingTrue = 1; // TRUE if (!Interop.WinHttp.WinHttpSetOption( sessionHandle, Interop.WinHttp.WINHTTP_OPTION_ASSURED_NON_BLOCKING_CALLBACKS, ref optionAssuredNonBlockingTrue, (uint)Marshal.SizeOf<uint>())) { // This option is not available on downlevel Windows versions. While it improves // performance, we can ignore the error that the option is not available. int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_WINHTTP_INVALID_OPTION) { throw WinHttpException.CreateExceptionUsingError(lastError); } } SetSessionHandleOptions(sessionHandle); _sessionHandle = sessionHandle; } } } }
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; // 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; } }
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 Task<bool> InternalSendRequestAsync(WinHttpRequestState state) { state.TcsSendRequest = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); lock (state.Lock) { if (!Interop.WinHttp.WinHttpSendRequest( state.RequestHandle, null, 0, IntPtr.Zero, 0, 0, state.ToIntPtr())) { WinHttpException.ThrowExceptionUsingLastError(); } } return state.TcsSendRequest.Task; }