HttpListener.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Net / System / Net / HttpListener.cs / 1305376 / HttpListener.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

//disabled until BindHandle has an overload that accepts Criticalhandles 
#pragma warning disable 618 

namespace System.Net { 
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized; 
    using System.IO;
    using System.Net; 
    using System.Net.Sockets; 
    using System.Runtime.InteropServices;
    using System.Security.Permissions; 
    using System.Text;
    using System.Threading;
    using System.ComponentModel;
    using System.Diagnostics; 
    using System.Globalization;
    using System.Security; 
    using System.Security.Authentication.ExtendedProtection; 
    using System.Security.Principal;
    using System.Security.Cryptography.X509Certificates; 
    using Microsoft.Win32;
    using Microsoft.Win32.SafeHandles;
    using System.Diagnostics.CodeAnalysis;
 
    public class HttpListenerBasicIdentity : GenericIdentity
    { 
        private string m_Password; 

        public HttpListenerBasicIdentity(string username, string password) : 
            base(username, BasicClient.AuthType)
        {
            m_Password = password;
        } 

        public virtual string Password 
        { 
            get
            { 
                return m_Password;
            }
        }
    } 

    internal abstract unsafe class RequestContextBase : IDisposable 
    { 
        private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* m_MemoryBlob;
        private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* m_OriginalBlobAddress; 
        private byte[] m_BackingBuffer;

        // Must call this from derived class' constructors.
        protected void BaseConstruction(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* requestBlob) 
        {
            if (requestBlob == null) 
            { 
                GC.SuppressFinalize(this);
            } 
            else
            {
                m_MemoryBlob = requestBlob;
            } 
        }
 
        // ReleasePins() should be called exactly once.  It must be called before Dispose() is called, which means it must be called 
        // before an object (HttpListenerReqeust) which closes the RequestContext on demand is returned to the application.
        internal void ReleasePins() 
        {
            GlobalLog.Assert(m_MemoryBlob != null || m_BackingBuffer == null, "RequestContextBase::ReleasePins()|ReleasePins() called twice.");
            m_OriginalBlobAddress = m_MemoryBlob;
            UnsetBlob(); 
            OnReleasePins();
        } 
 
        protected abstract void OnReleasePins();
 
        public void Close()
        {
            Dispose();
        } 

        public void Dispose() 
        { 
            GlobalLog.Assert(m_MemoryBlob == null, "RequestContextBase::Dispose()|Dispose() called before ReleasePins().");
            Dispose(true); 
        }

        protected virtual void Dispose(bool disposing) { }
 
        ~RequestContextBase()
        { 
            Dispose(false); 
        }
 
        internal UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* RequestBlob
        {
            get
            { 
                GlobalLog.Assert(m_MemoryBlob != null || m_BackingBuffer == null, "RequestContextBase::Dispose()|RequestBlob requested after ReleasePins().");
                return m_MemoryBlob; 
            } 
        }
 
        internal byte[] RequestBuffer
        {
            get
            { 
                return m_BackingBuffer;
            } 
        } 

        internal uint Size 
        {
            get
            {
                return (uint) m_BackingBuffer.Length; 
            }
        } 
 
        internal IntPtr OriginalBlobAddress
        { 
            get
            {
                UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* blob = m_MemoryBlob;
                return (IntPtr) (blob == null ? m_OriginalBlobAddress : blob); 
            }
        } 
 
        protected void SetBlob(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* requestBlob)
        { 
            GlobalLog.Assert(m_MemoryBlob != null || m_BackingBuffer == null, "RequestContextBase::Dispose()|SetBlob() called after ReleasePins().");
            if (requestBlob == null)
            {
                UnsetBlob(); 
                return;
            } 
 
            if (m_MemoryBlob == null)
            { 
                GC.ReRegisterForFinalize(this);
            }
            m_MemoryBlob = requestBlob;
        } 

        protected void UnsetBlob() 
        { 
            if (m_MemoryBlob != null)
            { 
                GC.SuppressFinalize(this);
            }
            m_MemoryBlob = null;
        } 

        protected void SetBuffer(int size) 
        { 
            m_BackingBuffer = size == 0 ? null : new byte[size];
        } 
    }

    internal unsafe class AsyncRequestContext : RequestContextBase
    { 
        private NativeOverlapped* m_NativeOverlapped;
        private ListenerAsyncResult m_Result; 
 
        internal AsyncRequestContext(ListenerAsyncResult result)
        { 
            m_Result = result;
            BaseConstruction(Allocate(0));
        }
 
        private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* Allocate(uint size)
        { 
            uint newSize = size != 0 ? size : RequestBuffer == null ? 4096 : Size; 
            if (m_NativeOverlapped != null && newSize != RequestBuffer.Length)
            { 
                NativeOverlapped* nativeOverlapped = m_NativeOverlapped;
                m_NativeOverlapped = null;
                Overlapped.Free(nativeOverlapped);
            } 
            if (m_NativeOverlapped == null)
            { 
                SetBuffer(checked((int) newSize)); 
                Overlapped overlapped = new Overlapped();
                overlapped.AsyncResult = m_Result; 
                m_NativeOverlapped = overlapped.Pack(ListenerAsyncResult.IOCallback, RequestBuffer);
                return (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST*) Marshal.UnsafeAddrOfPinnedArrayElement(RequestBuffer, 0);
            }
            return RequestBlob; 
        }
 
        internal void Reset(ulong requestId, uint size) 
        {
            SetBlob(Allocate(size)); 
            RequestBlob->RequestId = requestId;
        }

        protected override void OnReleasePins() 
        {
            if (m_NativeOverlapped != null) 
            { 
                NativeOverlapped* nativeOverlapped = m_NativeOverlapped;
                m_NativeOverlapped = null; 
                Overlapped.Free(nativeOverlapped);
            }
        }
 
        protected override void Dispose(bool disposing)
        { 
            if (m_NativeOverlapped != null) 
            {
                GlobalLog.Assert(!disposing, "AsyncRequestContext::Dispose()|Must call ReleasePins() before calling Dispose()."); 
                if (!NclUtilities.HasShutdownStarted || disposing)
                {
                    Overlapped.Free(m_NativeOverlapped);
                } 
            }
            base.Dispose(disposing); 
        } 

        internal NativeOverlapped* NativeOverlapped 
        {
            get
            {
                return m_NativeOverlapped; 
            }
        } 
    } 

    internal unsafe class SyncRequestContext : RequestContextBase 
    {
        private GCHandle m_PinnedHandle;

        internal SyncRequestContext(int size) 
        {
            BaseConstruction(Allocate(size)); 
        } 

        private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST* Allocate(int size) 
        {
            if (m_PinnedHandle.IsAllocated)
            {
                if (RequestBuffer.Length == size) 
                {
                    return RequestBlob; 
                } 
                m_PinnedHandle.Free();
            } 
            SetBuffer(size);
            if (RequestBuffer == null)
            {
                return null; 
            }
            m_PinnedHandle = GCHandle.Alloc(RequestBuffer, GCHandleType.Pinned); 
            return (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST*) Marshal.UnsafeAddrOfPinnedArrayElement(RequestBuffer, 0); 
        }
 
        internal void Reset(int size)
        {
            SetBlob(Allocate(size));
        } 

        protected override void OnReleasePins() 
        { 
            if (m_PinnedHandle.IsAllocated)
            { 
                m_PinnedHandle.Free();
            }
        }
 
        protected override void Dispose(bool disposing)
        { 
            if (m_PinnedHandle.IsAllocated) 
            {
                GlobalLog.Assert(!disposing, "AsyncRequestContext::Dispose()|Must call ReleasePins() before calling Dispose()."); 
                if (!NclUtilities.HasShutdownStarted || disposing)
                {
                    m_PinnedHandle.Free();
                } 
            }
            base.Dispose(disposing); 
        } 
    }
 
    public sealed unsafe class HttpListener : IDisposable
    {
        private static readonly Type ChannelBindingStatusType = typeof(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_CHANNEL_BIND_STATUS);
        private static readonly int RequestChannelBindStatusSize = 
            Marshal.SizeOf(typeof(UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_CHANNEL_BIND_STATUS));
        private static readonly UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION Version = 
            UnsafeNclNativeMethods.HttpApi.ApiVersion; 

        private static byte[] s_WwwAuthenticateBytes = new byte[] 
        {
            (byte) 'W', (byte) 'W', (byte) 'W', (byte) '-', (byte) 'A', (byte) 'u', (byte) 't', (byte) 'h',
            (byte) 'e', (byte) 'n', (byte) 't', (byte) 'i', (byte) 'c', (byte) 'a', (byte) 't', (byte) 'e'
        }; 

        private class AuthenticationSelectorInfo 
        { 
            private AuthenticationSchemeSelector m_SelectorDelegate;
            private bool m_CanUseAdvancedAuth; 

            internal AuthenticationSelectorInfo(AuthenticationSchemeSelector selectorDelegate, bool canUseAdvancedAuth)
            {
                Debug.Assert(selectorDelegate != null); 

                m_SelectorDelegate = selectorDelegate; 
                m_CanUseAdvancedAuth = canUseAdvancedAuth; 
            }
 
            internal AuthenticationSchemeSelector Delegate
            {
                get
                { 
                    return m_SelectorDelegate;
                } 
            } 

            internal bool AdvancedAuth 
            {
                get
                {
                    return m_CanUseAdvancedAuth; 
                }
            } 
        } 

        private AuthenticationSelectorInfo m_AuthenticationDelegate; 
        private AuthenticationSchemes m_AuthenticationScheme = AuthenticationSchemes.Anonymous;
        private SecurityException m_SecurityException;
        private string m_Realm;
        private CriticalHandle m_RequestQueueHandle; 
        private bool m_RequestHandleBound;
        private volatile State m_State; // m_State is set only within lock blocks, but often read outside locks. 
        private HttpListenerPrefixCollection m_Prefixes; 
        private bool m_IgnoreWriteExceptions;
        private bool m_UnsafeConnectionNtlmAuthentication; 
        private ExtendedProtectionSelector m_ExtendedProtectionSelectorDelegate;
        private ExtendedProtectionPolicy m_ExtendedProtectionPolicy;
        private ServiceNameStore m_DefaultServiceNames;
        private HttpServerSessionHandle m_ServerSessionHandle; 
        private ulong m_UrlGroupId;
        private HttpListenerTimeoutManager m_TimeoutManager; 
        private bool m_V2Initialized; 

        private Hashtable m_DisconnectResults;         // ulong -> DisconnectAsyncResult 
        private object m_InternalLock;

        internal Hashtable m_UriPrefixes = new Hashtable();
 
        public delegate ExtendedProtectionPolicy ExtendedProtectionSelector(HttpListenerRequest request);
 
        public HttpListener() 
        {
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "HttpListener", ""); 
            if (!UnsafeNclNativeMethods.HttpApi.Supported) {
                throw new PlatformNotSupportedException();
            }
 
            GlobalLog.Assert(Version != UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION.Invalid, "Invalid Http api version");
 
            m_State = State.Stopped; 
            m_InternalLock = new object();
            m_DefaultServiceNames = new ServiceNameStore(); 

            if (Version == UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION.Version20) {
                m_TimeoutManager = new HttpListenerTimeoutManager(this);
            } 

            // default: no CBT checks on any platform (appcompat reasons); applies also to PolicyEnforcement 
            // config element 
            m_ExtendedProtectionPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never);
 
