private void ThrowOnInvalidHandle(SafeWinHttpHandle handle) { if (handle.IsInvalid) { WinHttpException.ThrowExceptionUsingLastError(); } }
private void SetWinHttpCredential( SafeWinHttpHandle requestHandle, ICredentials credentials, Uri uri, uint authScheme, uint authTarget) { string userName; string password; Debug.Assert(credentials != null); Debug.Assert(authScheme != 0); Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY || authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER); NetworkCredential networkCredential = credentials.GetCredential(uri, s_authSchemeStringMapping[authScheme]); if (networkCredential == null) { return; } if (networkCredential == CredentialCache.DefaultNetworkCredentials) { // Allow WinHTTP to transmit the default credentials. ChangeDefaultCredentialsPolicy(requestHandle, authTarget, allowDefaultCredentials:true); userName = null; password = null; } else { userName = networkCredential.UserName; password = networkCredential.Password; string domain = networkCredential.Domain; // WinHTTP does not support a blank username. So, we will throw an exception. if (string.IsNullOrEmpty(userName)) { throw new InvalidOperationException(SR.net_http_username_empty_string); } if (!string.IsNullOrEmpty(domain)) { userName = domain + "\\" + userName; } } if (!Interop.WinHttp.WinHttpSetCredentials( requestHandle, authTarget, authScheme, userName, password, IntPtr.Zero)) { WinHttpException.ThrowExceptionUsingLastError(); } }
private void SetWinHttpOption(SafeWinHttpHandle handle, uint option, ref uint optionData) { if (!Interop.WinHttp.WinHttpSetOption( handle, option, ref optionData)) { WinHttpException.ThrowExceptionUsingLastError(); } }
private void SetWinHttpOption(SafeWinHttpHandle handle, uint option, string optionData) { if (!Interop.WinHttp.WinHttpSetOption( handle, option, optionData, (uint)optionData.Length)) { WinHttpException.ThrowExceptionUsingLastError(); } }
private void SetSessionHandleTimeoutOptions() { if (!Interop.WinHttp.WinHttpSetTimeouts( _sessionHandle, 0, (int)_connectTimeout.TotalMilliseconds, (int)_sendTimeout.TotalMilliseconds, (int)_receiveHeadersTimeout.TotalMilliseconds)) { WinHttpException.ThrowExceptionUsingLastError(); } }
private static void SetWinHttpOption( SafeWinHttpHandle handle, uint option, IntPtr optionData, uint optionSize) { if (!Interop.WinHttp.WinHttpSetOption( handle, option, optionData, optionSize)) { WinHttpException.ThrowExceptionUsingLastError(); } }
private static string GetResponseHeaderStringHelper( SafeWinHttpHandle requestHandle, uint infoLevel, ref uint index) { uint bytesNeeded = 0; bool results = false; // Call WinHttpQueryHeaders once to obtain the size of the buffer needed. The size is returned in // bytes but the API actually returns Unicode characters. if (!Interop.WinHttp.WinHttpQueryHeaders( requestHandle, infoLevel, Interop.WinHttp.WINHTTP_HEADER_NAME_BY_INDEX, null, ref bytesNeeded, ref index)) { int lastError = Marshal.GetLastWin32Error(); if (lastError == Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND) { return(null); } if (lastError != Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER) { throw WinHttpException.CreateExceptionUsingError(lastError); } } // Allocate space for the buffer. int charsNeeded = (int)bytesNeeded / sizeof(char); var buffer = new StringBuilder(charsNeeded, charsNeeded); results = Interop.WinHttp.WinHttpQueryHeaders( requestHandle, infoLevel, Interop.WinHttp.WINHTTP_HEADER_NAME_BY_INDEX, buffer, ref bytesNeeded, ref index); if (!results) { WinHttpException.ThrowExceptionUsingLastError(); } return(buffer.ToString()); }
private static void AddRequestHeaders( SafeWinHttpHandle requestHandle, HttpRequestMessage requestMessage, CookieContainer cookies) { var requestHeadersBuffer = new StringBuilder(); // Manually add cookies. if (cookies != null) { string cookieHeader = 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(); } }
/// <summary> /// Returns the first header or throws if the header isn't found. /// </summary> public static uint GetResponseHeaderNumberInfo(SafeWinHttpHandle requestHandle, uint infoLevel) { uint result = 0; uint resultSize = sizeof(uint); if (!Interop.WinHttp.WinHttpQueryHeaders( requestHandle, infoLevel | Interop.WinHttp.WINHTTP_QUERY_FLAG_NUMBER, Interop.WinHttp.WINHTTP_HEADER_NAME_BY_INDEX, ref result, ref resultSize, IntPtr.Zero)) { WinHttpException.ThrowExceptionUsingLastError(); } return(result); }
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 void ChangeDefaultCredentialsPolicy( SafeWinHttpHandle requestHandle, uint authTarget, bool allowDefaultCredentials) { Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY || authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER); uint optionData = allowDefaultCredentials ? (authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY ? Interop.WinHttp.WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM : Interop.WinHttp.WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW) : Interop.WinHttp.WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH; if (!Interop.WinHttp.WinHttpSetOption( requestHandle, Interop.WinHttp.WINHTTP_OPTION_AUTOLOGON_POLICY, ref optionData)) { WinHttpException.ThrowExceptionUsingLastError(); } }
public void GetProxyForUrl(SafeWinHttpHandle sessionHandle, Uri uri, out Interop.WinHttp.WINHTTP_PROXY_INFO proxyInfo) { Interop.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions; autoProxyOptions.AutoConfigUrl = AutoConfigUrl; autoProxyOptions.AutoDetectFlags = AutoDetect ? (Interop.WinHttp.WINHTTP_AUTO_DETECT_TYPE_DHCP | Interop.WinHttp.WINHTTP_AUTO_DETECT_TYPE_DNS_A) : 0; autoProxyOptions.AutoLoginIfChallenged = false; autoProxyOptions.Flags = (AutoDetect ? Interop.WinHttp.WINHTTP_AUTOPROXY_AUTO_DETECT : 0) | (!string.IsNullOrEmpty(AutoConfigUrl) ? Interop.WinHttp.WINHTTP_AUTOPROXY_CONFIG_URL : 0); autoProxyOptions.Reserved1 = IntPtr.Zero; autoProxyOptions.Reserved2 = 0; // AutoProxy Cache. // http://msdn.microsoft.com/en-us/library/windows/desktop/aa383153(v=vs.85).aspx // If the out-of-process service is active when WinHttpGetProxyForUrl is called, the cached autoproxy // URL and script are available to the whole computer. However, if the out-of-process service is used, // and the fAutoLogonIfChallenged flag in the pAutoProxyOptions structure is true, then the autoproxy // URL and script are not cached. Therefore, calling WinHttpGetProxyForUrl with the fAutoLogonIfChallenged // member set to TRUE results in additional overhead operations that may affect performance. // The following steps can be used to improve performance: // 1. Call WinHttpGetProxyForUrl with the fAutoLogonIfChallenged parameter set to false. The autoproxy // URL and script are cached for future calls to WinHttpGetProxyForUrl. // 2. If Step 1 fails, with ERROR_WINHTTP_LOGIN_FAILURE, then call WinHttpGetProxyForUrl with the // fAutoLogonIfChallenged member set to TRUE. var repeat = false; do { if (Interop.WinHttp.WinHttpGetProxyForUrl(sessionHandle, uri.AbsoluteUri, ref autoProxyOptions, out proxyInfo)) { repeat = false; } else { var lastError = Marshal.GetLastWin32Error(); if (lastError == Interop.WinHttp.ERROR_WINHTTP_AUTODETECTION_FAILED) { // Fall back to manual settings if available. if (!string.IsNullOrEmpty(Proxy)) { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NAMED_PROXY; proxyInfo.Proxy = Marshal.StringToHGlobalUni(Proxy); proxyInfo.ProxyBypass = string.IsNullOrEmpty(ProxyBypass) ? IntPtr.Zero : Marshal.StringToHGlobalUni(ProxyBypass); } else { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY; proxyInfo.Proxy = IntPtr.Zero; proxyInfo.ProxyBypass = IntPtr.Zero; } repeat = false; } else if (lastError == Interop.WinHttp.ERROR_WINHTTP_LOGIN_FAILURE) { if (repeat) { throw WinHttpException.CreateExceptionUsingError(lastError); } repeat = true; autoProxyOptions.AutoLoginIfChallenged = true; } else { WinHttpException.ThrowExceptionUsingLastError(); } } } while (repeat); }
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 bool SetWinHttpCredential( SafeWinHttpHandle requestHandle, ICredentials credentials, Uri uri, uint authScheme, uint authTarget) { string?userName; string?password; Debug.Assert(credentials != null); Debug.Assert(authScheme != 0); Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY || authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER); string?authType = s_authSchemeStringMapping[authScheme]; Debug.Assert(!string.IsNullOrEmpty(authType)); NetworkCredential?networkCredential = credentials.GetCredential(uri, authType); if (networkCredential == null) { return(false); } if (networkCredential == CredentialCache.DefaultNetworkCredentials) { // Only Negotiate and NTLM can use default credentials. Otherwise, // behave as-if there were no credentials. if (authScheme == Interop.WinHttp.WINHTTP_AUTH_SCHEME_NEGOTIATE || authScheme == Interop.WinHttp.WINHTTP_AUTH_SCHEME_NTLM) { // Allow WinHTTP to transmit the default credentials. ChangeDefaultCredentialsPolicy(requestHandle, authTarget, allowDefaultCredentials: true); userName = null; password = null; } else { return(false); } } else { userName = networkCredential.UserName; password = networkCredential.Password; string domain = networkCredential.Domain; // WinHTTP does not support a blank username. So, we will throw an exception. if (string.IsNullOrEmpty(userName)) { throw new InvalidOperationException(SR.net_http_username_empty_string); } if (!string.IsNullOrEmpty(domain)) { userName = domain + "\\" + userName; } } if (!Interop.WinHttp.WinHttpSetCredentials( requestHandle, authTarget, authScheme, userName, password, IntPtr.Zero)) { WinHttpException.ThrowExceptionUsingLastError(nameof(Interop.WinHttp.WinHttpSetCredentials)); } return(true); }
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 void EnsureSessionHandleExists(WinHttpRequestState state) { if (_sessionHandle == null) { lock (_lockObject) { if (_sessionHandle == null) { 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; } _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(); 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>())) { WinHttpException.ThrowExceptionUsingLastError(); } } } } }