| File: System\Net\Http\WinHttpRequestState.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.Http.WinHttpHandler\src\System.Net.Http.WinHttpHandler.csproj (System.Net.Http.WinHttpHandler) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Net.Security; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; using SafeWinHttpHandle = Interop.WinHttp.SafeWinHttpHandle; namespace System.Net.Http { internal sealed class WinHttpRequestState : IDisposable { #if DEBUG private static int s_dbg_allocated; private static int s_dbg_pin; private static int s_dbg_clearSendRequestState; private static int s_dbg_callDispose; private static int s_dbg_operationHandleFree; private IntPtr s_dbg_requestHandle; #endif // A GCHandle for this operation object. // This is owned by the callback and will be deallocated when the sessionHandle has been closed. private GCHandle _operationHandle; private WinHttpTransportContext? _transportContext; private int _disposed; // To detect redundant calls. public WinHttpRequestState() { #if DEBUG Interlocked.Increment(ref s_dbg_allocated); #endif } public void Pin() { if (!_operationHandle.IsAllocated) { #if DEBUG Interlocked.Increment(ref s_dbg_pin); #endif _operationHandle = GCHandle.Alloc(this); } } public static WinHttpRequestState? FromIntPtr(IntPtr gcHandle) { GCHandle stateHandle = GCHandle.FromIntPtr(gcHandle); return (WinHttpRequestState?)stateHandle.Target; } public IntPtr ToIntPtr() { return GCHandle.ToIntPtr(_operationHandle); } // The current locking mechanism doesn't allow any two WinHttp functions executing at // the same time for the same handle. Enhance locking to prevent only WinHttpCloseHandle being called // during other API execution. E.g. using a Reader/Writer model or, even better, Interlocked functions. // The lock object must be used during the execution of any WinHttp function to ensure no race conditions with // calling WinHttpCloseHandle. public object Lock => this; public void ClearSendRequestState() { #if DEBUG Interlocked.Increment(ref s_dbg_clearSendRequestState); #endif // Since WinHttpRequestState has a self-referenced strong GCHandle, we // need to clear out object references to break cycles and prevent leaks. Tcs = null; TcsInternalWriteDataToRequestStream = null; CancellationToken = default(CancellationToken); RequestMessage = null; Handler = null; ServerCertificateValidationCallback = null; TransportContext = null; Proxy = null; ServerCredentials = null; DefaultProxyCredentials = null; if (RequestHandle != null) { RequestHandle.Dispose(); RequestHandle = null; } } public TaskCompletionSource<HttpResponseMessage>? Tcs { get; set; } public CancellationToken CancellationToken { get; set; } public HttpRequestMessage? RequestMessage { get; set; } public WinHttpHandler? Handler { get; set; } private SafeWinHttpHandle? _requestHandle; public SafeWinHttpHandle? RequestHandle { get { return _requestHandle; } set { #if DEBUG if (value != null) { s_dbg_requestHandle = value.DangerousGetHandle(); } #endif _requestHandle = value; } } public Exception? SavedException { get; set; } public bool CheckCertificateRevocationList { get; set; } public Func<HttpRequestMessage, X509Certificate2, X509Chain, SslPolicyErrors, bool>? ServerCertificateValidationCallback { get; set; } [AllowNull] public WinHttpTransportContext TransportContext { get { return _transportContext ??= new WinHttpTransportContext(); } set { _transportContext = value; } } public WindowsProxyUsePolicy WindowsProxyUsePolicy { get; set; } public IWebProxy? Proxy { get; set; } public ICredentials? ServerCredentials { get; set; } public ICredentials? DefaultProxyCredentials { get; set; } public bool PreAuthenticate { get; set; } public HttpStatusCode LastStatusCode { get; set; } public bool RetryRequest { get; set; } public RendezvousAwaitable<int> LifecycleAwaitable { get; set; } = new RendezvousAwaitable<int>(); public TaskCompletionSource<bool>? TcsInternalWriteDataToRequestStream { get; set; } public volatile int AsyncReadInProgress; // WinHttpResponseStream state. public long? ExpectedBytesToRead { get; set; } public long CurrentBytesRead { get; set; } private GCHandle _cachedReceivePinnedBuffer; private GCHandle _cachedSendPinnedBuffer; public void PinReceiveBuffer(byte[] buffer) { if (!_cachedReceivePinnedBuffer.IsAllocated || _cachedReceivePinnedBuffer.Target != buffer) { if (_cachedReceivePinnedBuffer.IsAllocated) { _cachedReceivePinnedBuffer.Free(); } _cachedReceivePinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned); } } public void PinSendBuffer(byte[] buffer) { if (!_cachedSendPinnedBuffer.IsAllocated || _cachedSendPinnedBuffer.Target != buffer) { if (_cachedSendPinnedBuffer.IsAllocated) { _cachedSendPinnedBuffer.Free(); } _cachedSendPinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned); } } #region IDisposable Members private void Dispose(bool disposing) { #if DEBUG Interlocked.Increment(ref s_dbg_callDispose); #endif if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"GCHandle=0x{ToIntPtr():X}, disposed={_disposed != 0}, disposing={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 (Interlocked.Exchange(ref _disposed, 1) != 0) { return; } 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 the pinned buffers. if (_cachedReceivePinnedBuffer.IsAllocated) { _cachedReceivePinnedBuffer.Free(); _cachedReceivePinnedBuffer = default(GCHandle); } if (_cachedSendPinnedBuffer.IsAllocated) { _cachedSendPinnedBuffer.Free(); _cachedSendPinnedBuffer = default(GCHandle); } #if DEBUG Interlocked.Increment(ref s_dbg_operationHandleFree); #endif _operationHandle.Free(); _operationHandle = default(GCHandle); } } public void Dispose() { // No need to suppress finalization since the finalizer is not overridden and the class is sealed. Dispose(true); } #endregion } }