            if (Logging.On) Logging.Exit(Logging.HttpListener, this, "HttpListener", "");
        }

        internal CriticalHandle RequestQueueHandle { 
            get {
                return m_RequestQueueHandle; 
            } 
        }
 
        public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate {
            get {
                AuthenticationSelectorInfo selector = m_AuthenticationDelegate;
                return selector == null ? null : selector.Delegate; 
            }
            set { 
                CheckDisposed(); 

                try 
                {
                    new SecurityPermission(SecurityPermissionFlag.ControlPrincipal).Demand();
                    m_AuthenticationDelegate = new AuthenticationSelectorInfo(value, true);
                } 
                catch (SecurityException exception)
                { 
                    m_SecurityException = exception; 
                    m_AuthenticationDelegate = new AuthenticationSelectorInfo(value, false);
                } 
            }
        }

        public ExtendedProtectionSelector ExtendedProtectionSelectorDelegate 
        {
            get { 
                return m_ExtendedProtectionSelectorDelegate; 
            }
            set { 
                CheckDisposed();
                if (value == null) {
                    throw new ArgumentNullException();
                } 

                if (!AuthenticationManager.OSSupportsExtendedProtection) { 
                    throw new PlatformNotSupportedException(SR.GetString(SR.security_ExtendedProtection_NoOSSupport)); 
                }
 
                m_ExtendedProtectionSelectorDelegate = value;
            }
        }
 
        public AuthenticationSchemes AuthenticationSchemes {
            get { 
                return m_AuthenticationScheme; 
            }
            set { 
                CheckDisposed();

                // Enabling certain schemes requires special permissions.
                if ((value & (AuthenticationSchemes.Digest | AuthenticationSchemes.Negotiate | AuthenticationSchemes.Ntlm)) != 0) 
                {
                    new SecurityPermission(SecurityPermissionFlag.ControlPrincipal).Demand(); 
                } 

                m_AuthenticationScheme = value; 
            }
        }

        public ExtendedProtectionPolicy ExtendedProtectionPolicy 
        {
            get { 
                return m_ExtendedProtectionPolicy; 
            }
            set { 
                CheckDisposed();
                if (value == null)
                {
                    throw new ArgumentNullException("value"); 
                }
                if (!AuthenticationManager.OSSupportsExtendedProtection && value.PolicyEnforcement == PolicyEnforcement.Always) 
                { 
                    throw new PlatformNotSupportedException(SR.GetString(SR.security_ExtendedProtection_NoOSSupport));
                } 
                if (value.CustomChannelBinding != null)
                {
                    throw new ArgumentException(SR.GetString(SR.net_listener_cannot_set_custom_cbt), "CustomChannelBinding");
                } 

                m_ExtendedProtectionPolicy = value; 
            } 
        }
 
        public ServiceNameCollection DefaultServiceNames
        {
            get {
                return m_DefaultServiceNames.ServiceNames; 
            }
        } 
 
        public string Realm {
            get { 
                return m_Realm;
            }
            set {
                CheckDisposed(); 
                m_Realm = value;
            } 
        } 

        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", 
            Justification = "Suppress due to tools issues with unit test infrastructure")]
        private void ValidateV2Property() {
            // Make sure that calling CheckDisposed and SetupV2Config is an atomic operation. This
            // avoids race conditions if the listener is aborted/closed after CheckDisposed(), but 
            // before SetupV2Config().
            lock (m_InternalLock) { 
                CheckDisposed(); 

                if (Version == UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION.Version10) { 
                    throw new PlatformNotSupportedException(SR.GetString(SR.net_listener_v2_only));
                }

                SetupV2Config(); 
            }
        } 
 
        private void SetUrlGroupProperty(UnsafeNclNativeMethods.HttpApi.HTTP_SERVER_PROPERTY property, IntPtr info, uint infosize) {
            uint statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS; 

            GlobalLog.Assert(m_UrlGroupId != 0, "SetUrlGroupProperty called with invalid url group id");
            GlobalLog.Assert(info != IntPtr.Zero, "SetUrlGroupProperty called with invalid pointer");
 
            //
            // Set the url group property using Http Api. 
            // 
            statusCode = UnsafeNclNativeMethods.HttpApi.HttpSetUrlGroupProperty(
                m_UrlGroupId, property, info, infosize); 

            if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) {
                HttpListenerException exception = new HttpListenerException((int)statusCode);
                if (Logging.On) Logging.Exception(Logging.HttpListener, this, "HttpSetUrlGroupProperty:: Property: " + 
                    property, exception);
                throw exception; 
            } 
        }
 
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
            Justification = "Suppress due to tools issues with unit test infrastructure")]
        internal void SetServerTimeout(int[] timeouts, uint minSendRate) {
 
            UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_LIMIT_INFO timeoutinfo =
                new UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_LIMIT_INFO(); 
 
            timeoutinfo.Flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT;
            timeoutinfo.DrainEntityBody = 
                (ushort)timeouts[(int)UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.DrainEntityBody];
            timeoutinfo.EntityBody =
                (ushort)timeouts[(int)UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.EntityBody];
            timeoutinfo.RequestQueue = 
                (ushort)timeouts[(int)UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.RequestQueue];
            timeoutinfo.IdleConnection = 
                (ushort)timeouts[(int)UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.IdleConnection]; 
            timeoutinfo.HeaderWait =
                (ushort)timeouts[(int)UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.HeaderWait]; 
            timeoutinfo.MinSendRate = minSendRate;

            IntPtr infoptr = new IntPtr(&timeoutinfo);
 
            SetUrlGroupProperty(
                UnsafeNclNativeMethods.HttpApi.HTTP_SERVER_PROPERTY.HttpServerTimeoutsProperty, 
                infoptr, (uint)Marshal.SizeOf(typeof(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_LIMIT_INFO))); 

        } 

        //
        // This property is available only when using HTTP API V2. V2 is supported on Vista+.
        // 
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
            Justification = "Suppress due to tools issues with unit test infrastructure")] 
        internal HttpListenerTimeoutManager TimeoutManager { 
            get {
                ValidateV2Property(); 
                GlobalLog.Assert(m_TimeoutManager != null, "Timeout manager is not assigned");
                return m_TimeoutManager;
            }
        } 

        public static bool IsSupported { 
            get { 
                return UnsafeNclNativeMethods.HttpApi.Supported;
            } 
        }

        public bool IsListening {
            get { 
                return m_State==State.Started;
            } 
        } 

        public bool IgnoreWriteExceptions { 
            get {
                return m_IgnoreWriteExceptions;
            }
            set { 
                CheckDisposed();
                m_IgnoreWriteExceptions = value; 
            } 
        }
 
        public bool UnsafeConnectionNtlmAuthentication {
            get {
                return m_UnsafeConnectionNtlmAuthentication;
            } 

            set { 
                CheckDisposed(); 
                if (m_UnsafeConnectionNtlmAuthentication==value) {
                    return; 
                }
                lock (DisconnectResults.SyncRoot)
                {
                    if (m_UnsafeConnectionNtlmAuthentication == value) 
                    {
                        return; 
                    } 
                    m_UnsafeConnectionNtlmAuthentication = value;
                    if (!value) 
                    {
                        foreach (DisconnectAsyncResult result in DisconnectResults.Values)
                        {
                            result.AuthenticatedConnection = null; 
                        }
                    } 
                } 
            }
        } 

        private Hashtable DisconnectResults
        {
            get 
            {
                if (m_DisconnectResults == null) 
                { 
                    lock (m_InternalLock)
                    { 
                        if (m_DisconnectResults == null)
                        {
                            m_DisconnectResults = Hashtable.Synchronized(new Hashtable());
                        } 
                    }
                } 
                return m_DisconnectResults; 
            }
        } 

        internal void AddPrefix(string uriPrefix)
        {
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "AddPrefix", "uriPrefix:" + uriPrefix); 
            string registeredPrefix = null;
            try { 
                if (uriPrefix==null) { 
                    throw new ArgumentNullException("uriPrefix");
                } 
                (new WebPermission(NetworkAccess.Accept, uriPrefix)).Demand();
                CheckDisposed();
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::AddPrefix() uriPrefix:" + uriPrefix);
                int i; 
                if (string.Compare(uriPrefix, 0, "http://", 0, 7, StringComparison.OrdinalIgnoreCase)==0) {
                    i = 7; 
                } 
                else if (string.Compare(uriPrefix, 0, "https://", 0, 8, StringComparison.OrdinalIgnoreCase)==0) {
                    i = 8; 
                }
                else {
                    throw new ArgumentException(SR.GetString(SR.net_listener_scheme), "uriPrefix");
                } 
                bool inSquareBrakets = false;
                int j = i; 
                while (j0) { 
                    if (m_State==State.Started) {
                        foreach (string registeredPrefix in m_UriPrefixes.Values) {
                            // ignore possible failures
                            InternalRemovePrefix(registeredPrefix); 
                        }
                    } 
 
                    if (clear) {
                        m_UriPrefixes.Clear(); 
                        m_DefaultServiceNames.Clear();
                    }
                }
            } finally { 
                if(Logging.On)Logging.Exit(Logging.HttpListener, this, "RemoveAll", "");
            } 
        } 

        private IntPtr DangerousGetHandle() { 
            //
            // Depending upon Http API version used, type-case the request queue handle and get the actual native
            // layer handle.
            // 
            if (Version == UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION.Version20) {
                return ((HttpRequestQueueV2Handle)m_RequestQueueHandle).DangerousGetHandle(); 
            } 
            else {
                return ((SafeCloseHandle)m_RequestQueueHandle).DangerousGetHandle(); 
            }
        }

        [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] 
        internal void EnsureBoundHandle()
        { 
            if (!m_RequestHandleBound) 
            {
                lock (m_InternalLock) 
                {
                    if (!m_RequestHandleBound)
                    {
                        ThreadPool.BindHandle(DangerousGetHandle()); 
                        m_RequestHandleBound = true;
                    } 
                } 
            }
        } 

        private void SetupV2Config() {
            uint statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS;
            ulong id = 0; 

            // 
            // If we have already initialized V2 config, then nothing to do. 
            //
            if (m_V2Initialized) { 
                return;
            }

            // 
            // V2 initialization sequence:
            // 1. Create server session 
            // 2. Create url group 
            // 3. Create request queue - Done in Start()
            // 4. Add urls to url group - Done in Start() 
            // 5. Attach request queue to url group - Done in Start()
            //

            try { 
                statusCode = UnsafeNclNativeMethods.HttpApi.HttpCreateServerSession(
                    UnsafeNclNativeMethods.HttpApi.Version, &id, 0); 
 
                if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) {
                    throw new HttpListenerException((int)statusCode); 
                }

                GlobalLog.Assert(id != 0, "Invalid id returned by HttpCreateServerSession");
 
                m_ServerSessionHandle = new HttpServerSessionHandle(id);
 
                id = 0; 
                statusCode = UnsafeNclNativeMethods.HttpApi.HttpCreateUrlGroup(
                    m_ServerSessionHandle.DangerousGetServerSessionId(), &id, 0); 

                if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) {
                    throw new HttpListenerException((int)statusCode);
                } 

                GlobalLog.Assert(id != 0, "Invalid id returned by HttpCreateUrlGroup"); 
                m_UrlGroupId = id; 

                m_V2Initialized = true; 
            }
            catch (Exception exception) {
                //
                // If V2 initialization fails, we mark object as unusable. 
                //
                m_State = State.Closed; 
 
                //
                // If Url group or request queue creation failed, close server session before throwing. 
                //
                if (m_ServerSessionHandle != null) {
                    m_ServerSessionHandle.Close();
                } 
                if (Logging.On) Logging.Exception(Logging.HttpListener, this, "SetupV2Config", exception);
                throw; 
            } 
        }
 
        public void Start() {
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Start", "");

            // Make sure there are no race conditions between Start/Stop/Abort/Close/Dispose and 
            // calls to SetupV2Config: Start needs to setup all resources (esp. in V2 where besides
            // the request handle, there is also a server session and a Url group. Abort/Stop must 
            // not interfere while Start is allocating those resources. The lock also makes sure 
            // all methods changing state can read and change the state in an atomic way.
            lock (m_InternalLock) { 
                try {
                    CheckDisposed();
                    if (m_State==State.Started) {
                        return; 
                    }
 
                    if (Version == UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION.Version20) { 
                        // SetupV2Config() is not called in the ctor, because it may throw. This would
                        // be a regression since in v1 the ctor never threw. Besides, ctors should do 
                        // minimal work according to the framework design guidelines.
                        SetupV2Config();
                        CreateRequestQueueHandle();
                        AttachRequestQueueToUrlGroup(); 
                    }
                    else { 
                        CreateRequestQueueHandle(); 
                    }
 
                    // All resources are set up correctly. Now add all prefixes.
                    try {
                        AddAllPrefixes();
                    } 
                    catch (HttpListenerException) {
                        // If an error occured while adding prefixes, free all resources allocated by previous steps. 
                        if (Version == UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION.Version20) { 
                            DetachRequestQueueFromUrlGroup();
                        } 
                        ClearDigestCache();
                        throw;
                    }
 
                    m_State = State.Started;
                } catch (Exception exception) { 
                    // Make sure the HttpListener instance can't be used if Start() failed. 
                    m_State = State.Closed;
                    CloseRequestQueueHandle(); 
                    CleanupV2Config();
                    if (Logging.On) Logging.Exception(Logging.HttpListener, this, "Start", exception);
                    throw;
                } finally { 
                    if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Start", "");
                } 
            } 
        }
 
        private void CleanupV2Config() {

            //
            // If we never setup V2, just return. 
            //
            if (!m_V2Initialized) { 
                return; 
            }
 
            //
            // V2 stopping sequence:
            // 1. Detach request queue from url group - Done in Stop()/Abort()
            // 2. Remove urls from url group - Done in Stop() 
            // 3. Close request queue - Done in Stop()/Abort()
            // 4. Close Url group. 
            // 5. Close server session. 

            GlobalLog.Assert(m_UrlGroupId != 0, "HttpCloseUrlGroup called with invalid url group id"); 

            uint statusCode = UnsafeNclNativeMethods.HttpApi.HttpCloseUrlGroup(m_UrlGroupId);

            if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { 
                if (Logging.On) Logging.PrintError(Logging.HttpListener, this, "CloseV2Config", SR.GetString(SR.net_listener_close_urlgroup_error, statusCode));
            } 
            m_UrlGroupId = 0; 

            GlobalLog.Assert(m_ServerSessionHandle != null, "ServerSessionHandle is null in CloseV2Config"); 
            GlobalLog.Assert(!m_ServerSessionHandle.IsInvalid, "ServerSessionHandle is invalid in CloseV2Config");

            m_ServerSessionHandle.Close();
        } 

        private void AttachRequestQueueToUrlGroup() { 
            // 
            // Set the association between request queue and url group. After this, requests for registered urls will
            // get delivered to this request queue. 
            //
            UnsafeNclNativeMethods.HttpApi.HTTP_BINDING_INFO info = new UnsafeNclNativeMethods.HttpApi.HTTP_BINDING_INFO();
            info.Flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT;
            info.RequestQueueHandle = DangerousGetHandle(); 

            IntPtr infoptr = new IntPtr(&info); 
 
            SetUrlGroupProperty(UnsafeNclNativeMethods.HttpApi.HTTP_SERVER_PROPERTY.HttpServerBindingProperty,
                infoptr, (uint)Marshal.SizeOf(typeof(UnsafeNclNativeMethods.HttpApi.HTTP_BINDING_INFO))); 
        }

        private void DetachRequestQueueFromUrlGroup() {
            GlobalLog.Assert(m_UrlGroupId != 0, "DetachRequestQueueFromUrlGroup can't detach using Url group id 0."); 

            // 
            // Break the association between request queue and url group. After this, requests for registered urls 
            // will get 503s.
            // Note that this method may be called multiple times (Stop() and then Abort()). This 
            // is fine since http.sys allows to set HttpServerBindingProperty multiple times for valid
            // Url groups.
            //
            UnsafeNclNativeMethods.HttpApi.HTTP_BINDING_INFO info = new UnsafeNclNativeMethods.HttpApi.HTTP_BINDING_INFO(); 
            info.Flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE;
            info.RequestQueueHandle = IntPtr.Zero; 
 
            IntPtr infoptr = new IntPtr(&info);
 
            uint statusCode = UnsafeNclNativeMethods.HttpApi.HttpSetUrlGroupProperty(m_UrlGroupId,
                UnsafeNclNativeMethods.HttpApi.HTTP_SERVER_PROPERTY.HttpServerBindingProperty,
                infoptr, (uint)Marshal.SizeOf(typeof(UnsafeNclNativeMethods.HttpApi.HTTP_BINDING_INFO)));
 
            if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) {
                if (Logging.On) Logging.PrintError(Logging.HttpListener, this, "DetachRequestQueueFromUrlGroup", SR.GetString(SR.net_listener_detach_error, statusCode)); 
            } 
        }
 
        public void Stop() {
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Stop", "");
            try {
                lock (m_InternalLock) { 
                    CheckDisposed();
                    if (m_State==State.Stopped) { 
                        return; 
                    }
 
                    RemoveAll(false);
                    if (Version == UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION.Version20) {
                        DetachRequestQueueFromUrlGroup();
                    } 

                    // Even though it would be enough to just detach the request queue in v2, in order to 
                    // keep app compat with earlier versions of the framework, we need to close the request queue. 
                    // This will make sure that pending GetContext() calls will complete and throw an exception. Just
                    // detaching the url group from the request queue would not cause GetContext() to return. 
                    CloseRequestQueueHandle();

                    m_State = State.Stopped;
                } 

                ClearDigestCache(); 
            } catch (Exception exception) { 
                if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Stop", exception);
                throw; 
            } finally {
                if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Stop", "");
            }
        } 

        private unsafe void CreateRequestQueueHandle() { 
            uint statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS; 

            // 
            // Depending upon version, use appropriate API to create Http request queue.
            //
            if (Version == UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION.Version20) {
                HttpRequestQueueV2Handle requestQueueHandle = null; 
                statusCode =
                    UnsafeNclNativeMethods.SafeNetHandles.HttpCreateRequestQueue( 
                        UnsafeNclNativeMethods.HttpApi.Version, null, null, 0, out requestQueueHandle); 

                if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { 
                    throw new HttpListenerException((int)statusCode);
                }
                m_RequestQueueHandle = requestQueueHandle;
            } 
            else {
                SafeCloseHandle requestQueueHandle = null; 
                GlobalLog.Print("SafeCloseHandle::CreateRequestQueueHandle() calling UnsafeNclNativeMethods.HttpApi.HttpCreateHttpHandle"); 

                statusCode = UnsafeNclNativeMethods.SafeNetHandles.HttpCreateHttpHandle(out requestQueueHandle, 0); 

                GlobalLog.Print("SafeCloseHandle::CreateRequestQueueHandle() call to UnsafeNclNativeMethods.HttpApi.HttpCreateHttpHandle returned:" + statusCode);

                if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { 
                    throw new HttpListenerException((int)statusCode);
                } 
                m_RequestQueueHandle = requestQueueHandle; 
            }
        } 

        private unsafe void CloseRequestQueueHandle() {

            if ((m_RequestQueueHandle != null) && (!m_RequestQueueHandle.IsInvalid)) { 
                m_RequestQueueHandle.Close();
                m_RequestHandleBound = false; 
            } 
        }
 
        public void Abort() {
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Abort", "");
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::Abort()");
 
            lock (m_InternalLock) {
                try { 
                    if (m_State == State.Closed) { 
                        return;
                    } 

                    if (Version == UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION.Version20) {
                        // Just detach and free resources. Don't call Stop (which may throw). Behave like v1: just
                        // clean up resources. 
                        if (m_State == State.Started) {
                            DetachRequestQueueFromUrlGroup(); 
                            CloseRequestQueueHandle(); 
                        }
                        CleanupV2Config(); 
                    }
                    else {
                        if (m_RequestQueueHandle != null) {
                            ((SafeCloseHandle)m_RequestQueueHandle).Abort(); 
                            m_RequestHandleBound = false;
                        } 
                    } 
                    ClearDigestCache();
                } catch (Exception exception) { 
                    if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Abort", exception);
                    throw;
                } finally {
                    m_State = State.Closed; 
                    if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Abort", "");
                } 
            } 
        }
 
        public void Close() {
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Close", "");
            try {
                GlobalLog.Print("HttpListenerRequest::Close()"); 
                ((IDisposable)this).Dispose();
            } catch (Exception exception) { 
                if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Close", exception); 
                throw;
            } finally { 
                if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Close", "");
            }
        }
 
        // old API, now private, and helper methods
        private void Dispose(bool disposing) { 
            GlobalLog.Assert(disposing, "Dispose(bool) does nothing if called from the finalizer."); 

            if (!disposing) { 
                return;
            }

            if (Logging.On) Logging.Enter(Logging.HttpListener, this, "Dispose", ""); 
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::Dispose()");
 
            lock (m_InternalLock) { 
                try {
                    if (m_State == State.Closed){ 
                        return;
                    }

                    Stop(); 
                    if (Version == UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION.Version20) {
                        CleanupV2Config(); 
                    } 
                } catch (Exception exception) {
                    if (Logging.On) Logging.Exception(Logging.HttpListener, this, "Dispose", exception); 
                    throw;
                } finally {
                    m_State = State.Closed;
                    if (Logging.On) Logging.Exit(Logging.HttpListener, this, "Dispose", ""); 
                }
            } 
        } 

        ///  
        void IDisposable.Dispose() {
            Dispose(true);
        }
 
        private uint InternalAddPrefix(string uriPrefix) {
            uint statusCode = 0; 
 
            if (Version == UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION.Version20) {
                statusCode = 
                    UnsafeNclNativeMethods.HttpApi.HttpAddUrlToUrlGroup(
                        m_UrlGroupId,
                        uriPrefix,
                        0, 
                        0);
            } 
            else { 
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::InternalAddPrefix() uriPrefix:" + uriPrefix + " calling UnsafeNclNativeMethods.HttpApi.HttpAddUrl");
 
                statusCode =
                    UnsafeNclNativeMethods.HttpApi.HttpAddUrl(
                        m_RequestQueueHandle,
                        uriPrefix, 
                        null);
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::InternalAddPrefix() call to UnsafeNclNativeMethods.HttpApi.HttpAddUrl returned:" + statusCode); 
            } 

            return statusCode; 
        }

        private bool InternalRemovePrefix(string uriPrefix) {
            uint statusCode = 0; 

            if (Version == UnsafeNclNativeMethods.HttpApi.HTTP_API_VERSION.Version20) { 
                statusCode = 
                    UnsafeNclNativeMethods.HttpApi.HttpRemoveUrlFromUrlGroup(
                        m_UrlGroupId, 
                        uriPrefix,
                        0);
            }
            else { 
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::InternalRemovePrefix() uriPrefix:" + uriPrefix + " calling UnsafeNclNativeMethods.HttpApi.HttpRemoveUrl");
                statusCode = 
                   UnsafeNclNativeMethods.HttpApi.HttpRemoveUrl( 
                        m_RequestQueueHandle,
                        uriPrefix); 

                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::InternalRemovePrefix() call to UnsafeNclNativeMethods.HttpApi.HttpRemoveUrl returned:" + statusCode);
            }
 
            if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND)
            { 
                return false; 
            }
            return true; 
        }

        private void AddAllPrefixes() {
            // go through the uri list and register for each one of them 
            if (m_UriPrefixes.Count>0) {
                foreach (string registeredPrefix in m_UriPrefixes.Values) { 
                    uint statusCode = InternalAddPrefix(registeredPrefix); 
                    if (statusCode!=UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) {
                        if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_ALREADY_EXISTS) 
                            throw new HttpListenerException((int)statusCode, SR.GetString(SR.net_listener_already, registeredPrefix));
                        else
                            throw new HttpListenerException((int)statusCode);
                    } 
                }
            } 
        } 

        public HttpListenerContext GetContext() { 
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "GetContext", "");

            SyncRequestContext memoryBlob = null;
            HttpListenerContext httpContext = null; 
            bool stoleBlob = false;
 
            try { 
                CheckDisposed();
                if (m_State==State.Stopped) { 
                    throw new InvalidOperationException(SR.GetString(SR.net_listener_mustcall, "Start()"));
                }
                if (m_UriPrefixes.Count==0) {
                    throw new InvalidOperationException(SR.GetString(SR.net_listener_mustcall, "AddPrefix()")); 
                }
                uint statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS; 
                uint size = 4096; 
                ulong requestId = 0;
                memoryBlob = new SyncRequestContext((int) size); 
                for (;;) {
                    for (;;) {
                        GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::GetContext() calling UnsafeNclNativeMethods.HttpApi.HttpReceiveHttpRequest RequestId:" + requestId);
                        uint bytesTransferred = 0; 
                        statusCode =
                            UnsafeNclNativeMethods.HttpApi.HttpReceiveHttpRequest( 
                                m_RequestQueueHandle, 
                                requestId,
                                (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY, 
                                memoryBlob.RequestBlob,
                                size,
                                &bytesTransferred,
                                null); 

                        GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::GetContext() call to UnsafeNclNativeMethods.HttpApi.HttpReceiveHttpRequest returned:" + statusCode); 
 
                        if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_PARAMETER && requestId != 0) {
                            // we might get this if somebody stole our RequestId, 
                            // we need to start all over again but we can reuse the buffer we just allocated
                            requestId = 0;
                            continue;
                        } 
                        else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
                        { 
                            // the buffer was not big enough to fit the headers, we need 
                            // to read the RequestId returned, allocate a new buffer of the required size
                            size = bytesTransferred; 
                            requestId = memoryBlob.RequestBlob->RequestId;
                            memoryBlob.Reset(checked((int) size));
                            continue;
                        } 
                        break;
                    } 
                    if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) 
                    {
                        // someother bad error, possible(?) return values are: 
                        // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED
                        throw new HttpListenerException((int)statusCode);
                    }
 
                    // We need to hook up our authentication handling code here.
                    httpContext = HandleAuthentication(memoryBlob, out stoleBlob); 
                    if (stoleBlob) 
                    {
                        // The request has been handed to the user, which means this code can't reuse the blob.  Reset it here. 
                        memoryBlob = null;
                        stoleBlob = false;
                    }
                    GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::GetContext() HandleAuthentication() returned httpContext#" + ValidationHelper.HashString(httpContext)); 
                    // if the request survived authentication, return it to the user
                    if (httpContext!=null) { 
                        return httpContext; 
                    }
 
                    // HandleAuthentication may have cleaned this up.
                    if (memoryBlob == null)
                    {
                        memoryBlob = new SyncRequestContext(checked((int) size)); 
                    }
 
                    requestId = 0; 
                }
            } catch (Exception exception) { 
                if(Logging.On)Logging.Exception(Logging.HttpListener, this, "GetContext", exception);
                throw;
            } finally {
                if (memoryBlob != null && !stoleBlob) 
                {
                    memoryBlob.ReleasePins(); 
                    memoryBlob.Close(); 
                }
                if (Logging.On) Logging.Exit(Logging.HttpListener, this, "GetContext", "HttpListenerContext#" + ValidationHelper.HashString(httpContext) + " RequestTraceIdentifier#" + (httpContext != null ? httpContext.Request.RequestTraceIdentifier.ToString() : "")); 
            }
        }

        [HostProtection(ExternalThreading=true)] 
        public IAsyncResult BeginGetContext(AsyncCallback callback, object state) {
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "BeginGetContext", ""); 
            ListenerAsyncResult asyncResult = null; 
            try {
                CheckDisposed(); 
                if (m_State==State.Stopped) {
                    throw new InvalidOperationException(SR.GetString(SR.net_listener_mustcall, "Start()"));
                }
                // prepare the ListenerAsyncResult object (this will have it's own 
                // event that the user can wait on for IO completion - which means we
                // need to signal it when IO completes) 
                asyncResult = new ListenerAsyncResult(this, state, callback); 
                uint statusCode = asyncResult.QueueBeginGetContext();
                if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && 
                    statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING)
                {
                    // someother bad error, possible(?) return values are:
                    // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED 
                    throw new HttpListenerException((int)statusCode);
                } 
            } catch (Exception exception) { 
                if(Logging.On)Logging.Exception(Logging.HttpListener, this, "BeginGetContext", exception);
                throw; 
            } finally {
                if(Logging.On)Logging.Enter(Logging.HttpListener, this, "BeginGetContext", "IAsyncResult#" + ValidationHelper.HashString(asyncResult));
            }
 
            return asyncResult;
        } 
 
        public HttpListenerContext EndGetContext(IAsyncResult asyncResult) {
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "EndGetContext", "IAsyncResult#" + ValidationHelper.HashString(asyncResult)); 
            HttpListenerContext httpContext = null;
            try {
                CheckDisposed();
                if (asyncResult==null) { 
                    throw new ArgumentNullException("asyncResult");
                } 
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::EndGetContext() asyncResult:" + ValidationHelper.ToString(asyncResult)); 
                ListenerAsyncResult castedAsyncResult = asyncResult as ListenerAsyncResult;
                if (castedAsyncResult==null || castedAsyncResult.AsyncObject!=this) { 
                    throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult");
                }
                if (castedAsyncResult.EndCalled) {
                    throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndGetContext")); 
                }
                castedAsyncResult.EndCalled = true; 
                httpContext = castedAsyncResult.InternalWaitForCompletion() as HttpListenerContext; 
                if (httpContext == null) {
                    GlobalLog.Assert(castedAsyncResult.Result is Exception, "EndGetContext|The result is neither a HttpListenerContext nor an Exception."); 
                    throw castedAsyncResult.Result as Exception;
                }
            } catch (Exception exception) {
                if(Logging.On)Logging.Exception(Logging.HttpListener, this, "EndGetContext", exception); 
                throw;
            } finally { 
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::EndGetContext() returning HttpListenerContext#" + ValidationHelper.ToString(httpContext)); 
                if(Logging.On)Logging.Exit(Logging.HttpListener, this, "EndGetContext", httpContext == null ? "" : "HttpListenerContext#" + ValidationHelper.HashString(httpContext) + " RequestTraceIdentifier#" + httpContext.Request.RequestTraceIdentifier);
            } 
            return httpContext;
        }

        [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)] 
        [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.ControlPrincipal)]
        private WindowsIdentity CreateWindowsIdentity(IntPtr userToken, string type, WindowsAccountType acctType, bool isAuthenticated) 
        { 
            return new WindowsIdentity(userToken, type, acctType, isAuthenticated);
        } 

        internal HttpListenerContext HandleAuthentication(RequestContextBase memoryBlob, out bool stoleBlob)
        {
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() memoryBlob:0x" + ((IntPtr) memoryBlob.RequestBlob).ToString("x")); 

            string challenge = null; 
            stoleBlob = false; 

            // Some things we need right away.  Lift them out now while it's convenient. 
            string verb = UnsafeNclNativeMethods.HttpApi.GetVerb(memoryBlob.RequestBlob);
            string authorizationHeader = UnsafeNclNativeMethods.HttpApi.GetKnownHeader(memoryBlob.RequestBlob, (int) HttpRequestHeader.Authorization);
            ulong connectionId = memoryBlob.RequestBlob->ConnectionId;
            ulong requestId = memoryBlob.RequestBlob->RequestId; 
            bool isSecureConnection = memoryBlob.RequestBlob->pSslInfo != null;
 
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() authorizationHeader:" + ValidationHelper.ToString(authorizationHeader)); 

            // if the app has turned on AuthPersistence, an anonymous request might 
            // be authenticated by virtue of it coming on a connection that was
            // previously authenticated.
            // assurance that we do this only for NTLM/Negotiate is not here, but in the
            // code that caches WindowsIdentity instances in the Dictionary. 
            DisconnectAsyncResult disconnectResult = (DisconnectAsyncResult) DisconnectResults[connectionId];
            if (UnsafeConnectionNtlmAuthentication) 
            { 
                if (authorizationHeader == null)
                { 
                    WindowsPrincipal principal = disconnectResult == null ? null : disconnectResult.AuthenticatedConnection;
                    if (principal != null)
                    {
                        GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() got principal:" + ValidationHelper.ToString(principal) + " principal.Identity.Name:" + ValidationHelper.ToString(principal.Identity.Name) + " creating request"); 
                        stoleBlob = true;
                        HttpListenerContext ntlmContext = new HttpListenerContext(this, memoryBlob); 
                        ntlmContext.SetIdentity(principal, null); 
                        ntlmContext.Request.ReleasePins();
                        return ntlmContext; 
                    }
                }
                else
                { 
                    // They sent an authorization - destroy their previous credentials.
                    GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() clearing principal cache"); 
                    if (disconnectResult != null) 
                    {
                        disconnectResult.AuthenticatedConnection = null; 
                    }
                }
            }
 
            // Figure out what schemes we're allowing, what context we have.
            stoleBlob = true; 
            HttpListenerContext httpContext = null; 
            NTAuthentication oldContext = null;
            NTAuthentication newContext = null; 
            NTAuthentication context = null;
            AuthenticationSchemes headerScheme = AuthenticationSchemes.None;
            AuthenticationSchemes authenticationScheme = AuthenticationSchemes;
            ExtendedProtectionPolicy extendedProtectionPolicy = m_ExtendedProtectionPolicy; 
            try
            { 
                // Take over handling disconnects for now. 
                if (disconnectResult != null && !disconnectResult.StartOwningDisconnectHandling())
                { 
                    // Oops!  Just disconnected just then.  Pretend we didn't see the disconnectResult.
                    disconnectResult = null;
                }
 
                // Pick out the old context now.  By default, it'll be removed in the finally, unless context is set somewhere.
                if (disconnectResult != null) 
                { 
                    oldContext = disconnectResult.Session;
                } 

                httpContext = new HttpListenerContext(this, memoryBlob);

                AuthenticationSelectorInfo authenticationSelector = m_AuthenticationDelegate; 
                if (authenticationSelector != null)
                { 
                    try 
                    {
                        httpContext.Request.ReleasePins(); 
                        authenticationScheme = authenticationSelector.Delegate(httpContext.Request);
                        if (!authenticationSelector.AdvancedAuth &&
                            (authenticationScheme & (AuthenticationSchemes.Negotiate | AuthenticationSchemes.Ntlm | AuthenticationSchemes.Digest)) != 0)
                        { 
                            throw m_SecurityException;
                        } 
                        GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() AuthenticationSchemeSelectorDelegate() returned authenticationScheme:" + authenticationScheme); 
                    }
                    catch (Exception exception) 
                    {
                        if (NclUtilities.IsFatal(exception)) throw;

                        if (Logging.On) Logging.PrintError(Logging.HttpListener, this, "HandleAuthentication", SR.GetString(SR.net_log_listener_delegate_exception, exception)); 
                        GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() AuthenticationSchemeSelectorDelegate() returned authenticationScheme:" + authenticationScheme);
                        SendError(requestId, HttpStatusCode.InternalServerError, null); 
                        httpContext.Close(); 
                        return null;
                    } 
                }
                else
                {
                    // We didn't give the request to the user yet, so we haven't lost control of the unmanaged blob and can 
                    // continue to reuse the buffer.
                    stoleBlob = false; 
                } 

                ExtendedProtectionSelector extendedProtectionSelector = m_ExtendedProtectionSelectorDelegate; 
                if (extendedProtectionSelector != null)
                {
                    extendedProtectionPolicy = extendedProtectionSelector(httpContext.Request);
 
                    if (extendedProtectionPolicy == null)
                    { 
                        extendedProtectionPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never); 
                    }
                } 

                // Then figure out what scheme they're trying (if any are allowed)
                int index = -1;
                if (authorizationHeader != null && (authenticationScheme & ~AuthenticationSchemes.Anonymous) != AuthenticationSchemes.None) 
                {
                    // Find the end of the scheme name.  Trust that HTTP.SYS parsed out just our header ok. 
                    for (index = 0; index < authorizationHeader.Length; index++) 
                    {
                        if (authorizationHeader[index] == ' ' || authorizationHeader[index] == '\t' || 
                            authorizationHeader[index] == '\r' || authorizationHeader[index] == '\n')
                        {
                            break;
                        } 
                    }
 
                    // Currently only allow one Authorization scheme/header per request. 
                    if (index < authorizationHeader.Length)
                    { 
                        if ((authenticationScheme & AuthenticationSchemes.Negotiate) != AuthenticationSchemes.None &&
                            string.Compare(authorizationHeader, 0, NegotiateClient.AuthType, 0, index, StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            headerScheme = AuthenticationSchemes.Negotiate; 
                        }
                        else if ((authenticationScheme & AuthenticationSchemes.Ntlm) != AuthenticationSchemes.None && 
                            string.Compare(authorizationHeader, 0, NtlmClient.AuthType, 0, index, StringComparison.OrdinalIgnoreCase) == 0) 
                        {
                            headerScheme = AuthenticationSchemes.Ntlm; 
                        }
                        else if ((authenticationScheme & AuthenticationSchemes.Digest) != AuthenticationSchemes.None &&
                            string.Compare(authorizationHeader, 0, DigestClient.AuthType, 0, index, StringComparison.OrdinalIgnoreCase) == 0)
                        { 
                            headerScheme = AuthenticationSchemes.Digest;
                        } 
                        else if ((authenticationScheme & AuthenticationSchemes.Basic) != AuthenticationSchemes.None && 
                            string.Compare(authorizationHeader, 0, BasicClient.AuthType, 0, index, StringComparison.OrdinalIgnoreCase) == 0)
                        { 
                            headerScheme = AuthenticationSchemes.Basic;
                        }
                        else
                        { 
                            if (Logging.On) Logging.PrintWarning(Logging.HttpListener, this, "HandleAuthentication", SR.GetString(SR.net_log_listener_unsupported_authentication_scheme, authorizationHeader , authenticationScheme));
                        } 
                    } 
                }
 
                // httpError holds the error we will return if an Authorization header is present but can't be authenticated
                HttpStatusCode httpError = HttpStatusCode.InternalServerError;
                bool error = false;
 
                // See if we found an acceptable auth header
                if (headerScheme == AuthenticationSchemes.None) 
                { 
                    if (Logging.On) Logging.PrintWarning(Logging.HttpListener, this, "HandleAuthentication", SR.GetString(SR.net_log_listener_unmatched_authentication_scheme, ValidationHelper.ToString(authenticationScheme), (authorizationHeader == null ? "" : authorizationHeader)));
 
                    // If anonymous is allowed, just return the context.  Otherwise go for the 401.
                    if ((authenticationScheme & AuthenticationSchemes.Anonymous) != AuthenticationSchemes.None)
                    {
                        if (!stoleBlob) 
                        {
                            stoleBlob = true; 
                            httpContext.Request.ReleasePins(); 
                        }
                        return httpContext; 
                    }

                    httpError = HttpStatusCode.Unauthorized;
                    httpContext.Request.DetachBlob(memoryBlob); 
                    httpContext.Close();
                    httpContext = null; 
                } 
                else
                { 
                    // Perform Authentication
                    byte[] bytes = null;
                    byte[] decodedOutgoingBlob = null;
                    string outBlob = null; 

                    // Find the beginning of the blob.  Trust that HTTP.SYS parsed out just our header ok. 
                    for (index++; index < authorizationHeader.Length; index++) 
                    {
                        if (authorizationHeader[index] != ' ' && authorizationHeader[index] != '\t' && 
                            authorizationHeader[index] != '\r' && authorizationHeader[index] != '\n')
                        {
                            break;
                        } 
                    }
                    string inBlob = index < authorizationHeader.Length ? authorizationHeader.Substring(index) : ""; 
 
                    IPrincipal principal = null;
                    SecurityStatus statusCodeNew; 
                    ChannelBinding binding;
                    GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() Performing Authentication headerScheme:" + ValidationHelper.ToString(headerScheme));
                    switch (headerScheme)
                    { 
                        case AuthenticationSchemes.Digest:
                            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() package:WDigest headerScheme:" + headerScheme); 
 
                            // WDigest had some weird behavior.  This is what I have discovered:
                            // Local accounts don't work, only domain accounts.  The domain (i.e. REDMOND) is implied.  Not sure how it is chosen. 
                            // If the domain is specified and the credentials are correct, it works.  If they're not (domain, username or password):
                            //      AcceptSecurityContext (GetOutgoingDigestBlob) returns success but with a bogus 4k challenge, and
                            //      QuerySecurityContextToken (GetContextToken) fails with NoImpersonation.
                            // If the domain isn't specified, AcceptSecurityContext returns NoAuthenticatingAuthority for a bad username, 
                            // and LogonDenied for a bad password.
 
                            // Also interesting is that WDigest requires us to keep a reference to the previous context, but fails if we 
                            // actually pass it in!  (It't ok to pass it in for the first request, but not if nc > 1.)  For Whidbey,
                            // we create a new context and associate it with the connection, just like NTLM, but instead of using it for 
                            // the next request on the connection, we always create a new context and swap the old one out.  As long
                            // as we keep the old one around until after we authenticate with the new one, it works.  For this reason,
                            // we also keep these contexts around past the lifetime of the connection, so that KeepAlive=false works.
                            binding = GetChannelBinding(connectionId, isSecureConnection, extendedProtectionPolicy); 

                            context = new NTAuthentication(true, NegotiationInfoClass.WDigest, null, 
                                GetContextFlags(extendedProtectionPolicy, isSecureConnection), binding); 

                            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() verb:" + verb + " context.IsValidContext:" + context.IsValidContext.ToString()); 

                            outBlob = context.GetOutgoingDigestBlob(inBlob, verb, null, Realm, false, false, out statusCodeNew);
                            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() GetOutgoingDigestBlob() returned IsCompleted:" + context.IsCompleted + " statusCodeNew:" + statusCodeNew + " outBlob:[" + outBlob + "]");
 
                            // WDigest bug: sometimes when AcceptSecurityContext returns success, it provides a bogus, empty 4k buffer.
                            // Ignore it.  (Should find out what's going on here from WDigest people.) 
                            if (statusCodeNew == SecurityStatus.OK) 
                            {
                                outBlob = null; 
                            }

                            if (context.IsValidContext)
                            { 
                                SafeCloseHandle userContext = null;
                                try 
                                { 
                                    if (!CheckSpn(context, isSecureConnection, extendedProtectionPolicy)) {
                                        httpError = HttpStatusCode.Unauthorized; 
                                    }
                                    else {
                                        httpContext.Request.ServiceName = context.ClientSpecifiedSpn;
 
                                        userContext = context.GetContextToken(out statusCodeNew);
                                        GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() GetContextToken() returned:" + statusCodeNew.ToString()); 
                                        if (statusCodeNew != SecurityStatus.OK) 
                                        {
                                             httpError = HttpStatusFromSecurityStatus(statusCodeNew); 
                                        }
                                        else if (userContext == null)
                                        {
                                            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() error: GetContextToken() returned:null statusCodeNew:" + statusCodeNew.ToString()); 
                                            httpError = HttpStatusCode.Unauthorized;
                                        } 
                                        else 
                                        {
                                            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() creating new WindowsIdentity() from userContext:" + userContext.DangerousGetHandle().ToString("x8")); 
                                            principal = new WindowsPrincipal(CreateWindowsIdentity(userContext.DangerousGetHandle(), DigestClient.AuthType, WindowsAccountType.Normal, true));
                                        }
                                    }
                                } 
                                finally {
                                    if (userContext!=null) { 
                                        userContext.Close(); 
                                    }
                                } 

                                newContext = context;

                                if (outBlob != null) 
                                {
                                    challenge = DigestClient.AuthType + " " + outBlob; 
                                } 
                            }
                            else 
                            {
                                httpError = HttpStatusFromSecurityStatus(statusCodeNew);
                            }
                            break; 

                        case AuthenticationSchemes.Negotiate: 
                        case AuthenticationSchemes.Ntlm: 
                            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() headerScheme:" + headerScheme);
                            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() returned context#" + ValidationHelper.HashString(oldContext) + " for connectionId:" + connectionId); 

                            string package = headerScheme == AuthenticationSchemes.Ntlm ? NtlmClient.AuthType : NegotiateClient.AuthType;
                            if (oldContext != null && oldContext.Package == package)
                            { 
                                context = oldContext;
                            } 
                            else 
                            {
                                binding = GetChannelBinding(connectionId, isSecureConnection, extendedProtectionPolicy); 

                                context = new NTAuthentication(true, package, null,
                                    GetContextFlags(extendedProtectionPolicy, isSecureConnection), binding);
                            } 

                            try 
                            { 
                                bytes = Convert.FromBase64String(inBlob);
                            } 
                            catch (FormatException)
                            {
                                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() FromBase64String threw a FormatException.");
                                httpError = HttpStatusCode.BadRequest; 
                                error = true;
                            } 
                            if (!error) 
                            {
                                decodedOutgoingBlob = context.GetOutgoingBlob(bytes, false, out statusCodeNew); 
                                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() GetOutgoingBlob() returned IsCompleted:" + context.IsCompleted + " statusCodeNew:" + statusCodeNew);
                                error = !context.IsValidContext;
                                if (error)
                                { 
                                    // Bug #474228: SSPI Workaround
                                    // If a client sends up a blob on the initial request, Negotiate returns SEC_E_INVALID_HANDLE 
                                    // when it should return SEC_E_INVALID_TOKEN. 
                                    if (statusCodeNew == SecurityStatus.InvalidHandle && oldContext == null && bytes != null && bytes.Length > 0)
                                    { 
                                        statusCodeNew = SecurityStatus.InvalidToken;
                                    }

                                    httpError = HttpStatusFromSecurityStatus(statusCodeNew); 
                                }
                            } 
 
                            if (decodedOutgoingBlob!=null) {
                                outBlob = Convert.ToBase64String(decodedOutgoingBlob); 
                            }

                            if (!error)
                            { 
                                if (context.IsCompleted) {
                                    SafeCloseHandle userContext = null; 
                                    try 
                                    {
                                        if (!CheckSpn(context, isSecureConnection, extendedProtectionPolicy)) { 
                                            httpError = HttpStatusCode.Unauthorized;
                                        }
                                        else {
                                            httpContext.Request.ServiceName = context.ClientSpecifiedSpn; 

                                            userContext = context.GetContextToken(out statusCodeNew); 
                                            if (statusCodeNew != SecurityStatus.OK) 
                                            {
                                                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() GetContextToken() failed with statusCodeNew:" + statusCodeNew.ToString()); 
                                                httpError = HttpStatusFromSecurityStatus(statusCodeNew);
                                            }
                                            else
                                            { 
                                                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() creating new WindowsIdentity() from userContext:" + userContext.DangerousGetHandle().ToString("x8"));
                                                WindowsPrincipal windowsPrincipal = new WindowsPrincipal(CreateWindowsIdentity(userContext.DangerousGetHandle(), context.ProtocolName, WindowsAccountType.Normal, true)); 
                                                principal = windowsPrincipal; 

                                                // if appropriate, cache this credential on this connection 
                                                if (UnsafeConnectionNtlmAuthentication && context.ProtocolName == NegotiationInfoClass.NTLM)
                                                {
                                                    GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() inserting principal#" + ValidationHelper.HashString(principal) + " for connectionId:" + connectionId);
 
                                                    // We may need to call WaitForDisconnect.
                                                    if (disconnectResult == null) 
                                                    { 
                                                        RegisterForDisconnectNotification(connectionId, ref disconnectResult);
                                                    } 
                                                    if (disconnectResult != null)
                                                    {
                                                        lock (DisconnectResults.SyncRoot)
                                                        { 
                                                            if (UnsafeConnectionNtlmAuthentication)
                                                            { 
                                                                disconnectResult.AuthenticatedConnection = windowsPrincipal; 
                                                            }
                                                        } 
                                                    }
                                                    else
                                                    {
                                                        // Registration failed - UnsafeConnectionNtlmAuthentication ignored. 
                                                        GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() RegisterForDisconnectNotification() failed.");
                                                    } 
                                                } 
                                            }
                                        } 
                                    }
                                    finally {
                                        if (userContext!=null) {
                                            userContext.Close(); 
                                        }
                                    } 
                                } 
                                else
                                { 
                                    // auth incomplete
                                    newContext = context;

                                    challenge = (headerScheme==AuthenticationSchemes.Ntlm ? NtlmClient.AuthType : NegotiateClient.AuthType); 
                                    if (!String.IsNullOrEmpty(outBlob))
                                    { 
                                        challenge += " " + outBlob; 
                                    }
                                } 
                            }
                            break;

                        case AuthenticationSchemes.Basic: 
                            try
                            { 
                                bytes = Convert.FromBase64String(inBlob); 

                                inBlob = WebHeaderCollection.HeaderEncoding.GetString(bytes, 0, bytes.Length); 
                                index = inBlob.IndexOf(':');

                                if (index!=-1) {
                                    string userName = inBlob.Substring(0, index); 
                                    string password = inBlob.Substring(index+1);
 
                                    GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() basic identity found, userName:" + userName); 
                                    principal = new GenericPrincipal(new HttpListenerBasicIdentity(userName, password), null);
                                } 
                                else
                                {
                                    httpError = HttpStatusCode.BadRequest;
                                } 
                            }
                            catch (FormatException) 
                            { 
                                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() FromBase64String threw a FormatException.");
                            } 
                            break;
                    }

                    if (principal != null) 
                    {
                        GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() got principal:" + ValidationHelper.ToString(principal) + " principal.Identity.Name:" + ValidationHelper.ToString(principal.Identity.Name) + " creating request"); 
                        httpContext.SetIdentity(principal, outBlob); 
                    }
                    else 
                    {
                        GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() handshake has failed");
                        if(Logging.On)Logging.PrintWarning(Logging.HttpListener, this, "HandleAuthentication", SR.GetString(SR.net_log_listener_create_valid_identity_failed));
                        httpContext.Request.DetachBlob(memoryBlob); 
                        httpContext.Close();
                        httpContext = null; 
                    } 
                }
 
                // if we're not giving a request to the application, we need to send an error
                ArrayList challenges = null;
                if (httpContext == null)
                { 
                    // If we already have a challenge, just use it.  Otherwise put a challenge for each acceptable scheme.
                    if (challenge != null) 
                    { 
                        AddChallenge(ref challenges, challenge);
                    } 
                    else
                    {
                        // We're starting over.  Any context SSPI might have wanted us to keep is useless.
                        if (newContext != null) 
                        {
                            if (newContext == context) 
                            { 
                                context = null;
                            } 

                            if (newContext != oldContext)
                            {
                                NTAuthentication toClose = newContext; 
                                newContext = null;
                                toClose.CloseContext(); 
                            } 
                            else
                            { 
                                newContext = null;
                            }
                        }
 
                        // If we're sending something besides 401, do it here.
                        if (httpError != HttpStatusCode.Unauthorized) 
                        { 
                            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() failed context#" + ValidationHelper.HashString(context) + " for connectionId:" + connectionId + " because of error:" + httpError.ToString());
                            SendError(requestId, httpError, null); 
                            return null;
                        }

                        challenges = BuildChallenge(authenticationScheme, connectionId, out newContext, 
                            extendedProtectionPolicy, isSecureConnection);
                    } 
                } 

                // Check if we need to call WaitForDisconnect, because if we do and it fails, we want to send a 500 instead. 
                if (disconnectResult == null && newContext != null)
                {
                    RegisterForDisconnectNotification(connectionId, ref disconnectResult);
 
                    // Failed - send 500.
                    if (disconnectResult == null) 
                    { 
                        if (newContext != null)
                        { 
                            if (newContext == context)
                            {
                                context = null;
                            } 

                            if (newContext != oldContext) 
                            { 
                                NTAuthentication toClose = newContext;
                                newContext = null; 
                                toClose.CloseContext();
                            }
                            else
                            { 
                                newContext = null;
                            } 
                        } 

                        GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() failed context#" + ValidationHelper.HashString(context) + " for connectionId:" + connectionId + " because of failed HttpWaitForDisconnect"); 
                        SendError(requestId, HttpStatusCode.InternalServerError, null);
                        httpContext.Request.DetachBlob(memoryBlob);
                        httpContext.Close();
                        return null; 
                    }
                } 
 
                // Update Session if necessary.
                if (oldContext != newContext) 
                {
                    if (oldContext == context)
                    {
                        // Prevent the finally from closing this twice. 
                        context = null;
                    } 
 
                    NTAuthentication toClose = oldContext;
                    oldContext = newContext; 
                    disconnectResult.Session = newContext;

                    if (toClose != null)
                    { 
                        // Save digest context in digest cache, we may need it later because of
                        // subsequest responses to the same req on the same/diff connection 
                        if ((authenticationScheme & AuthenticationSchemes.Digest) != 0) 
                        {
                            SaveDigestContext(toClose); 
                        }
                        else
                        {
                            toClose.CloseContext(); 
                        }
                    } 
                } 

                // Send the 401 here. 
                if (httpContext == null)
                {
                    SendError(requestId, challenges != null && challenges.Count > 0 ? HttpStatusCode.Unauthorized : HttpStatusCode.Forbidden, challenges);
                    GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() SendUnauthorized(Scheme:" + authenticationScheme + ")"); 
                    return null;
                } 
 
                if (!stoleBlob)
                { 
                    stoleBlob = true;
                    httpContext.Request.ReleasePins();
                }
                return httpContext; 
            }
            catch 
            { 
                if (httpContext != null)
                { 
                    httpContext.Request.DetachBlob(memoryBlob);
                    httpContext.Close();
                }
                if (newContext != null) 
                {
                    if (newContext == context) 
                    { 
                        // Prevent the finally from closing this twice.
                        context = null; 
                    }

                    if (newContext != oldContext)
                    { 
                        NTAuthentication toClose = newContext;
                        newContext = null; 
                        toClose.CloseContext(); 
                    }
                    else 
                    {
                        newContext = null;
                    }
                } 
                throw;
            } 
            finally 
            {
                try 
                {
                    // Clean up the previous context if necessary.
                    if (oldContext != null && oldContext != newContext)
                    { 
                        // Clear out Session if it wasn't already.
                        if (newContext == null && disconnectResult != null) 
                        { 
                            disconnectResult.Session = null;
                        } 

                        // Save digest context in digest cache, we may need it later because of
                        // subsequest responses to the same req on the same/diff connection
 
                        if ((authenticationScheme & AuthenticationSchemes.Digest) != 0)
                        { 
                            SaveDigestContext(oldContext); 
                        }
                        else 
                        {
                            oldContext.CloseContext();
                        }
                    } 

                    // Delete any context created but not stored. 
                    if (context != null && oldContext != context && newContext != context) 
                    {
                        context.CloseContext(); 
                    }
                }
                finally
                { 
                    // Check if the connection got deleted while in this method, and clear out the hashtables if it did.
                    // In a nested finally because if this doesn't happen, we leak. 
                    if (disconnectResult != null) 
                    {
                        disconnectResult.FinishOwningDisconnectHandling(); 
                    }
                }
            }
        } 

        private static bool ScenarioChecksChannelBinding(bool isSecureConnection, ProtectionScenario scenario) 
        { 
            return (isSecureConnection && scenario == ProtectionScenario.TransportSelected);
        } 

        private ChannelBinding GetChannelBinding(ulong connectionId, bool isSecureConnection, ExtendedProtectionPolicy policy)
        {
            if (policy.PolicyEnforcement == PolicyEnforcement.Never) 
            {
                if (Logging.On) Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_cbt_disabled)); 
                return null; 
            }
 
            if (!isSecureConnection)
            {
                if (Logging.On) Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_cbt_http));
                return null; 
            }
 
            if (!AuthenticationManager.OSSupportsExtendedProtection) 
            {
                GlobalLog.Assert(policy.PolicyEnforcement != PolicyEnforcement.Always, "User managed to set PolicyEnforcement.Always when the OS does not support extended protection!"); 
                if (Logging.On) Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_cbt_platform));
                return null;
            }
 
            if (policy.ProtectionScenario == ProtectionScenario.TrustedProxy)
            { 
                if (Logging.On) Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_cbt_trustedproxy)); 
                return null;
            } 

            ChannelBinding result = GetChannelBindingFromTls(connectionId);

            GlobalLog.Assert(result != null, "GetChannelBindingFromTls returned null even though OS supposedly supports Extended Protection"); 
            if (Logging.On) Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_cbt));
            return result; 
        } 

        private bool CheckSpn(NTAuthentication context, bool isSecureConnection, ExtendedProtectionPolicy policy) 
        {
            // Kerberos does SPN check already in ASC
            if (context.IsKerberos)
            { 
                if (Logging.On)
                { 
                    Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_spn_kerberos)); 
                }
                return true; 
            }

            // Don't check the SPN if Extended Protection is off or we already checked the CBT
            if (policy.PolicyEnforcement == PolicyEnforcement.Never) 
            {
                if (Logging.On) 
                { 
                    Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_spn_disabled));
                } 
                return true;
            }

            if (ScenarioChecksChannelBinding(isSecureConnection, policy.ProtectionScenario)) 
            {
                if (Logging.On) 
                { 
                    Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_spn_cbt));
                } 
                return true;
            }

            if (!AuthenticationManager.OSSupportsExtendedProtection) 
            {
                GlobalLog.Assert(policy.PolicyEnforcement != PolicyEnforcement.Always, "User managed to set PolicyEnforcement.Always when the OS does not support extended protection!"); 
                if (Logging.On) 
                {
                    Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_spn_platform)); 
                }
                return true;
            }
 
            string clientSpn = context.ClientSpecifiedSpn;
 
            // An empty SPN is only allowed in the WhenSupported case 
            if (String.IsNullOrEmpty(clientSpn))
            { 
                if (policy.PolicyEnforcement == PolicyEnforcement.WhenSupported)
                {
                    if (Logging.On)
                    { 
                        Logging.PrintInfo(Logging.HttpListener, this,
                            SR.GetString(SR.net_log_listener_no_spn_whensupported)); 
                    } 
                    return true;
                } 
                else
                {
                    if (Logging.On)
                    { 
                        Logging.PrintInfo(Logging.HttpListener, this,
                            SR.GetString(SR.net_log_listener_spn_failed_always)); 
                    } 
                    return false;
                } 
            }
            else if (String.Compare(clientSpn, "http/localhost", StringComparison.OrdinalIgnoreCase) == 0)
            {
                if (Logging.On) 
                {
                    Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_spn_loopback)); 
                } 

                return true; 
            }
            else
            {
                if (Logging.On) 
                {
                    Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_spn, clientSpn)); 
                } 

                ServiceNameCollection serviceNames = GetServiceNames(policy); 

                bool found = false;
                foreach (string serviceName in serviceNames)
                { 
                    if (String.Compare(clientSpn, serviceName, StringComparison.OrdinalIgnoreCase) == 0)
                    { 
                        found = true; 
                        if (Logging.On)
                        { 
                            Logging.PrintInfo(Logging.HttpListener, this,
                                SR.GetString(SR.net_log_listener_spn_passed));
                        }
                        break; 
                    }
                } 
 
                if (Logging.On && !found)
                { 
                    Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_spn_failed));

                    if (serviceNames.Count == 0)
                    { 
                        Logging.PrintWarning(Logging.HttpListener, this, "CheckSpn",
                            SR.GetString(SR.net_log_listener_spn_failed_empty)); 
                    } 
                    else
                    { 
                        Logging.PrintInfo(Logging.HttpListener, this,
                            SR.GetString(SR.net_log_listener_spn_failed_dump));

                        foreach (string serviceName in serviceNames) 
                        {
                            Logging.PrintInfo(Logging.HttpListener, this, "\t" + serviceName); 
                        } 
                    }
                } 

                return found;
            }
        } 

        private ServiceNameCollection GetServiceNames(ExtendedProtectionPolicy policy) 
        { 
            ServiceNameCollection serviceNames;
 
            if (policy.CustomServiceNames == null) {

                if (m_DefaultServiceNames.ServiceNames.Count == 0) {
                    throw new InvalidOperationException(SR.GetString(SR.net_listener_no_spns)); 
                }
                serviceNames = m_DefaultServiceNames.ServiceNames; 
            } 
            else {
                serviceNames = policy.CustomServiceNames; 
            }
            return serviceNames;
        }
 
        private ContextFlags GetContextFlags(ExtendedProtectionPolicy policy, bool isSecureConnection)
        { 
            ContextFlags result = ContextFlags.Connection; 

            if (policy.PolicyEnforcement != PolicyEnforcement.Never) { 

                if (policy.PolicyEnforcement == PolicyEnforcement.WhenSupported) {
                    result |= ContextFlags.AllowMissingBindings;
                } 

                if (policy.ProtectionScenario == ProtectionScenario.TrustedProxy) { 
                    result |= ContextFlags.ProxyBindings; 
                }
            } 

            return result;
        }
 
        private static void AddChallenge(ref ArrayList challenges, string challenge)
        { 
            if (challenge!=null) { 
                challenge = challenge.Trim();
                if (challenge.Length>0) { 
                    GlobalLog.Print("HttpListener:AddChallenge() challenge:" + challenge);
                    if (challenges == null)
                    {
                        challenges = new ArrayList(4); 
                    }
                    challenges.Add(challenge); 
                } 
            }
        } 

        private ArrayList BuildChallenge(AuthenticationSchemes authenticationScheme, ulong connectionId,
            out NTAuthentication newContext, ExtendedProtectionPolicy policy, bool isSecureConnection)
        { 
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::BuildChallenge()  authenticationScheme:" + authenticationScheme.ToString());
            ArrayList challenges = null; 
            newContext = null; 

            if ((authenticationScheme & AuthenticationSchemes.Negotiate) != 0) 
            {
                AddChallenge(ref challenges, NegotiateClient.AuthType);
            }
 
            if ((authenticationScheme & AuthenticationSchemes.Ntlm) != 0)
            { 
                AddChallenge(ref challenges, NtlmClient.AuthType); 
            }
 
            if ((authenticationScheme & AuthenticationSchemes.Digest) != 0)
            {
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::BuildChallenge() package:WDigest");
 
                NTAuthentication context = null;
                try 
                { 
                    string outBlob = null;
                    ChannelBinding binding = GetChannelBinding(connectionId, isSecureConnection, policy); 

                    context = new NTAuthentication(true, NegotiationInfoClass.WDigest, null,
                        GetContextFlags(policy, isSecureConnection), binding);
 
                    SecurityStatus statusCode;
                    outBlob = context.GetOutgoingDigestBlob(null, null, null, Realm, false, false, out statusCode); 
                    GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::BuildChallenge() GetOutgoingDigestBlob() returned IsCompleted:" + context.IsCompleted + " statusCode:" + statusCode + " outBlob:[" + outBlob + "]"); 

                    if (context.IsValidContext) 
                    {
                        newContext = context;
                    }
 
                    AddChallenge(ref challenges, DigestClient.AuthType + (string.IsNullOrEmpty(outBlob) ? "" : " " + outBlob));
                } 
                finally 
                {
                    if (context != null && newContext != context) 
                    {
                        context.CloseContext();
                    }
                } 
            }
 
            if ((authenticationScheme & AuthenticationSchemes.Basic) != 0) 
            {
                AddChallenge(ref challenges, BasicClient.AuthType + " realm=\"" + Realm + "\""); 
            }

            return challenges;
        } 

        private void RegisterForDisconnectNotification(ulong connectionId, ref DisconnectAsyncResult disconnectResult) 
        { 
            GlobalLog.Assert(disconnectResult == null, "HttpListener#{0}::RegisterForDisconnectNotification()|Called with a disconnectResult.", ValidationHelper.HashString(this));
 
            try
            {
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::RegisterForDisconnectNotification() calling UnsafeNclNativeMethods.HttpApi.HttpWaitForDisconnect");
 
                DisconnectAsyncResult result = new DisconnectAsyncResult(this, connectionId);
 
                EnsureBoundHandle(); 
                uint statusCode = UnsafeNclNativeMethods.HttpApi.HttpWaitForDisconnect(
                    m_RequestQueueHandle, 
                    connectionId,
                    result.NativeOverlapped);

                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::RegisterForDisconnectNotification() call to UnsafeNclNativeMethods.HttpApi.HttpWaitForDisconnect returned:" + statusCode); 

                if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS || 
                    statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) 
                {
                    // Need to make sure it's going to get returned before adding it to the hash.  That way it'll be handled 
                    // correctly in HandleAuthentication's finally.
                    disconnectResult = result;
                    DisconnectResults[connectionId] = disconnectResult;
                } 
            }
            catch (Win32Exception exception) 
            { 
                uint statusCode = (uint) exception.NativeErrorCode;
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::RegisterForDisconnectNotification() call to UnsafeNclNativeMethods.HttpApi.HttpWaitForDisconnect threw.  statusCode:" + statusCode); 
            }
        }

        private void SendError(ulong requestId, HttpStatusCode httpStatusCode, ArrayList challenges) 
        {
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::SendInternalError() requestId:" + ValidationHelper.ToString(requestId)); 
            UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE httpResponse = new UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE(); 
            httpResponse.Version = new UnsafeNclNativeMethods.HttpApi.HTTP_VERSION();
            httpResponse.Version.MajorVersion = (ushort)1; 
            httpResponse.Version.MinorVersion = (ushort)1;
            httpResponse.StatusCode = (ushort)httpStatusCode;
            string statusDescription = HttpListenerResponse.GetStatusDescription((int)httpStatusCode);
            uint DataWritten = 0; 
            uint statusCode;
            byte[] byteReason = Encoding.Default.GetBytes(statusDescription); 
            fixed (byte* pReason = byteReason) 
            {
                httpResponse.pReason = (sbyte*)pReason; 
                httpResponse.ReasonLength = (ushort)byteReason.Length;

                byte[] byteContentLength = Encoding.Default.GetBytes("0");
                fixed (byte* pContentLength = byteContentLength) 
                {
                    (&httpResponse.Headers.KnownHeaders)[(int)HttpResponseHeader.ContentLength].pRawValue = (sbyte*)pContentLength; 
                    (&httpResponse.Headers.KnownHeaders)[(int)HttpResponseHeader.ContentLength].RawValueLength = (ushort)byteContentLength.Length; 

                    httpResponse.Headers.UnknownHeaderCount = checked((ushort) (challenges == null ? 0 : challenges.Count)); 
                    GCHandle[] challengeHandles = null;
                    UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[] headersArray = null;
                    GCHandle headersArrayHandle = new GCHandle();
                    GCHandle wwwAuthenticateHandle = new GCHandle(); 
                    if (httpResponse.Headers.UnknownHeaderCount > 0)
                    { 
                        challengeHandles = new GCHandle[httpResponse.Headers.UnknownHeaderCount]; 
                        headersArray = new UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER[httpResponse.Headers.UnknownHeaderCount];
                    } 

                    try
                    {
                        if (httpResponse.Headers.UnknownHeaderCount > 0) 
                        {
                            headersArrayHandle = GCHandle.Alloc(headersArray, GCHandleType.Pinned); 
                            httpResponse.Headers.pUnknownHeaders = (UnsafeNclNativeMethods.HttpApi.HTTP_UNKNOWN_HEADER*) Marshal.UnsafeAddrOfPinnedArrayElement(headersArray, 0); 
                            wwwAuthenticateHandle = GCHandle.Alloc(s_WwwAuthenticateBytes, GCHandleType.Pinned);
                            sbyte* wwwAuthenticate = (sbyte*) Marshal.UnsafeAddrOfPinnedArrayElement(s_WwwAuthenticateBytes, 0); 

                            for (int i = 0; i < challengeHandles.Length; i++)
                            {
                                byte[] byteChallenge = Encoding.Default.GetBytes((string) challenges[i]); 
                                challengeHandles[i] = GCHandle.Alloc(byteChallenge, GCHandleType.Pinned);
                                headersArray[i].pName = wwwAuthenticate; 
                                headersArray[i].NameLength = (ushort) s_WwwAuthenticateBytes.Length; 
                                headersArray[i].pRawValue = (sbyte*) Marshal.UnsafeAddrOfPinnedArrayElement(byteChallenge, 0);
                                headersArray[i].RawValueLength = checked((ushort) byteChallenge.Length); 
                            }
                        }

                        GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::SendInternalError() calling UnsafeNclNativeMethods.HttpApi.HttpSendHtthttpResponse"); 
                        statusCode =
                            UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse( 
                                m_RequestQueueHandle, 
                                requestId,
                                0, 
                                &httpResponse,
                                null,
                                &DataWritten,
                                SafeLocalFree.Zero, 
                                0,
                                null, 
                                null ); 
                    }
                    finally 
                    {
                        if (headersArrayHandle.IsAllocated)
                        {
                            headersArrayHandle.Free(); 
                        }
                        if (wwwAuthenticateHandle.IsAllocated) 
                        { 
                            wwwAuthenticateHandle.Free();
                        } 
                        if (challengeHandles != null)
                        {
                            for (int i = 0; i < challengeHandles.Length; i++)
                            { 
                                if (challengeHandles[i].IsAllocated)
                                { 
                                    challengeHandles[i].Free(); 
                                }
                            } 
                        }
                    }
                }
            } 
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::SendInternalError() call to UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse returned:" + statusCode);
            if (statusCode!=UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { 
                // if we fail to send a 401 something's seriously wrong, abort the request 
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() SendUnauthorized() returned:" + statusCode);
                HttpListenerContext.CancelRequest(m_RequestQueueHandle, requestId); 
            }
        }

        private unsafe static int GetTokenOffsetFromBlob(IntPtr blob) 
        {
            Debug.Assert(blob != IntPtr.Zero); 
            IntPtr tokenPointer = Marshal.ReadIntPtr((IntPtr)blob, (int)Marshal.OffsetOf(ChannelBindingStatusType, "ChannelToken")); 

            Debug.Assert(tokenPointer != IntPtr.Zero); 
            return (int)IntPtrHelper.Subtract(tokenPointer, blob);
        }

        private unsafe static int GetTokenSizeFromBlob(IntPtr blob) 
        {
            Debug.Assert(blob != IntPtr.Zero); 
            return Marshal.ReadInt32(blob, (int)Marshal.OffsetOf(ChannelBindingStatusType, "ChannelTokenSize")); 
        }
 
        internal ChannelBinding GetChannelBindingFromTls(ulong connectionId)
        {
            if (Logging.On) {
                Logging.Enter(Logging.HttpListener, "HttpListener#" + ValidationHelper.HashString(this) + 
                    "::GetChannelBindingFromTls() connectionId: " + connectionId.ToString());
            } 
 
            // +128 since a CBT is usually <128 thus we need to call HRCC just once. If the CBT
            // is >128 we will get ERROR_MORE_DATA and call again 
            int size = RequestChannelBindStatusSize + 128;

            Debug.Assert(size >= 0);
 
            byte[] blob = null;
            SafeLocalFreeChannelBinding token = null; 
 
            uint bytesReceived = 0;
            uint statusCode; 

            do {
                blob = new byte[size];
                fixed (byte* blobPtr = blob) 
                {
                    // Http.sys team: ServiceName will always be null if 
                    // HTTP_RECEIVE_SECURE_CHANNEL_TOKEN flag is set. 
                    statusCode = UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate(
                        RequestQueueHandle, 
                        connectionId,
                        (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_RECEIVE_SECURE_CHANNEL_TOKEN,
                        blobPtr,
                        (uint)size, 
                        &bytesReceived,
                        null); 
 
                    if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
                    { 
                        int tokenOffset = GetTokenOffsetFromBlob((IntPtr)blobPtr);
                        int tokenSize = GetTokenSizeFromBlob((IntPtr)blobPtr);
                        Debug.Assert(tokenSize < Int32.MaxValue);
 
                        token = SafeLocalFreeChannelBinding.LocalAlloc(tokenSize);
                        if (token.IsInvalid) 
                        { 
                            throw new OutOfMemoryException();
                        } 
                        Marshal.Copy(blob, tokenOffset, token.DangerousGetHandle(), tokenSize);
                    }
                    else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
                    { 
                        int tokenSize = GetTokenSizeFromBlob((IntPtr)blobPtr);
                        Debug.Assert(tokenSize < Int32.MaxValue); 
 
                        size = RequestChannelBindStatusSize + tokenSize;
                    } 
                    else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_PARAMETER)
                    {
                        if (Logging.On)
                        { 
                            Logging.PrintError(Logging.HttpListener, "HttpListener#" +
                                ValidationHelper.HashString(this) + 
                                "::GetChannelBindingFromTls() Can't retrieve CBT from TLS: ERROR_INVALID_PARAMETER"); 
                        }
                        return null; // old schannel library which doesn't support CBT 
                    }
                    else
                    {
                        throw new HttpListenerException((int)statusCode); 
                    }
                } 
            } while (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS); 

            return token; 
        }

        internal void CheckDisposed() {
            if (m_State==State.Closed) { 
                throw new ObjectDisposedException(this.GetType().FullName);
            } 
        } 

        // This only works for context-destroying errors. 
        private HttpStatusCode HttpStatusFromSecurityStatus(SecurityStatus status)
        {
            if (NclUtilities.IsCredentialFailure(status))
            { 
                return HttpStatusCode.Unauthorized;
            } 
            if (NclUtilities.IsClientFault(status)) 
            {
                return HttpStatusCode.BadRequest; 
            }
            return HttpStatusCode.InternalServerError;
        }
 
        enum State {
            Stopped, 
            Started, 
            Closed,
        } 

        private const int DigestLifetimeSeconds = 300;
        private const int MaximumDigests = 1024;  // Must be a power of two.
        private const int MinimumDigestLifetimeSeconds = 10; 

        private struct DigestContext 
        { 
            internal NTAuthentication context;
            internal int timestamp; 
        }

        private DigestContext[] m_SavedDigests;
        private ArrayList m_ExtraSavedDigests; 
        private ArrayList m_ExtraSavedDigestsBaking;
        private int m_ExtraSavedDigestsTimestamp; 
        private int m_NewestContext; 
        private int m_OldestContext;
 
        private void SaveDigestContext(NTAuthentication digestContext)
        {
            if (m_SavedDigests == null)
            { 
                Interlocked.CompareExchange(ref m_SavedDigests, new DigestContext[MaximumDigests], null);
            } 
 
            // We want to actually close the contexts outside the lock.
            NTAuthentication oldContext = null; 
            ArrayList digestsToClose = null;
            lock (m_SavedDigests)
            {
                // If we're stopped, just throw it away. 
                if (!IsListening)
                { 
                    digestContext.CloseContext(); 
                    return;
                } 

                int now = ((now = Environment.TickCount) == 0 ? 1 : now);

                m_NewestContext = (m_NewestContext + 1) & (MaximumDigests - 1); 

                int oldTimestamp = m_SavedDigests[m_NewestContext].timestamp; 
                oldContext = m_SavedDigests[m_NewestContext].context; 
                m_SavedDigests[m_NewestContext].timestamp = now;
                m_SavedDigests[m_NewestContext].context = digestContext; 

                // May need to move this up.
                if (m_OldestContext == m_NewestContext)
                { 
                    m_OldestContext = (m_NewestContext + 1) & (MaximumDigests - 1);
                } 
 
                // Delete additional contexts older than five minutes.
                while (unchecked(now - m_SavedDigests[m_OldestContext].timestamp) >= DigestLifetimeSeconds && m_SavedDigests[m_OldestContext].context != null) 
                {
                    if (digestsToClose == null)
                    {
                        digestsToClose = new ArrayList(); 
                    }
                    digestsToClose.Add(m_SavedDigests[m_OldestContext].context); 
                    m_SavedDigests[m_OldestContext].context = null; 
                    m_OldestContext = (m_OldestContext + 1) & (MaximumDigests - 1);
                } 

                // If the old context is younger than 10 seconds, put it in the backup pile.
                if (oldContext != null && unchecked(now - oldTimestamp) <= MinimumDigestLifetimeSeconds * 1000)
                { 
                    // Use a two-tier ArrayList system to guarantee each entry lives at least 10 seconds.
                    if (m_ExtraSavedDigests == null || 
                        unchecked(now - m_ExtraSavedDigestsTimestamp) > MinimumDigestLifetimeSeconds * 1000) 
                    {
                        digestsToClose = m_ExtraSavedDigestsBaking; 
                        m_ExtraSavedDigestsBaking = m_ExtraSavedDigests;
                        m_ExtraSavedDigestsTimestamp = now;
                        m_ExtraSavedDigests = new ArrayList();
                    } 
                    m_ExtraSavedDigests.Add(oldContext);
                    oldContext = null; 
                } 
            }
 
            if (oldContext != null)
            {
                oldContext.CloseContext();
            } 
            if (digestsToClose != null)
            { 
                for (int i = 0; i < digestsToClose.Count; i++) 
                {
                    ((NTAuthentication)digestsToClose[i]).CloseContext(); 
                }
            }
        }
 
        private void ClearDigestCache()
        { 
            if (m_SavedDigests == null) 
            {
                return; 
            }

            ArrayList[] toClose = new ArrayList[3];
            lock (m_SavedDigests) 
            {
                toClose[0] = m_ExtraSavedDigestsBaking; 
                m_ExtraSavedDigestsBaking = null; 
                toClose[1] = m_ExtraSavedDigests;
                m_ExtraSavedDigests = null; 

                m_NewestContext = 0;
                m_OldestContext = 0;
 
                toClose[2] = new ArrayList();
                for (int i = 0; i < MaximumDigests; i++) 
                { 
                    if (m_SavedDigests[i].context != null)
                    { 
                        toClose[2].Add(m_SavedDigests[i].context);
                        m_SavedDigests[i].context = null;
                    }
                    m_SavedDigests[i].timestamp = 0; 
                }
            } 
 
            for (int j = 0; j < toClose.Length; j++)
            { 
                if (toClose[j] != null)
                {
                    for (int k = 0; k < toClose[j].Count; k++)
                    { 
                        ((NTAuthentication) toClose[j][k]).CloseContext();
                    } 
                } 
            }
        } 

        class DisconnectAsyncResult : IAsyncResult {
            private static readonly IOCompletionCallback s_IOCallback = new IOCompletionCallback(WaitCallback);
 
            private ulong m_ConnectionId;
            private HttpListener m_HttpListener; 
            NativeOverlapped* m_NativeOverlapped; 
            private int m_OwnershipState;   // 0 = normal, 1 = in HandleAuthentication(), 2 = disconnected, 3 = cleaned up
 
            private WindowsPrincipal m_AuthenticatedConnection;
            private NTAuthentication m_Session;

            internal const string NTLM = "NTLM"; 

            internal NativeOverlapped* NativeOverlapped{ 
                get{ 
                    return m_NativeOverlapped;
                } 
            }

            public object AsyncState {
                get { 
                    throw ExceptionHelper.PropertyNotImplementedException;
                } 
            } 
            public WaitHandle AsyncWaitHandle {
                get { 
                    throw ExceptionHelper.PropertyNotImplementedException;
                }
            }
            public bool CompletedSynchronously { 
                get {
                    throw ExceptionHelper.PropertyNotImplementedException; 
                } 
            }
            public bool IsCompleted { 
                get {
                    throw ExceptionHelper.PropertyNotImplementedException;
                }
            } 

            internal unsafe DisconnectAsyncResult(HttpListener httpListener, ulong connectionId) { 
                GlobalLog.Print("DisconnectAsyncResult#" + ValidationHelper.HashString(this) + "::.ctor() httpListener#" + ValidationHelper.HashString(httpListener) + " connectionId:" + connectionId); 
                m_OwnershipState = 1;
                m_HttpListener = httpListener; 
                m_ConnectionId = connectionId;
                Overlapped overlapped = new Overlapped();
                overlapped.AsyncResult = this;
                // we can call the Unsafe API here, we won't ever call user code 
                m_NativeOverlapped = overlapped.UnsafePack(s_IOCallback, null);
                GlobalLog.Print("DisconnectAsyncResult#" + ValidationHelper.HashString(this) + "::.ctor() overlapped#" + ValidationHelper.HashString(overlapped) + " nativeOverlapped:" + ((IntPtr)m_NativeOverlapped).ToString("x")); 
            } 

            internal bool StartOwningDisconnectHandling() 
            {
                int oldValue;

                while ((oldValue = Interlocked.CompareExchange(ref m_OwnershipState, 1, 0)) == 2) 
                {
                    // Must block until it equals 3 - we must be in the callback right now. 
                    Thread.SpinWait(1); 
                }
 
                GlobalLog.Assert(oldValue != 1, "DisconnectAsyncResult#{0}::HandleDisconnect()|StartOwningDisconnectHandling() called twice.", ValidationHelper.HashString(this));
                return oldValue < 2;
            }
 
            internal void FinishOwningDisconnectHandling()
            { 
                // If it got disconnected, run the disconnect code. 
                if (Interlocked.CompareExchange(ref m_OwnershipState, 0, 1) == 2)
                { 
                    HandleDisconnect();
                }
            }
 
            private static unsafe void WaitCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) {
                GlobalLog.Print("DisconnectAsyncResult::WaitCallback() errorCode:" + errorCode + " numBytes:" + numBytes + " nativeOverlapped:" + ((IntPtr)nativeOverlapped).ToString("x")); 
                // take the DisconnectAsyncResult object from the state 
                Overlapped callbackOverlapped = Overlapped.Unpack(nativeOverlapped);
                DisconnectAsyncResult asyncResult = (DisconnectAsyncResult) callbackOverlapped.AsyncResult; 
                GlobalLog.Print("DisconnectAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::WaitCallback() callbackOverlapped#" + ValidationHelper.HashString(callbackOverlapped) + " m_ConnectionId:" + asyncResult.m_ConnectionId);
                Overlapped.Free(nativeOverlapped);
                if (Interlocked.Exchange(ref asyncResult.m_OwnershipState, 2) == 0)
                { 
                    asyncResult.HandleDisconnect();
                } 
            } 

            private void HandleDisconnect() 
            {
                GlobalLog.Print("DisconnectAsyncResult#" + ValidationHelper.HashString(this) + "::HandleDisconnect() DisconnectResults#" + ValidationHelper.HashString(m_HttpListener.DisconnectResults) + " removing for m_ConnectionId:" + m_ConnectionId);
                m_HttpListener.DisconnectResults.Remove(m_ConnectionId);
                if (m_Session != null) 
                {
                    if (m_Session.Package == NegotiationInfoClass.WDigest) 
                    { 
                        // VSWhidbey #497767
                        // WDigest doesn't like having the context passed back in on the next request on a connection, but it does want 
                        // the server to keep a reference to it for as long as a client might reuse the nonce.  The heuristic we use is,
                        // keep contexts for five minutes, up to a maximum of 1024, except also keep all contexts at least 10 seconds to avoid
                        // total DoS (where no handshakes can be completed in time).
                        m_HttpListener.SaveDigestContext(m_Session); 
                    }
                    else 
                    { 
                        m_Session.CloseContext();
                    } 
                }

                // Clean up the identity. This is for scenarios where identity was not cleaned up before due to
                // identity caching for unsafe ntlm authentication 

                IDisposable identity = m_AuthenticatedConnection == null ? null : m_AuthenticatedConnection.Identity as IDisposable; 
                if ((identity != null) && 
                    (m_AuthenticatedConnection.Identity.AuthenticationType == NTLM) &&
                    (m_HttpListener.UnsafeConnectionNtlmAuthentication)) 
                {
                    identity.Dispose();
                }
 
                int oldValue = Interlocked.Exchange(ref m_OwnershipState, 3);
                GlobalLog.Assert(oldValue == 2, "DisconnectAsyncResult#{0}::HandleDisconnect()|Expected OwnershipState of 2, saw {1}.", ValidationHelper.HashString(this), oldValue); 
            } 

            internal WindowsPrincipal AuthenticatedConnection 
            {
                get
                {
                    return m_AuthenticatedConnection; 
                }
 
                set 
                {
                    // The previous value can't be disposed because it may be in use by the app. 
                    m_AuthenticatedConnection = value;
                }
            }
 
            internal NTAuthentication Session
            { 
                get 
                {
                    return m_Session; 
                }

                set
                { 
                    m_Session = value;
                } 
            } 
        }
    } 

