internal WinHttpRequestStream(SafeWinHttpHandle requestHandle, bool chunkedMode) { bool ignore = false; requestHandle.DangerousAddRef(ref ignore); _requestHandle = requestHandle; _chunkedMode = chunkedMode; }
public static SafeWinHttpHandle WinHttpConnect( SafeWinHttpHandle sessionHandle, string serverName, ushort serverPort, uint reserved) { return new FakeSafeWinHttpHandle(true); }
internal void SetChannelBinding(SafeWinHttpHandle requestHandle) { var channelBinding = new WinHttpChannelBinding(requestHandle); if (channelBinding.IsInvalid) { channelBinding.Dispose(); } else { _channelBinding = channelBinding; } }
internal WinHttpResponseStream( SafeWinHttpHandle sessionHandle, SafeWinHttpHandle connectHandle, SafeWinHttpHandle requestHandle) { // While we only use the requestHandle to do actual reads of the response body, // we need to keep the parent handles (connection, session) alive as well. bool ignore = false; sessionHandle.DangerousAddRef(ref ignore); connectHandle.DangerousAddRef(ref ignore); requestHandle.DangerousAddRef(ref ignore); _sessionHandle = sessionHandle; _connectHandle = connectHandle; _requestHandle = requestHandle; }
internal WinHttpChannelBinding(SafeWinHttpHandle requestHandle) { IntPtr data = IntPtr.Zero; uint dataSize = 0; if (!Interop.WinHttp.WinHttpQueryOption(requestHandle, Interop.WinHttp.WINHTTP_OPTION_SERVER_CBT, null, ref dataSize)) { if (Marshal.GetLastWin32Error() == Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER) { data = Marshal.AllocHGlobal((int)dataSize); if (Interop.WinHttp.WinHttpQueryOption(requestHandle, Interop.WinHttp.WINHTTP_OPTION_SERVER_CBT, data, ref dataSize)) { SetHandle(data); _size = (int)dataSize; } else { Marshal.FreeHGlobal(data); } } } }
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; // Create a single buffer to use for all subsequent WinHttpQueryHeaders string interop calls. // This buffer is the length needed for WINHTTP_QUERY_RAW_HEADERS_CRLF, which includes the status line // and all headers separated by CRLF, so it should be large enough for any individual status line or header queries. int bufferLength = GetResponseHeaderCharBufferLength(requestHandle, Interop.WinHttp.WINHTTP_QUERY_RAW_HEADERS_CRLF); char[] buffer = new char[bufferLength]; // Get HTTP version, status code, reason phrase from the response headers. if (IsResponseHttp2(requestHandle)) { response.Version = WinHttpHandler.HttpVersion20; } else { int versionLength = GetResponseHeader(requestHandle, Interop.WinHttp.WINHTTP_QUERY_VERSION, buffer); response.Version = CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase("HTTP/1.1", buffer, 0, versionLength) ? HttpVersion.Version11 : CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase("HTTP/1.0", buffer, 0, versionLength) ? HttpVersion.Version10 : WinHttpHandler.HttpVersionUnknown; } response.StatusCode = (HttpStatusCode)GetResponseHeaderNumberInfo( requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE); int reasonPhraseLength = GetResponseHeader(requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_TEXT, buffer); response.ReasonPhrase = reasonPhraseLength > 0 ? GetReasonPhrase(response.StatusCode, buffer, reasonPhraseLength) : string.Empty; // Create response stream and wrap it in a StreamContent object. var responseStream = new WinHttpResponseStream(requestHandle, state); state.RequestHandle = null; // ownership successfully transfered to WinHttpResponseStram. Stream decompressedStream = responseStream; if (doManualDecompressionCheck) { int contentEncodingStartIndex = 0; int contentEncodingLength = GetResponseHeader( requestHandle, Interop.WinHttp.WINHTTP_QUERY_CONTENT_ENCODING, buffer); CharArrayHelpers.Trim(buffer, ref contentEncodingStartIndex, ref contentEncodingLength); if (contentEncodingLength > 0) { if (CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase( EncodingNameGzip, buffer, contentEncodingStartIndex, contentEncodingLength)) { decompressedStream = new GZipStream(responseStream, CompressionMode.Decompress); stripEncodingHeaders = true; } else if (CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase( EncodingNameDeflate, buffer, contentEncodingStartIndex, contentEncodingLength)) { decompressedStream = new DeflateStream(responseStream, CompressionMode.Decompress); stripEncodingHeaders = true; } } } #if HTTP_DLL var content = new StreamContent(decompressedStream, state.CancellationToken); #else // TODO: Issue https://github.com/dotnet/corefx/issues/9071 // We'd like to be able to pass state.CancellationToken into the StreamContent so that its // SerializeToStreamAsync method can use it, but that ctor isn't public, nor is there a // SerializeToStreamAsync override that takes a CancellationToken. var content = new StreamContent(decompressedStream); #endif response.Content = content; response.RequestMessage = request; // Parse raw response headers and place them into response message. ParseResponseHeaders(requestHandle, response, buffer, stripEncodingHeaders); if (response.RequestMessage.Method != HttpMethod.Head) { state.ExpectedBytesToRead = response.Content.Headers.ContentLength; } return(response); }
private 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 void SetRequestHandleCookieOptions(SafeWinHttpHandle requestHandle) { if (_cookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer || _cookieUsePolicy == CookieUsePolicy.IgnoreCookies) { uint optionData = Interop.WinHttp.WINHTTP_DISABLE_COOKIES; SetWinHttpOption(requestHandle, Interop.WinHttp.WINHTTP_OPTION_DISABLE_FEATURE, ref optionData); } }
private void SetRequestHandleClientCertificateOptions(SafeWinHttpHandle requestHandle, Uri requestUri) { // Must be HTTPS scheme to use client certificates. if (requestUri.Scheme != UriSchemeHttps) { return; } // Get candidate list for client certificates. X509Certificate2Collection certs; if (_clientCertificateOption == ClientCertificateOption.Manual) { certs = ClientCertificates; } else { using (var myStore = new X509Store()) { myStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly); certs = myStore.Certificates; } } // Check for no certs now as a performance optimization. if (certs.Count == 0) { SetNoClientCertificate(requestHandle); return; } // Reduce the set of certificates to match the proper 'Client Authentication' criteria. certs = certs.Find(X509FindType.FindByKeyUsage, X509KeyUsageFlags.DigitalSignature, true); certs = certs.Find(X509FindType.FindByApplicationPolicy, ClientAuthenticationOID, true); // Build a new collection with certs that have a private key. Need to do this // manually because there is no X509FindType to match this criteria. var clientCerts = new X509Certificate2Collection(); foreach (var cert in certs) { if (cert.HasPrivateKey) { clientCerts.Add(cert); } } // TOOD: Filter the list based on TrustedIssuerList info from WINHTTP. // Set the client certificate. if (certs.Count == 0) { SetNoClientCertificate(requestHandle); } else { SetWinHttpOption( requestHandle, Interop.WinHttp.WINHTTP_OPTION_CLIENT_CERT_CONTEXT, clientCerts[0].Handle, (uint)Marshal.SizeOf<Interop.Crypt32.CERT_CONTEXT>()); } }
private void ProcessResponse( RequestState state, SafeWinHttpHandle requestHandle, ref uint proxyAuthScheme, ref uint serverAuthScheme, out bool retryRequest) { retryRequest = false; // Check the status code and retry the request applying credentials if needed. var statusCode = (HttpStatusCode)GetResponseHeaderNumberInfo( requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE); uint supportedSchemes = 0; uint firstSchemeIgnored = 0; uint authTarget = 0; Uri uri = state.RequestMessage.RequestUri; 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( requestHandle, out supportedSchemes, out firstSchemeIgnored, out authTarget)) { WinHttpException.ThrowExceptionUsingLastError(); } // Verify the authTarget is for server authentication only. if (authTarget != Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER) { // TODO: Protocol violation. Add detailed error message. throw new InvalidOperationException(); } serverAuthScheme = ChooseAuthScheme(supportedSchemes); if (serverAuthScheme != 0) { SetWinHttpCredential( requestHandle, state.ServerCredentials, uri, serverAuthScheme, authTarget); 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( requestHandle, out supportedSchemes, out firstSchemeIgnored, out authTarget)) { WinHttpException.ThrowExceptionUsingLastError(); } // Verify the authTarget is for proxy authentication only. if (authTarget != Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY) { // TODO: Protocol violation. Add detailed error message. throw new InvalidOperationException(); } proxyAuthScheme = ChooseAuthScheme(supportedSchemes); retryRequest = true; break; default: if (_preAuthenticate && serverAuthScheme != 0) { SaveServerCredentialsToCache(uri, serverAuthScheme, state.ServerCredentials); } break; } }
private void SetRequestHandleDecompressionOptions(SafeWinHttpHandle requestHandle) { uint optionData = 0; if (_automaticDecompression != DecompressionMethods.None) { if ((_automaticDecompression & DecompressionMethods.GZip) != 0) { optionData |= Interop.WinHttp.WINHTTP_DECOMPRESSION_FLAG_GZIP; } if ((_automaticDecompression & DecompressionMethods.Deflate) != 0) { optionData |= Interop.WinHttp.WINHTTP_DECOMPRESSION_FLAG_DEFLATE; } try { SetWinHttpOption(requestHandle, Interop.WinHttp.WINHTTP_OPTION_DECOMPRESSION, ref optionData); } catch (WinHttpException ex) { if (ex.NativeErrorCode != (int)Interop.WinHttp.ERROR_WINHTTP_INVALID_OPTION) { throw; } // We are running on a platform earlier than Win8.1 for which WINHTTP.DLL // doesn't support this option. So, we'll have to do the decompression // manually. _doManualDecompressionCheck = true; } } }
public bool GetProxyForUrl( SafeWinHttpHandle sessionHandle, Uri uri, out Interop.WinHttp.WINHTTP_PROXY_INFO proxyInfo) { proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY; proxyInfo.Proxy = IntPtr.Zero; proxyInfo.ProxyBypass = IntPtr.Zero; if (!_useProxy) { return(false); } bool useProxy = false; 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. // https://docs.microsoft.com/en-us/windows/desktop/WinHttp/autoproxy-cache // 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. // // We match behavior of WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY and ignore errors. var repeat = false; do { _autoDetectionFailed = false; if (Interop.WinHttp.WinHttpGetProxyForUrl( sessionHandle, uri.AbsoluteUri, ref autoProxyOptions, out proxyInfo)) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, "Using autoconfig proxy settings"); } useProxy = true; break; } else { var lastError = Marshal.GetLastWin32Error(); if (NetEventSource.IsEnabled) { NetEventSource.Error(this, $"error={lastError}"); } if (lastError == Interop.WinHttp.ERROR_WINHTTP_LOGIN_FAILURE) { if (repeat) { // We don't retry more than once. break; } else { repeat = true; autoProxyOptions.AutoLoginIfChallenged = true; } } else { if (lastError == Interop.WinHttp.ERROR_WINHTTP_AUTODETECTION_FAILED) { _autoDetectionFailed = true; _lastTimeAutoDetectionFailed = Environment.TickCount; } break; } } } while (repeat); // Fall back to manual settings if available. if (!useProxy && !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); if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"Fallback to Proxy={Proxy}, ProxyBypass={ProxyBypass}"); } useProxy = true; } if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"useProxy={useProxy}"); } return(useProxy); }
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>())) { // 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); } } } } } }
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 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; // Create a single buffer to use for all subsequent WinHttpQueryHeaders string interop calls. // This buffer is the length needed for WINHTTP_QUERY_RAW_HEADERS_CRLF, which includes the status line // and all headers separated by CRLF, so it should be large enough for any individual status line or header queries. int bufferLength = GetResponseHeaderCharBufferLength(requestHandle, Interop.WinHttp.WINHTTP_QUERY_RAW_HEADERS_CRLF); char[] buffer = new char[bufferLength]; // Get HTTP version, status code, reason phrase from the response headers. int versionLength = GetResponseHeader(requestHandle, Interop.WinHttp.WINHTTP_QUERY_VERSION, buffer); response.Version = CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase("HTTP/1.1", buffer, 0, versionLength) ? HttpVersion.Version11 : CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase("HTTP/1.0", buffer, 0, versionLength) ? HttpVersion.Version10 : HttpVersion.Unknown; response.StatusCode = (HttpStatusCode)GetResponseHeaderNumberInfo( requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE); int reasonPhraseLength = GetResponseHeader(requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_TEXT, buffer); if (reasonPhraseLength > 0) { response.ReasonPhrase = GetReasonPhrase(response.StatusCode, buffer, reasonPhraseLength); } // Create response stream and wrap it in a StreamContent object. var responseStream = new WinHttpResponseStream(requestHandle, state); Stream decompressedStream = responseStream; if (doManualDecompressionCheck) { int contentEncodingStartIndex = 0; int contentEncodingLength = GetResponseHeader( requestHandle, Interop.WinHttp.WINHTTP_QUERY_CONTENT_ENCODING, buffer); CharArrayHelpers.Trim(buffer, ref contentEncodingStartIndex, ref contentEncodingLength); if (contentEncodingLength > 0) { if (CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase( EncodingNameGzip, buffer, contentEncodingStartIndex, contentEncodingLength)) { decompressedStream = new GZipStream(responseStream, CompressionMode.Decompress); stripEncodingHeaders = true; } else if (CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase( EncodingNameDeflate, buffer, contentEncodingStartIndex, contentEncodingLength)) { decompressedStream = new DeflateStream(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, buffer, stripEncodingHeaders); if (response.RequestMessage.Method != HttpMethod.Head) { state.ExpectedBytesToRead = response.Content.Headers.ContentLength; } return(response); }
public static bool WinHttpQueryDataAvailable(SafeWinHttpHandle requestHandle, out uint bytesAvailable) { bytesAvailable = 0; return(true); }
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); return(response); }
// Returns the first header or null if header isn't found. public static string GetResponseHeaderStringInfo(SafeWinHttpHandle requestHandle, uint infoLevel) { uint index = 0; return(GetResponseHeaderStringHelper(requestHandle, infoLevel, ref index)); }
private void ParseResponseHeaders( SafeWinHttpHandle requestHandle, HttpResponseMessage response, bool stripEncodingHeaders) { string rawResponseHeaders = GetResponseHeaderStringInfo( requestHandle, Interop.WinHttp.WINHTTP_QUERY_RAW_HEADERS_CRLF); string[] responseHeaderArray = rawResponseHeaders.Split( s_httpHeadersSeparator, StringSplitOptions.RemoveEmptyEntries); // Parse the array of headers and split them between Content headers and Response headers. // Skip the first line which contains status code, etc. information that we already parsed. for (int i = 1; i < responseHeaderArray.Length; i++) { int colonIndex = responseHeaderArray[i].IndexOf(':'); // Skip malformed header lines that are missing the colon character. if (colonIndex > 0) { string headerName = responseHeaderArray[i].Substring(0, colonIndex); string headerValue = responseHeaderArray[i].Substring(colonIndex + 1).Trim(); // Normalize header value by trimming white space. if (!response.Headers.TryAddWithoutValidation(headerName, headerValue)) { if (stripEncodingHeaders) { // Remove Content-Length and Content-Encoding headers if we are // decompressing the response stream in the handler (due to // WINHTTP not supporting it in a particular downlevel platform). // This matches the behavior of WINHTTP when it does decompression iself. if (string.Equals( HeaderNameContentLength, headerName, StringComparison.OrdinalIgnoreCase)) { continue; } if (string.Equals( HeaderNameContentEncoding, headerName, StringComparison.OrdinalIgnoreCase)) { continue; } } // TODO: Should we log if there is an error here? response.Content.Headers.TryAddWithoutValidation(headerName, headerValue); } } } }
internal WinHttpResponseStream(SafeWinHttpHandle requestHandle, WinHttpRequestState state, HttpResponseMessage responseMessage) { _state = state; _responseMessage = responseMessage; _requestHandle = requestHandle; }
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(); } }
private HttpSystemProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle sessionHandle) { _proxyHelper = proxyHelper; _sessionHandle = sessionHandle; if (proxyHelper.ManualSettingsOnly) { if (NetEventSource.IsEnabled) { NetEventSource.Info(proxyHelper, $"ManualSettingsUsed, {proxyHelper.Proxy}"); } ParseProxyConfig(proxyHelper.Proxy, out _insecureProxyUri, out _secureProxyUri); if (_insecureProxyUri == null && _secureProxyUri == null) { // If advanced parsing by protocol fails, fall-back to simplified parsing. _insecureProxyUri = _secureProxyUri = GetUriFromString(proxyHelper.Proxy); } if (!string.IsNullOrWhiteSpace(proxyHelper.ProxyBypass)) { int idx = 0; int start = 0; string tmp; // Process bypass list for manual setting. // Initial list size is best guess based on string length assuming each entry is at least 5 characters on average. _bypass = new List <Regex>(proxyHelper.ProxyBypass.Length / 5); while (idx < proxyHelper.ProxyBypass.Length) { // Strip leading spaces and scheme if any. while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == ' ') { idx += 1; } ; if (string.Compare(proxyHelper.ProxyBypass, idx, "http://", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) { idx += 7; } else if (string.Compare(proxyHelper.ProxyBypass, idx, "https://", 0, 8, StringComparison.OrdinalIgnoreCase) == 0) { idx += 8; } if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == '[') { // Strip [] from IPv6 so we can use IdnHost laster for matching. idx += 1; } start = idx; while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ' ' && proxyHelper.ProxyBypass[idx] != ';' && proxyHelper.ProxyBypass[idx] != ']') { idx += 1; } ; if (idx == start) { // Empty string. tmp = null; } else if (string.Compare(proxyHelper.ProxyBypass, start, "<local>", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) { _bypassLocal = true; tmp = null; } else { tmp = proxyHelper.ProxyBypass.Substring(start, idx - start); } // Skip trailing characters if any. if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ';') { // Got stopped at space or ']'. Strip until next ';' or end. while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ';') { idx += 1; } ; } if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == ';') { idx++; } if (tmp == null) { continue; } try { // Escape any special characters and unescape * to get wildcard pattern match. Regex re = new Regex(Regex.Escape(tmp).Replace("\\*", ".*?") + "$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); _bypass.Add(re); } catch (Exception ex) { if (NetEventSource.IsEnabled) { NetEventSource.Error(this, $"Failed to process {tmp} from bypass list: {ex}"); } } } if (_bypass.Count == 0) { // Bypass string only had garbage we did not parse. _bypass = null; } } if (_bypassLocal) { _localIp = new List <IPAddress>(); foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) { IPInterfaceProperties ipProps = netInterface.GetIPProperties(); foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses) { _localIp.Add(addr.Address); } } } } }
private async void StartRequest(object obj) { WinHttpRequestState state = (WinHttpRequestState)obj; bool secureConnection = false; HttpResponseMessage responseMessage = null; Exception savedException = null; SafeWinHttpHandle connectHandle = null; if (state.CancellationToken.IsCancellationRequested) { state.Tcs.TrySetCanceled(state.CancellationToken); return; } try { EnsureSessionHandleExists(state); SetSessionHandleOptions(); // Specify an HTTP server. connectHandle = Interop.WinHttp.WinHttpConnect( _sessionHandle, state.RequestMessage.RequestUri.Host, (ushort)state.RequestMessage.RequestUri.Port, 0); ThrowOnInvalidHandle(connectHandle); connectHandle.SetParentHandle(_sessionHandle); if (state.RequestMessage.RequestUri.Scheme == UriScheme.Https) { secureConnection = true; } else { secureConnection = false; } // Try to use the requested version if a known/supported version was explicitly requested. // Otherwise, we simply use winhttp's default. string httpVersion = null; if (state.RequestMessage.Version == HttpVersion.Version10) { httpVersion = "HTTP/1.0"; } else if (state.RequestMessage.Version == HttpVersion.Version11) { httpVersion = "HTTP/1.1"; } // Create an HTTP request handle. state.RequestHandle = Interop.WinHttp.WinHttpOpenRequest( connectHandle, state.RequestMessage.Method.Method, state.RequestMessage.RequestUri.PathAndQuery, httpVersion, Interop.WinHttp.WINHTTP_NO_REFERER, Interop.WinHttp.WINHTTP_DEFAULT_ACCEPT_TYPES, secureConnection ? Interop.WinHttp.WINHTTP_FLAG_SECURE : 0); ThrowOnInvalidHandle(state.RequestHandle); state.RequestHandle.SetParentHandle(connectHandle); // Set callback function. SetStatusCallback(state.RequestHandle, WinHttpRequestCallback.StaticCallbackDelegate); // Set needed options on the request handle. SetRequestHandleOptions(state); bool chunkedModeForSend = IsChunkedModeForSend(state.RequestMessage); AddRequestHeaders( state.RequestHandle, state.RequestMessage, _cookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer ? _cookieContainer : null); uint proxyAuthScheme = 0; uint serverAuthScheme = 0; state.RetryRequest = false; // The only way to abort pending async operations in WinHTTP is to close the WinHTTP handle. // We will detect a cancellation request on the cancellation token by registering a callback. // If the callback is invoked, then we begin the abort process by disposing the handle. This // will have the side-effect of WinHTTP cancelling any pending I/O and accelerating its callbacks // on the handle and thus releasing the awaiting tasks in the loop below. This helps to provide // a more timely, cooperative, cancellation pattern. using (state.CancellationToken.Register(s => ((WinHttpRequestState)s).RequestHandle.Dispose(), state)) { do { _authHelper.PreAuthenticateRequest(state, proxyAuthScheme); await InternalSendRequestAsync(state).ConfigureAwait(false); if (state.RequestMessage.Content != null) { await InternalSendRequestBodyAsync(state, chunkedModeForSend).ConfigureAwait(false); } bool receivedResponse = await InternalReceiveResponseHeadersAsync(state).ConfigureAwait(false); if (receivedResponse) { // If we're manually handling cookies, we need to add them to the container after // each response has been received. if (state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer) { WinHttpCookieContainerAdapter.AddResponseCookiesToContainer(state); } _authHelper.CheckResponseForAuthentication( state, ref proxyAuthScheme, ref serverAuthScheme); } } while (state.RetryRequest); } state.CancellationToken.ThrowIfCancellationRequested(); responseMessage = WinHttpResponseParser.CreateResponseMessage(state, _doManualDecompressionCheck); // Since the headers have been read, set the "receive" timeout to be based on each read // call of the response body data. WINHTTP_OPTION_RECEIVE_TIMEOUT sets a timeout on each // lower layer winsock read. uint optionData = (uint)_receiveDataTimeout.TotalMilliseconds; SetWinHttpOption(state.RequestHandle, Interop.WinHttp.WINHTTP_OPTION_RECEIVE_TIMEOUT, ref optionData); } catch (Exception ex) { if (state.SavedException != null) { savedException = state.SavedException; } else { savedException = ex; } } finally { SafeWinHttpHandle.DisposeAndClearHandle(ref connectHandle); } // Move the main task to a terminal state. This releases any callers of SendAsync() that are awaiting. if (responseMessage != null) { state.Tcs.TrySetResult(responseMessage); } else { HandleAsyncException(state, savedException); } }
public static HttpResponseMessage CreateResponseMessage( WinHttpRequestState state, DecompressionMethods manuallyProcessedDecompressionMethods) { HttpRequestMessage request = state.RequestMessage; SafeWinHttpHandle requestHandle = state.RequestHandle; var response = new HttpResponseMessage(); bool stripEncodingHeaders = false; // Create a single buffer to use for all subsequent WinHttpQueryHeaders string interop calls. // This buffer is the length needed for WINHTTP_QUERY_RAW_HEADERS_CRLF, which includes the status line // and all headers separated by CRLF, so it should be large enough for any individual status line or header queries. int bufferLength = GetResponseHeaderCharBufferLength(requestHandle, Interop.WinHttp.WINHTTP_QUERY_RAW_HEADERS_CRLF); char[] buffer = ArrayPool <char> .Shared.Rent(bufferLength); try { // Get HTTP version, status code, reason phrase from the response headers. if (IsResponseHttp2(requestHandle)) { response.Version = WinHttpHandler.HttpVersion20; } else { int versionLength = GetResponseHeader(requestHandle, Interop.WinHttp.WINHTTP_QUERY_VERSION, buffer); response.Version = CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase("HTTP/1.1", buffer, 0, versionLength) ? HttpVersion.Version11 : CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase("HTTP/1.0", buffer, 0, versionLength) ? HttpVersion.Version10 : WinHttpHandler.HttpVersionUnknown; } response.StatusCode = (HttpStatusCode)GetResponseHeaderNumberInfo( requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE); int reasonPhraseLength = GetResponseHeader(requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_TEXT, buffer); response.ReasonPhrase = reasonPhraseLength > 0 ? GetReasonPhrase(response.StatusCode, buffer, reasonPhraseLength) : string.Empty; // Create response stream and wrap it in a StreamContent object. var responseStream = new WinHttpResponseStream(requestHandle, state); state.RequestHandle = null; // ownership successfully transfered to WinHttpResponseStram. Stream decompressedStream = responseStream; if (manuallyProcessedDecompressionMethods != DecompressionMethods.None) { int contentEncodingStartIndex = 0; int contentEncodingLength = GetResponseHeader( requestHandle, Interop.WinHttp.WINHTTP_QUERY_CONTENT_ENCODING, buffer); CharArrayHelpers.Trim(buffer, ref contentEncodingStartIndex, ref contentEncodingLength); if (contentEncodingLength > 0) { if ((manuallyProcessedDecompressionMethods & DecompressionMethods.GZip) == DecompressionMethods.GZip && CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase(EncodingNameGzip, buffer, contentEncodingStartIndex, contentEncodingLength)) { decompressedStream = new GZipStream(responseStream, CompressionMode.Decompress); stripEncodingHeaders = true; } else if ((manuallyProcessedDecompressionMethods & DecompressionMethods.Deflate) == DecompressionMethods.Deflate && CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase(EncodingNameDeflate, buffer, contentEncodingStartIndex, contentEncodingLength)) { decompressedStream = new DeflateStream(responseStream, CompressionMode.Decompress); stripEncodingHeaders = true; } } } response.Content = new NoWriteNoSeekStreamContent(decompressedStream); response.RequestMessage = request; // Parse raw response headers and place them into response message. ParseResponseHeaders(requestHandle, response, buffer, stripEncodingHeaders); if (response.RequestMessage.Method != HttpMethod.Head) { state.ExpectedBytesToRead = response.Content.Headers.ContentLength; } return(response); } finally { ArrayPool <char> .Shared.Return(buffer); } }
private void PreAuthenticateRequest( RequestState state, SafeWinHttpHandle requestHandle, 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( requestHandle, state.Proxy == null ? _defaultProxyCredentials : state.Proxy.Credentials, state.RequestMessage.RequestUri, proxyAuthScheme, Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY); } // Apply pre-authentication headers for server authentication? if (_preAuthenticate) { uint authScheme; NetworkCredential serverCredentials; if (GetServerCredentialsFromCache( state.RequestMessage.RequestUri, out authScheme, out serverCredentials)) { SetWinHttpCredential( requestHandle, serverCredentials, state.RequestMessage.RequestUri, authScheme, Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER); state.LastStatusCode = HttpStatusCode.Unauthorized; // Remember we already set the creds. } } }
private void SetRequestHandleBufferingOptions(SafeWinHttpHandle requestHandle) { uint optionData = (uint)_maxResponseHeadersLength; SetWinHttpOption(requestHandle, Interop.WinHttp.WINHTTP_OPTION_MAX_RESPONSE_HEADER_SIZE, ref optionData); optionData = (uint)_maxResponseDrainSize; SetWinHttpOption(requestHandle, Interop.WinHttp.WINHTTP_OPTION_MAX_RESPONSE_DRAIN_SIZE, ref optionData); }
private void SetRequestHandleRedirectionOptions(SafeWinHttpHandle requestHandle) { uint optionData = 0; if (_automaticRedirection) { optionData = (uint)_maxAutomaticRedirections; SetWinHttpOption( requestHandle, Interop.WinHttp.WINHTTP_OPTION_MAX_HTTP_AUTOMATIC_REDIRECTS, ref optionData); } optionData = _automaticRedirection ? Interop.WinHttp.WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP : Interop.WinHttp.WINHTTP_OPTION_REDIRECT_POLICY_NEVER; SetWinHttpOption(requestHandle, Interop.WinHttp.WINHTTP_OPTION_REDIRECT_POLICY, ref optionData); }
private void SetWinHttpOption(SafeWinHttpHandle handle, uint option, ref uint optionData) { if (!Interop.WinHttp.WinHttpSetOption( handle, option, ref optionData)) { WinHttpException.ThrowExceptionUsingLastError(); } }
private void SetRequestHandleTlsOptions(SafeWinHttpHandle requestHandle) { // If we have a custom server certificate validation callback method then // we need to have WinHTTP ignore some errors so that the callback method // will have a chance to be called. uint optionData; if (_serverCertificateValidationCallback != null) { optionData = Interop.WinHttp.SECURITY_FLAG_IGNORE_UNKNOWN_CA | Interop.WinHttp.SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE | Interop.WinHttp.SECURITY_FLAG_IGNORE_CERT_CN_INVALID | Interop.WinHttp.SECURITY_FLAG_IGNORE_CERT_DATE_INVALID; SetWinHttpOption(requestHandle, Interop.WinHttp.WINHTTP_OPTION_SECURITY_FLAGS, ref optionData); } else if (_checkCertificateRevocationList) { // If no custom validation method, then we let WinHTTP do the revocation check itself. optionData = Interop.WinHttp.WINHTTP_ENABLE_SSL_REVOCATION; SetWinHttpOption(requestHandle, Interop.WinHttp.WINHTTP_OPTION_ENABLE_FEATURE, ref optionData); } }
private static void SetWinHttpOption( SafeWinHttpHandle handle, uint option, IntPtr optionData, uint optionSize) { if (!Interop.WinHttp.WinHttpSetOption( handle, option, optionData, optionSize)) { WinHttpException.ThrowExceptionUsingLastError(); } }
private static void SetNoClientCertificate(SafeWinHttpHandle requestHandle) { SetWinHttpOption( requestHandle, Interop.WinHttp.WINHTTP_OPTION_CLIENT_CERT_CONTEXT, IntPtr.Zero, 0); }
public static bool WinHttpQueryHeaders( SafeWinHttpHandle requestHandle, uint infoLevel, string name, StringBuilder buffer, ref uint bufferLength, IntPtr index) { string httpVersion = "HTTP/1.1"; string statusText = "OK"; if (infoLevel == Interop.WinHttp.WINHTTP_QUERY_VERSION) { if (buffer == null) { bufferLength = ((uint)httpVersion.Length + 1) * 2; TestControl.LastWin32Error = (int)Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER; return(false); } buffer.Append(httpVersion); return(true); } if (infoLevel == Interop.WinHttp.WINHTTP_QUERY_STATUS_TEXT) { if (buffer == null) { bufferLength = ((uint)statusText.Length + 1) * 2; TestControl.LastWin32Error = (int)Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER; return(false); } buffer.Append(statusText); return(true); } if (infoLevel == Interop.WinHttp.WINHTTP_QUERY_CONTENT_ENCODING) { string compression = null; if (TestServer.ResponseHeaders.Contains("Content-Encoding: deflate")) { compression = "deflate"; } else if (TestServer.ResponseHeaders.Contains("Content-Encoding: gzip")) { compression = "gzip"; } if (compression == null) { TestControl.LastWin32Error = (int)Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND; return(false); } if (buffer == null) { bufferLength = ((uint)compression.Length + 1) * 2; TestControl.LastWin32Error = (int)Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER; return(false); } buffer.Append(compression); return(true); } if (infoLevel == Interop.WinHttp.WINHTTP_QUERY_RAW_HEADERS_CRLF) { if (buffer == null) { bufferLength = ((uint)TestServer.ResponseHeaders.Length + 1) * 2; TestControl.LastWin32Error = (int)Interop.WinHttp.ERROR_INSUFFICIENT_BUFFER; return(false); } buffer.Append(TestServer.ResponseHeaders); return(true); } return(false); }
private void SetWinHttpCredential( SafeWinHttpHandle requestHandle, ICredentials credentials, Uri uri, uint authScheme, uint authTarget) { 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]); // Skip if no credentials or this is the default credential. if (networkCredential == null || networkCredential == CredentialCache.DefaultNetworkCredentials) { return; } string userName = networkCredential.UserName; string password = networkCredential.Password; string domain = networkCredential.Domain; // WinHTTP does not support a blank username. So, we will throw an exception. if (string.IsNullOrEmpty(userName)) { // TODO: Add error message. throw new InvalidOperationException(); } if (!string.IsNullOrEmpty(domain)) { userName = domain + "\\" + userName; } if (!Interop.WinHttp.WinHttpSetCredentials( requestHandle, authTarget, authScheme, userName, password, IntPtr.Zero)) { WinHttpException.ThrowExceptionUsingLastError(); } }
public static bool WinHttpQueryAuthSchemes( SafeWinHttpHandle requestHandle, out uint supportedSchemes, out uint firstScheme, out uint authTarget) { supportedSchemes = 0; firstScheme = 0; authTarget = 0; return true; }
private void SetWinHttpOption(SafeWinHttpHandle handle, uint option, string optionData) { if (!Interop.WinHttp.WinHttpSetOption( handle, option, optionData, (uint)optionData.Length)) { WinHttpException.ThrowExceptionUsingLastError(); } }
public static bool WinHttpSetTimeouts( SafeWinHttpHandle handle, int resolveTimeout, int connectTimeout, int sendTimeout, int receiveTimeout) { return true; }
private HttpResponseMessage CreateResponseMessage(SafeWinHttpHandle requestHandle, HttpRequestMessage request) { var response = new HttpResponseMessage(); bool useDeflateDecompression = false; bool useGzipDecompression = 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 = new Version(1, 1); } else if (string.Compare("HTTP/1.0", version, StringComparison.OrdinalIgnoreCase) == 0) { response.Version = new Version(1, 0); } else { response.Version = null; } response.StatusCode = (HttpStatusCode)GetResponseHeaderNumberInfo( requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE); response.ReasonPhrase = GetResponseHeaderStringInfo( requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_TEXT); if (_doManualDecompressionCheck) { string contentEncoding = GetResponseHeaderStringInfo( requestHandle, Interop.WinHttp.WINHTTP_QUERY_CONTENT_ENCODING); if (!string.IsNullOrEmpty(contentEncoding)) { if (contentEncoding.IndexOf(EncodingNameDeflate, StringComparison.OrdinalIgnoreCase) > -1) { useDeflateDecompression = true; } else if (contentEncoding.IndexOf(EncodingNameGzip, StringComparison.OrdinalIgnoreCase) > -1) { useGzipDecompression = true; } } } // Create response stream and wrap it in a StreamContent object. var responseStream = new WinHttpResponseStream(requestHandle); Stream decompressedStream = responseStream; if (_doManualDecompressionCheck) { if (useDeflateDecompression) { decompressedStream = new DeflateStream(responseStream, CompressionMode.Decompress); } else if (useGzipDecompression) { decompressedStream = new GZipStream(responseStream, CompressionMode.Decompress); } } var content = new StreamContent(decompressedStream); response.Content = content; response.RequestMessage = request; // Parse raw response headers and place them into response message. ParseResponseHeaders(requestHandle, response, useDeflateDecompression || useGzipDecompression); // 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. } } } } // 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(requestHandle, Interop.WinHttp.WINHTTP_OPTION_RECEIVE_TIMEOUT, ref optionData); return response; }
public static bool WinHttpGetProxyForUrl( SafeWinHttpHandle sessionHandle, string url, ref Interop.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out Interop.WinHttp.WINHTTP_PROXY_INFO proxyInfo) { if (TestControl.PACFileNotDetectedOnNetwork) { proxyInfo.AccessType = WINHTTP_ACCESS_TYPE_NO_PROXY; proxyInfo.Proxy = IntPtr.Zero; proxyInfo.ProxyBypass = IntPtr.Zero; TestControl.LastWin32Error = (int)Interop.WinHttp.ERROR_WINHTTP_AUTODETECTION_FAILED; return false; } proxyInfo.AccessType = Interop.WinHttp.WINHTTP_ACCESS_TYPE_NAMED_PROXY; proxyInfo.Proxy = Marshal.StringToHGlobalUni(FakeRegistry.WinInetProxySettings.Proxy); proxyInfo.ProxyBypass = IntPtr.Zero; return true; }
private string GetResponseHeaderStringInfo(SafeWinHttpHandle requestHandle, uint infoLevel) { 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, IntPtr.Zero)) { 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 / 2; var buffer = new StringBuilder(charsNeeded, charsNeeded); results = Interop.WinHttp.WinHttpQueryHeaders( requestHandle, infoLevel, Interop.WinHttp.WINHTTP_HEADER_NAME_BY_INDEX, buffer, ref bytesNeeded, IntPtr.Zero); if (!results) { WinHttpException.ThrowExceptionUsingLastError(); } return buffer.ToString(); }
public static IntPtr WinHttpSetStatusCallback( SafeWinHttpHandle handle, Interop.WinHttp.WINHTTP_STATUS_CALLBACK callback, uint notificationFlags, IntPtr reserved) { if (handle == null) { throw new ArgumentNullException("handle"); } return IntPtr.Zero; }
private void SetStatusCallback( SafeWinHttpHandle requestHandle, Interop.WinHttp.WINHTTP_STATUS_CALLBACK callback) { IntPtr oldCallback = Interop.WinHttp.WinHttpSetStatusCallback( requestHandle, callback, Interop.WinHttp.WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, IntPtr.Zero); if (oldCallback == new IntPtr(Interop.WinHttp.WINHTTP_INVALID_STATUS_CALLBACK)) { int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_INVALID_HANDLE) // Ignore error if handle was already closed. { throw WinHttpException.CreateExceptionUsingError(lastError); } } }
public static bool WinHttpAddRequestHeaders( SafeWinHttpHandle requestHandle, StringBuilder headers, uint headersLength, uint modifiers) { return true; }
private void EnsureSessionHandleExists(RequestState 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, 0); if (!_sessionHandle.IsInvalid) { return; } int lastError = Marshal.GetLastWin32Error(); if (lastError != Interop.WinHttp.ERROR_INVALID_PARAMETER) { throw new HttpRequestException( SR.net_http_client_execution_error, WinHttpException.CreateExceptionUsingError(lastError)); } // 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, 0); if (_sessionHandle.IsInvalid) { throw new HttpRequestException( SR.net_http_client_execution_error, WinHttpException.CreateExceptionUsingLastError()); } } } } }
private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle sessionHandle) { _proxyHelper = proxyHelper; _sessionHandle = sessionHandle; if (proxyHelper.ManualSettingsUsed) { if (NetEventSource.IsEnabled) { NetEventSource.Info(proxyHelper, $"ManualSettingsUsed, {proxyHelper.Proxy}"); } _secureProxy = MultiProxy.Parse(_failedProxies, proxyHelper.Proxy, true); _insecureProxy = MultiProxy.Parse(_failedProxies, proxyHelper.Proxy, false); if (!string.IsNullOrWhiteSpace(proxyHelper.ProxyBypass)) { int idx = 0; int start = 0; string tmp; // Process bypass list for manual setting. // Initial list size is best guess based on string length assuming each entry is at least 5 characters on average. _bypass = new List <string>(proxyHelper.ProxyBypass.Length / 5); while (idx < proxyHelper.ProxyBypass.Length) { // Strip leading spaces and scheme if any. while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == ' ') { idx += 1; } ; if (string.Compare(proxyHelper.ProxyBypass, idx, "http://", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) { idx += 7; } else if (string.Compare(proxyHelper.ProxyBypass, idx, "https://", 0, 8, StringComparison.OrdinalIgnoreCase) == 0) { idx += 8; } if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == '[') { // Strip [] from IPv6 so we can use IdnHost laster for matching. idx += 1; } start = idx; while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ' ' && proxyHelper.ProxyBypass[idx] != ';' && proxyHelper.ProxyBypass[idx] != ']') { idx += 1; } ; if (idx == start) { // Empty string. tmp = null; } else if (string.Compare(proxyHelper.ProxyBypass, start, "<local>", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) { _bypassLocal = true; tmp = null; } else { tmp = proxyHelper.ProxyBypass.Substring(start, idx - start); } // Skip trailing characters if any. if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ';') { // Got stopped at space or ']'. Strip until next ';' or end. while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ';') { idx += 1; } ; } if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == ';') { idx++; } if (tmp == null) { continue; } _bypass.Add(tmp); } if (_bypass.Count == 0) { // Bypass string only had garbage we did not parse. _bypass = null; } } if (_bypassLocal) { _localIp = new List <IPAddress>(); foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) { IPInterfaceProperties ipProps = netInterface.GetIPProperties(); foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses) { _localIp.Add(addr.Address); } } } } }