private void Dispose(bool disposing) { if (WinHttpTraceHelper.IsTraceEnabled()) { WinHttpTraceHelper.Trace( "WinHttpRequestState.Dispose, GCHandle=0x{0:X}, disposed={1}, disposing={2}", ToIntPtr(), _disposed, disposing); } // Since there is no finalizer and this class is sealed, the disposing parameter should be TRUE. Debug.Assert(disposing, "WinHttpRequestState.Dispose() should have disposing=TRUE"); if (_disposed) { return; } _disposed = true; if (_operationHandle.IsAllocated) { _operationHandle.Free(); } }
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); } } }
// The only way to abort pending async operations in WinHTTP is to close the request handle. // This causes WinHTTP to cancel any pending I/O and accelerating its callbacks on the handle. // This causes our related TaskCompletionSource objects to move to a terminal state. // // We only want to dispose the handle if we are actually waiting for a pending WinHTTP I/O to complete, // meaning that we are await'ing for a Task to complete. While we could simply call dispose without // a pending operation, it would cause random failures in the other threads when we expect a valid handle. private void CancelPendingResponseStreamReadOperation() { WinHttpTraceHelper.Trace("WinHttpResponseStream.CancelPendingResponseStreamReadOperation"); lock (_state.Lock) { if (_state.AsyncReadInProgress) { WinHttpTraceHelper.Trace("WinHttpResponseStream.CancelPendingResponseStreamReadOperation: before dispose"); _requestHandle?.Dispose(); // null check necessary to handle race condition between stream disposal and cancellation WinHttpTraceHelper.Trace("WinHttpResponseStream.CancelPendingResponseStreamReadOperation: after dispose"); } } }
// The only way to abort pending async operations in WinHTTP is to close the request handle. // This causes WinHTTP to cancel any pending I/O and accelerating its callbacks on the handle. // This causes our related TaskCompletionSource objects to move to a terminal state. // // We only want to dispose the handle if we are actually waiting for a pending WinHTTP I/O to complete, // meaning that we are await'ing for a Task to complete. While we could simply call dispose without // a pending operation, it would cause random failures in the other threads when we expect a valid handle. private void CancelPendingResponseStreamReadOperation() { WinHttpTraceHelper.Trace("WinHttpResponseStream.CancelPendingResponseStreamReadOperation"); lock (_state.Lock) { if (_state.AsyncReadInProgress) { Debug.Assert(_requestHandle != null); Debug.Assert(!_requestHandle.IsInvalid); WinHttpTraceHelper.Trace("WinHttpResponseStream.CancelPendingResponseStreamReadOperation: before dispose"); _requestHandle.Dispose(); WinHttpTraceHelper.Trace("WinHttpResponseStream.CancelPendingResponseStreamReadOperation: after dispose"); } } }
// The only way to abort pending async operations in WinHTTP is to close the request handle. // This causes WinHTTP to cancel any pending I/O and accelerating its callbacks on the handle. // This causes our related TaskCompletionSource objects to move to a terminal state. // // We only want to dispose the handle if we are actually waiting for a pending WinHTTP I/O to complete, // meaning that we are await'ing for a Task to complete. While we could simply call dispose without // a pending operation, it would cause random failures in the other threads when we expect a valid handle. private void CancelPendingResponseStreamReadOperation() { WinHttpTraceHelper.Trace("WinHttpResponseStream.CancelPendingResponseStreamReadOperation"); lock (_state.Lock) { WinHttpTraceHelper.Trace( string.Format("WinHttpResponseStream.CancelPendingResponseStreamReadOperation: {0} {1}", (int)_state.TcsQueryDataAvailable.Task.Status, (int)_state.TcsReadFromResponseStream.Task.Status)); if (!_state.TcsQueryDataAvailable.Task.IsCompleted) { Debug.Assert(_requestHandle != null); Debug.Assert(!_requestHandle.IsInvalid); WinHttpTraceHelper.Trace("WinHttpResponseStream.CancelPendingResponseStreamReadOperation: before dispose"); _requestHandle.Dispose(); WinHttpTraceHelper.Trace("WinHttpResponseStream.CancelPendingResponseStreamReadOperation: after dispose"); } } }
private void Dispose(bool disposing) { #if DEBUG Interlocked.Increment(ref s_dbg_callDispose); #endif if (WinHttpTraceHelper.IsTraceEnabled()) { WinHttpTraceHelper.Trace( "WinHttpRequestState.Dispose, GCHandle=0x{0:X}, disposed={1}, disposing={2}", ToIntPtr(), _disposed, disposing); } // Since there is no finalizer and this class is sealed, the disposing parameter should be TRUE. Debug.Assert(disposing, "WinHttpRequestState.Dispose() should have disposing=TRUE"); if (_disposed) { return; } _disposed = true; if (_operationHandle.IsAllocated) { // This method only gets called when the WinHTTP request handle is fully closed and thus all // async operations are done. So, it is safe at this point to unpin the buffers and release // the strong GCHandle for this object. if (_cachedReceivePinnedBuffer.IsAllocated) { _cachedReceivePinnedBuffer.Free(); _cachedReceivePinnedBuffer = default(GCHandle); } #if DEBUG Interlocked.Increment(ref s_dbg_operationHandleFree); #endif _operationHandle.Free(); _operationHandle = default(GCHandle); } }
private const int _recentAutoDetectionInterval = 120_000; // 2 minutes in milliseconds. public WinInetProxyHelper() { var proxyConfig = new Interop.WinHttp.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG(); try { if (Interop.WinHttp.WinHttpGetIEProxyConfigForCurrentUser(out proxyConfig)) { AutoConfigUrl = Marshal.PtrToStringUni(proxyConfig.AutoConfigUrl); AutoDetect = proxyConfig.AutoDetect; Proxy = Marshal.PtrToStringUni(proxyConfig.Proxy); ProxyBypass = Marshal.PtrToStringUni(proxyConfig.ProxyBypass); WinHttpTraceHelper.Trace( "WinInetProxyHelper.ctor: AutoConfigUrl={0}, AutoDetect={1}, Proxy={2}, ProxyBypass={3}", AutoConfigUrl, AutoDetect, Proxy, ProxyBypass); _useProxy = true; } else { // We match behavior of WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY and ignore errors. int lastError = Marshal.GetLastWin32Error(); WinHttpTraceHelper.Trace("WinInetProxyHelper.ctor: error={0}", lastError); } WinHttpTraceHelper.Trace("WinInetProxyHelper.ctor: _useProxy={0}", _useProxy); } finally { // FreeHGlobal already checks for null pointer before freeing the memory. Marshal.FreeHGlobal(proxyConfig.AutoConfigUrl); Marshal.FreeHGlobal(proxyConfig.Proxy); Marshal.FreeHGlobal(proxyConfig.ProxyBypass); } }
private static void OnRequestSendingRequest(WinHttpRequestState state) { Debug.Assert(state != null, "OnRequestSendingRequest: state is null"); Debug.Assert(state.RequestHandle != null, "OnRequestSendingRequest: state.RequestHandle 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(); WinHttpTraceHelper.Trace( "OnRequestSendingRequest: Error getting WINHTTP_OPTION_SERVER_CERT_CONTEXT, {0}", lastError); if (lastError == Interop.WinHttp.ERROR_WINHTTP_INCORRECT_HANDLE_STATE) { // Not yet an SSL/TLS connection. This occurs while connecting thru a proxy where the // CONNECT verb hasn't yet been processed due to the proxy requiring authentication. // We need to ignore this notification. Another notification will be sent once the final // connection thru the proxy is completed. return; } throw WinHttpException.CreateExceptionUsingError(lastError); } // Get any additional certificates sent from the remote server during the TLS/SSL handshake. X509Certificate2Collection remoteCertificateStore = UnmanagedCertificateContext.GetRemoteCertificatesFromStoreContext(certHandle); // 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; bool result = false; try { WinHttpCertificateHelper.BuildChain( serverCertificate, remoteCertificateStore, state.RequestMessage.RequestUri.Host, state.CheckCertificateRevocationList, out chain, out sslPolicyErrors); result = state.ServerCertificateValidationCallback( state.RequestMessage, serverCertificate, chain, sslPolicyErrors); } catch (Exception ex) { throw WinHttpException.CreateExceptionUsingError( (int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE, ex); } finally { if (chain != null) { chain.Dispose(); } serverCertificate.Dispose(); } if (!result) { throw WinHttpException.CreateExceptionUsingError( (int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE); } } }
public void DisposeCtrReadFromResponseStream() { WinHttpTraceHelper.Trace("WinHttpRequestState.DisposeCtrReadFromResponseStream: disposing ctr"); CtrReadFromResponseStream.Dispose(); CtrReadFromResponseStream = default(CancellationTokenRegistration); }
// TODO: Issue #2165. Merge with similar code used in System.Net.Security move to Common/src//System/Net. public static void BuildChain( X509Certificate2 certificate, X509Certificate2Collection remoteCertificateStore, string hostName, bool checkCertificateRevocationList, out X509Chain chain, out SslPolicyErrors sslPolicyErrors) { chain = null; sslPolicyErrors = SslPolicyErrors.None; // Build the chain. chain = new X509Chain(); chain.ChainPolicy.RevocationMode = checkCertificateRevocationList ? X509RevocationMode.Online : X509RevocationMode.NoCheck; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; // Authenticate the remote party: (e.g. when operating in client mode, authenticate the server). chain.ChainPolicy.ApplicationPolicy.Add(s_serverAuthOid); if (remoteCertificateStore.Count > 0) { if (WinHttpTraceHelper.IsTraceEnabled()) { foreach (X509Certificate cert in remoteCertificateStore) { WinHttpTraceHelper.Trace("WinHttpCertificateHelper.BuildChain: adding cert to ExtraStore: {0}", cert.Subject); } } chain.ChainPolicy.ExtraStore.AddRange(remoteCertificateStore); } if (!chain.Build(certificate)) { sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors; } // Verify the hostName matches the certificate. unsafe { var cppStruct = new Interop.Crypt32.CERT_CHAIN_POLICY_PARA(); cppStruct.cbSize = (uint)Marshal.SizeOf <Interop.Crypt32.CERT_CHAIN_POLICY_PARA>(); cppStruct.dwFlags = 0; var eppStruct = new Interop.Crypt32.SSL_EXTRA_CERT_CHAIN_POLICY_PARA(); eppStruct.cbSize = (uint)Marshal.SizeOf <Interop.Crypt32.SSL_EXTRA_CERT_CHAIN_POLICY_PARA>(); eppStruct.dwAuthType = Interop.Crypt32.AuthType.AUTHTYPE_SERVER; cppStruct.pvExtraPolicyPara = &eppStruct; fixed(char *namePtr = hostName) { eppStruct.pwszServerName = namePtr; cppStruct.dwFlags = Interop.Crypt32.CertChainPolicyIgnoreFlags.CERT_CHAIN_POLICY_IGNORE_ALL & ~Interop.Crypt32.CertChainPolicyIgnoreFlags.CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG; var status = new Interop.Crypt32.CERT_CHAIN_POLICY_STATUS(); status.cbSize = (uint)sizeof(Interop.Crypt32.CERT_CHAIN_POLICY_STATUS); if (Interop.Crypt32.CertVerifyCertificateChainPolicy( (IntPtr)Interop.Crypt32.CertChainPolicy.CERT_CHAIN_POLICY_SSL, chain.SafeHandle, ref cppStruct, ref status)) { if (status.dwError == Interop.Crypt32.CertChainPolicyErrors.CERT_E_CN_NO_MATCH) { sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch; } } else { // Failure checking the policy. This is a rare error. We will assume the name check failed. // TODO: Issue #2165. Log this error or perhaps throw an exception instead. sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch; } } } }
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken token) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (offset < 0) { throw new ArgumentOutOfRangeException(nameof(offset)); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } if (count > buffer.Length - offset) { throw new ArgumentException(SR.net_http_buffer_insufficient_length, nameof(buffer)); } if (token.IsCancellationRequested) { return Task.FromCanceled<int>(token); } CheckDisposed(); if (_state.TcsReadFromResponseStream != null && !_state.TcsReadFromResponseStream.Task.IsCompleted) { throw new InvalidOperationException(SR.net_http_no_concurrent_io_allowed); } _state.PinReceiveBuffer(buffer); _state.TcsReadFromResponseStream = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously); _state.TcsQueryDataAvailable = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously); _state.TcsQueryDataAvailable.Task.ContinueWith((previousTask) => { if (previousTask.IsFaulted) { _state.DisposeCtrReadFromResponseStream(); _state.TcsReadFromResponseStream.TrySetException(previousTask.Exception.InnerException); } else if (previousTask.IsCanceled || token.IsCancellationRequested) { _state.DisposeCtrReadFromResponseStream(); _state.TcsReadFromResponseStream.TrySetCanceled(token); } else { int bytesToRead; int bytesAvailable = previousTask.Result; if (bytesAvailable > count) { bytesToRead = count; } else { bytesToRead = bytesAvailable; } lock (_state.Lock) { Debug.Assert(!_requestHandle.IsInvalid); if (!Interop.WinHttp.WinHttpReadData( _requestHandle, Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset), (uint)bytesToRead, IntPtr.Zero)) { _state.DisposeCtrReadFromResponseStream(); _state.TcsReadFromResponseStream.TrySetException( new IOException(SR.net_http_io_read, WinHttpException.CreateExceptionUsingLastError().InitializeStackTrace())); } } } }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default); // Register callback on cancellation token to cancel any pending WinHTTP operation. if (token.CanBeCanceled) { WinHttpTraceHelper.Trace("WinHttpResponseStream.ReadAsync: registering for cancellation token request"); _state.CtrReadFromResponseStream = token.Register(s => ((WinHttpResponseStream)s).CancelPendingResponseStreamReadOperation(), this); } else { WinHttpTraceHelper.Trace("WinHttpResponseStream.ReadAsync: received no cancellation token"); } lock (_state.Lock) { Debug.Assert(!_requestHandle.IsInvalid); if (!Interop.WinHttp.WinHttpQueryDataAvailable(_requestHandle, IntPtr.Zero)) { _state.DisposeCtrReadFromResponseStream(); _state.TcsReadFromResponseStream.TrySetException( new IOException(SR.net_http_io_read, WinHttpException.CreateExceptionUsingLastError().InitializeStackTrace())); } } return _state.TcsReadFromResponseStream.Task; }
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. // 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. // // 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)) { WinHttpTraceHelper.Trace("WinInetProxyHelper.GetProxyForUrl: Using autoconfig proxy settings"); useProxy = true; break; } else { var lastError = Marshal.GetLastWin32Error(); WinHttpTraceHelper.Trace("WinInetProxyHelper.GetProxyForUrl: error={0}", 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); WinHttpTraceHelper.Trace( "WinInetProxyHelper.GetProxyForUrl: Fallback to Proxy={0}, ProxyBypass={1}", Proxy, ProxyBypass); useProxy = true; } WinHttpTraceHelper.Trace("WinInetProxyHelper.GetProxyForUrl: useProxy={0}", useProxy); return(useProxy); }