/*  Proposed Future HTTP Base Classes
    see \ndp\mb\docs\specs\NetworkFramework\HTTPSYS\ASP.NET\stub.cs
 
    // System.Net exposes base abstract classes that System.Web will inherit from
 
    public abstract class BaseHttpContext { 
        public virtual IPrincipal User { get; set; }
 
        // it doesn't make sense to make these virtual because we can't override
        // and change the returned type for System.Web or people would break.
        // the only thing we can do is to declare them normally and hide the
        // base implementation like "public new System.Web.HttpRequest Request" 

        public BaseHttpRequest Request { get; } 
        public BaseHttpResponse Response { get; } 

        // these provide plumbing to make the above two methods callable with a BaseHttpContext reference 

        protected virtual BaseHttpRequest GetRequest();
        protected virtual BaseHttpResponse GetResponse();
    } 

    public abstract class BaseHttpRequest { 
        public virtual string[] AcceptTypes { get; } 
        public virtual Encoding ContentEncoding { get; set; }
        public virtual string ContentType { get; set; } 
        public virtual NameValueCollection Headers { get; }
        public virtual string HttpMethod { get; }
        public virtual Stream InputStream { get; }
        public virtual bool IsAuthenticated { get; } 
        public virtual bool IsLocal { get; }
        public virtual bool IsSecureConnection { get; } 
        public virtual NameValueCollection QueryString { get; } 
        public virtual string RawUrl { get; }
        public virtual Uri Url { get; } 
        public virtual Uri UrlReferrer { get; }
        public virtual string UserAgent { get; }
        public virtual string UserHostAddress { get; }
        public virtual string UserHostName { get; } 
        public virtual string[] UserLanguages { get; }
 
        // APIs that are in the base class but are new to ASP .NET 

        public virtual long ContentLengthLong { get; } 
        public virtual bool HasEntityBody { get; }
        public virtual bool KeepAlive { get; }
        public virtual IPEndPoint RemoteEndPoint { get; }
        public virtual IPEndPoint LocalEndPoint { get; } 
    }
 
    public abstract class BaseHttpResponse { 
        public virtual void AppendHeader(string name, string value);
        public virtual void Close(); 
        public virtual Encoding ContentEncoding { get; set; }
        public virtual string ContentType { get; set; }
        public virtual Stream OutputStream { get; }
        public virtual string RedirectLocation { get; set; } 
        public virtual int StatusCode { get; set; }
        public virtual string StatusDescription { get; set; } 
 
        // APIs that are in the base class but are new to ASP .NET
 
        public virtual bool KeepAlive { get; set; }
    }
*/
} 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK