HttpListener.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Net / System / Net / HttpListener.cs / 6 / 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;

    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 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 SafeCloseHandle m_RequestQueueHandle; 
        private bool m_RequestHandleBound;
        private State m_State;
        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 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();
            } 
            m_State = State.Stopped;
            m_InternalLock = new object(); 
            m_DefaultServiceNames = new ServiceNameStore(); 

            // 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 SafeCloseHandle 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(); 
                } 

                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 (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; 
            }
        }

        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", "");
            } 
        }
 
        [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] 
        internal void EnsureBoundHandle()
        { 
            if (!m_RequestHandleBound)
            {
                lock (m_InternalLock)
                { 
                    if (!m_RequestHandleBound)
                    { 
                        ThreadPool.BindHandle(m_RequestQueueHandle.DangerousGetHandle()); 
                        m_RequestHandleBound = true;
                    } 
                }
            }
        }
 
        public void Start() {
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Start", ""); 
            try { 
                CheckDisposed();
                if (m_State==State.Started) { 
                    return;
                }
                m_RequestQueueHandle = SafeCloseHandle.CreateRequestQueueHandle();
                AddAll(); 
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::.ctor() SafeCloseHandle.CreateRequestQueueHandle() returned m_RequestQueueHandle:" + m_RequestQueueHandle);
                m_State = State.Started; 
            } catch (Exception exception) { 
                if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Start", exception);
                throw; 
            } finally {
                if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Start", "");
            }
        } 

        public void Stop() { 
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Stop", ""); 
            try {
                CheckDisposed(); 
                if (m_State==State.Stopped) {
                    return;
                }
                RemoveAll(false); 
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::Stop() calling SafeCloseHandle.Close()");
                m_RequestQueueHandle.Close(); 
                m_RequestHandleBound = false; 
                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", "");
            } 
        } 

        public void Abort() { 
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Abort", "");
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::Abort()");

            try { 
                if (m_RequestQueueHandle!=null)
                { 
                    m_RequestQueueHandle.Abort(); 
                }
                m_RequestHandleBound = false; 
                m_State = State.Closed;
                ClearDigestCache();
            } catch (Exception exception) {
                if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Abort", exception); 
                throw;
            } finally { 
                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) { 
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Dispose", "");
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::Dispose()"); 
            try {
                if (m_State == State.Closed){
                    return;
                } 
                Stop();
                m_RequestHandleBound = false; 
                m_State = State.Closed; 
            } catch (Exception exception) {
                if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Dispose", exception); 
                throw;
            } finally {
                if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Dispose", "");
            } 
        }
 
        ///  
        void IDisposable.Dispose() {
            Dispose(true); 
        }

        private uint InternalAddPrefix(string uriPrefix) {
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::InternalAddPrefix() uriPrefix:" + uriPrefix + " calling UnsafeNclNativeMethods.HttpApi.HttpAddUrl"); 
            uint statusCode = 0;
            fixed (char* pUriPrefix = uriPrefix) { 
                statusCode = 
                    UnsafeNclNativeMethods.HttpApi.HttpAddUrl(
                        m_RequestQueueHandle, 
                        (ushort*)pUriPrefix,
                        null);
            }
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::InternalAddPrefix() call to UnsafeNclNativeMethods.HttpApi.HttpAddUrl returned:" + statusCode); 
            return statusCode;
        } 
 
        private bool InternalRemovePrefix(string uriPrefix) {
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::InternalRemovePrefix() uriPrefix:" + uriPrefix + " calling UnsafeNclNativeMethods.HttpApi.HttpRemoveUrl"); 
            uint statusCode = 0;
            fixed (char* pUriPrefix = uriPrefix) {
                statusCode =
                   UnsafeNclNativeMethods.HttpApi.HttpRemoveUrl( 
                        m_RequestQueueHandle,
                        (ushort*)pUriPrefix); 
            } 
            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 AddAll() { 
            // 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) {
                        Abort(); 
                        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.Request.RequestTraceIdentifier); 
            }
        } 
 
        [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.Enter(Logging.HttpListener, this, "EndGetContext", "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;
                    bool extendedProtectionFailure = false; 
                    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, out extendedProtectionFailure); 

                            if (!extendedProtectionFailure)
                            {
                                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); 
                                }
                            }
                            else
                            { 
                                httpError = HttpStatusCode.Unauthorized;
                            } 
                            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, out extendedProtectionFailure);
 
                                if (!extendedProtectionFailure)
                                { 
                                    context = new NTAuthentication(true, package, null, 
                                        GetContextFlags(extendedProtectionPolicy, isSecureConnection), binding);
                                } 
                            }

                            if (!extendedProtectionFailure)
                            { 
                                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; 
                                        } 
                                    }
                                } 
                            }
                            else
                            {
                                httpError = HttpStatusCode.Unauthorized; 
                            }
                            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, out bool extendedProtectionFailure) 
        { 
            extendedProtectionFailure = false;
 
            if (policy.PolicyEnforcement == PolicyEnforcement.Never)
            {
                if (Logging.On) Logging.PrintInfo(Logging.HttpListener, "HttpListener#" + ValidationHelper.HashString(this) + "::GetChannelBinding Extended Protection disabled");
                return null; 
            }
 
            if (!isSecureConnection) 
            {
                if (Logging.On) Logging.PrintInfo(Logging.HttpListener, "HttpListener#" + ValidationHelper.HashString(this) + "::GetChannelBinding No Channel Binding check for requests without a secure channel"); 
                return null;
            }

            ChannelBinding result = GetChannelBindingFromTls(connectionId); 

            if (result == null) 
            { 
                if (Logging.On) Logging.PrintError(Logging.HttpListener, "HttpListener#" + ValidationHelper.HashString(this) + "::GetChannelBinding() Policy requires extended protection but platform does not support it!");
                extendedProtectionFailure = true; 
            }

            if (policy.ProtectionScenario == ProtectionScenario.TrustedProxy)
            { 
                if (Logging.On) Logging.PrintInfo(Logging.HttpListener, "HttpListener#" + ValidationHelper.HashString(this) + "::GetChannelBinding TrustedProxy policy does not require channel binding check");
                result = null; 
            } 

            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, "HttpListener#" + ValidationHelper.HashString(this) +
                        "::CheckSpn() No SPN check since auth scheme is Kerberos.");
                }
                return true; 
            }
 
            // Don't check the SPN if Extended Protection is off or we already checked the CBT 
            if (policy.PolicyEnforcement == PolicyEnforcement.Never ||
                ScenarioChecksChannelBinding(isSecureConnection, policy.ProtectionScenario)) 
            {
                if (Logging.On)
                {
                    Logging.PrintInfo(Logging.HttpListener, "HttpListener#" + 
                        ValidationHelper.HashString(this) + "::CheckSpn() Returning true because " +
                        policy == null ? "no extended protection policy set" : "CBT was checked"); 
                } 
                return true;
            } 

            string clientSpn = context.ClientSpecifiedSpn;

            // An empty SPN is only allowed in the WhenSupported case 
            if (String.IsNullOrEmpty(clientSpn))
            { 
                bool result = false; 
                if (policy.PolicyEnforcement == PolicyEnforcement.WhenSupported)
                { 
                    result = true;

                    if (Logging.On)
                    { 
                        Logging.PrintInfo(Logging.HttpListener, "HttpListener#" +
                            ValidationHelper.HashString(this) + "::CheckSpn() No client SPN provided, but PolicyEnforcement " + 
                            "is WhenSupported"); 
                    }
                } 

                return result;
            }
            else if (String.Compare(clientSpn, "http/localhost", StringComparison.OrdinalIgnoreCase) == 0) 
            {
                if (Logging.On) 
                { 
                    Logging.PrintInfo(Logging.HttpListener, "HttpListener#" +
                        ValidationHelper.HashString(this) + "::CheckSpn() SSP indicates loopback authentication, skipping SPN check"); 
                }

                return true;
            } 
            else
            { 
                if (Logging.On) 
                {
                    Logging.PrintInfo(Logging.HttpListener, "HttpListener#" + 
                            ValidationHelper.HashString(this) + "::CheckSpn() Client provided SPN '" +
                            clientSpn + "'");
                }
 
                ServiceNameCollection serviceNames = GetServiceNames(policy);
 
                bool found = false; 
                foreach (string serviceName in serviceNames)
                { 
                    if (String.Compare(clientSpn, serviceName, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        found = true;
                        break; 
                    }
                } 
 
                if (Logging.On && !found)
                { 
                    Logging.PrintInfo(Logging.HttpListener, "HttpListener#" +
                            ValidationHelper.HashString(this) + "::CheckSpn() SPN check failed");

                    if (serviceNames.Count == 0) 
                    {
                        Logging.PrintWarning(Logging.HttpListener, "HttpListener#" + 
                                ValidationHelper.HashString(this) + "::CheckSpn() Service Name collection was emtpy!"); 
                    }
                    else 
                    {
                        Logging.PrintInfo(Logging.HttpListener, "HttpListener#" +
                                ValidationHelper.HashString(this) + "::CheckSpn() Dumping Acceptable Service Names");
 
                        foreach (string serviceName in serviceNames)
                        { 
                            Logging.PrintInfo(Logging.HttpListener, "HttpListener#" + 
                                    ValidationHelper.HashString(this) + "::CheckSpn() \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
                { 
                    bool extendedProtectionFailure;
                    string outBlob = null;
                    ChannelBinding binding = GetChannelBinding(connectionId, isSecureConnection, policy, out extendedProtectionFailure);
 
                    if (!extendedProtectionFailure)
                    { 
                        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.
//------------------------------------------------------------------------------ 
// 
//     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;

    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 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 SafeCloseHandle m_RequestQueueHandle; 
        private bool m_RequestHandleBound;
        private State m_State;
        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 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();
            } 
            m_State = State.Stopped;
            m_InternalLock = new object(); 
            m_DefaultServiceNames = new ServiceNameStore(); 

            // 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 SafeCloseHandle 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(); 
                } 

                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 (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; 
            }
        }

        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", "");
            } 
        }
 
        [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] 
        internal void EnsureBoundHandle()
        { 
            if (!m_RequestHandleBound)
            {
                lock (m_InternalLock)
                { 
                    if (!m_RequestHandleBound)
                    { 
                        ThreadPool.BindHandle(m_RequestQueueHandle.DangerousGetHandle()); 
                        m_RequestHandleBound = true;
                    } 
                }
            }
        }
 
        public void Start() {
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Start", ""); 
            try { 
                CheckDisposed();
                if (m_State==State.Started) { 
                    return;
                }
                m_RequestQueueHandle = SafeCloseHandle.CreateRequestQueueHandle();
                AddAll(); 
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::.ctor() SafeCloseHandle.CreateRequestQueueHandle() returned m_RequestQueueHandle:" + m_RequestQueueHandle);
                m_State = State.Started; 
            } catch (Exception exception) { 
                if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Start", exception);
                throw; 
            } finally {
                if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Start", "");
            }
        } 

        public void Stop() { 
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Stop", ""); 
            try {
                CheckDisposed(); 
                if (m_State==State.Stopped) {
                    return;
                }
                RemoveAll(false); 
                GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::Stop() calling SafeCloseHandle.Close()");
                m_RequestQueueHandle.Close(); 
                m_RequestHandleBound = false; 
                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", "");
            } 
        } 

        public void Abort() { 
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Abort", "");
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::Abort()");

            try { 
                if (m_RequestQueueHandle!=null)
                { 
                    m_RequestQueueHandle.Abort(); 
                }
                m_RequestHandleBound = false; 
                m_State = State.Closed;
                ClearDigestCache();
            } catch (Exception exception) {
                if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Abort", exception); 
                throw;
            } finally { 
                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) { 
            if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Dispose", "");
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::Dispose()"); 
            try {
                if (m_State == State.Closed){
                    return;
                } 
                Stop();
                m_RequestHandleBound = false; 
                m_State = State.Closed; 
            } catch (Exception exception) {
                if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Dispose", exception); 
                throw;
            } finally {
                if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Dispose", "");
            } 
        }
 
        ///  
        void IDisposable.Dispose() {
            Dispose(true); 
        }

        private uint InternalAddPrefix(string uriPrefix) {
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::InternalAddPrefix() uriPrefix:" + uriPrefix + " calling UnsafeNclNativeMethods.HttpApi.HttpAddUrl"); 
            uint statusCode = 0;
            fixed (char* pUriPrefix = uriPrefix) { 
                statusCode = 
                    UnsafeNclNativeMethods.HttpApi.HttpAddUrl(
                        m_RequestQueueHandle, 
                        (ushort*)pUriPrefix,
                        null);
            }
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::InternalAddPrefix() call to UnsafeNclNativeMethods.HttpApi.HttpAddUrl returned:" + statusCode); 
            return statusCode;
        } 
 
        private bool InternalRemovePrefix(string uriPrefix) {
            GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::InternalRemovePrefix() uriPrefix:" + uriPrefix + " calling UnsafeNclNativeMethods.HttpApi.HttpRemoveUrl"); 
            uint statusCode = 0;
            fixed (char* pUriPrefix = uriPrefix) {
                statusCode =
                   UnsafeNclNativeMethods.HttpApi.HttpRemoveUrl( 
                        m_RequestQueueHandle,
                        (ushort*)pUriPrefix); 
            } 
            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 AddAll() { 
            // 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) {
                        Abort(); 
                        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.Request.RequestTraceIdentifier); 
            }
        } 
 
        [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.Enter(Logging.HttpListener, this, "EndGetContext", "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;
                    bool extendedProtectionFailure = false; 
                    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, out extendedProtectionFailure); 

                            if (!extendedProtectionFailure)
                            {
                                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); 
                                }
                            }
                            else
                            { 
                                httpError = HttpStatusCode.Unauthorized;
                            } 
                            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, out extendedProtectionFailure);
 
                                if (!extendedProtectionFailure)
                                { 
                                    context = new NTAuthentication(true, package, null, 
                                        GetContextFlags(extendedProtectionPolicy, isSecureConnection), binding);
                                } 
                            }

                            if (!extendedProtectionFailure)
                            { 
                                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; 
                                        } 
                                    }
                                } 
                            }
                            else
                            {
                                httpError = HttpStatusCode.Unauthorized; 
                            }
                            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, out bool extendedProtectionFailure) 
        { 
            extendedProtectionFailure = false;
 
            if (policy.PolicyEnforcement == PolicyEnforcement.Never)
            {
                if (Logging.On) Logging.PrintInfo(Logging.HttpListener, "HttpListener#" + ValidationHelper.HashString(this) + "::GetChannelBinding Extended Protection disabled");
                return null; 
            }
 
            if (!isSecureConnection) 
            {
                if (Logging.On) Logging.PrintInfo(Logging.HttpListener, "HttpListener#" + ValidationHelper.HashString(this) + "::GetChannelBinding No Channel Binding check for requests without a secure channel"); 
                return null;
            }

            ChannelBinding result = GetChannelBindingFromTls(connectionId); 

            if (result == null) 
            { 
                if (Logging.On) Logging.PrintError(Logging.HttpListener, "HttpListener#" + ValidationHelper.HashString(this) + "::GetChannelBinding() Policy requires extended protection but platform does not support it!");
                extendedProtectionFailure = true; 
            }

            if (policy.ProtectionScenario == ProtectionScenario.TrustedProxy)
            { 
                if (Logging.On) Logging.PrintInfo(Logging.HttpListener, "HttpListener#" + ValidationHelper.HashString(this) + "::GetChannelBinding TrustedProxy policy does not require channel binding check");
                result = null; 
            } 

            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, "HttpListener#" + ValidationHelper.HashString(this) +
                        "::CheckSpn() No SPN check since auth scheme is Kerberos.");
                }
                return true; 
            }
 
            // Don't check the SPN if Extended Protection is off or we already checked the CBT 
            if (policy.PolicyEnforcement == PolicyEnforcement.Never ||
                ScenarioChecksChannelBinding(isSecureConnection, policy.ProtectionScenario)) 
            {
                if (Logging.On)
                {
                    Logging.PrintInfo(Logging.HttpListener, "HttpListener#" + 
                        ValidationHelper.HashString(this) + "::CheckSpn() Returning true because " +
                        policy == null ? "no extended protection policy set" : "CBT was checked"); 
                } 
                return true;
            } 

            string clientSpn = context.ClientSpecifiedSpn;

            // An empty SPN is only allowed in the WhenSupported case 
            if (String.IsNullOrEmpty(clientSpn))
            { 
                bool result = false; 
                if (policy.PolicyEnforcement == PolicyEnforcement.WhenSupported)
                { 
                    result = true;

                    if (Logging.On)
                    { 
                        Logging.PrintInfo(Logging.HttpListener, "HttpListener#" +
                            ValidationHelper.HashString(this) + "::CheckSpn() No client SPN provided, but PolicyEnforcement " + 
                            "is WhenSupported"); 
                    }
                } 

                return result;
            }
            else if (String.Compare(clientSpn, "http/localhost", StringComparison.OrdinalIgnoreCase) == 0) 
            {
                if (Logging.On) 
                { 
                    Logging.PrintInfo(Logging.HttpListener, "HttpListener#" +
                        ValidationHelper.HashString(this) + "::CheckSpn() SSP indicates loopback authentication, skipping SPN check"); 
                }

                return true;
            } 
            else
            { 
                if (Logging.On) 
                {
                    Logging.PrintInfo(Logging.HttpListener, "HttpListener#" + 
                            ValidationHelper.HashString(this) + "::CheckSpn() Client provided SPN '" +
                            clientSpn + "'");
                }
 
                ServiceNameCollection serviceNames = GetServiceNames(policy);
 
                bool found = false; 
                foreach (string serviceName in serviceNames)
                { 
                    if (String.Compare(clientSpn, serviceName, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        found = true;
                        break; 
                    }
                } 
 
                if (Logging.On && !found)
                { 
                    Logging.PrintInfo(Logging.HttpListener, "HttpListener#" +
                            ValidationHelper.HashString(this) + "::CheckSpn() SPN check failed");

                    if (serviceNames.Count == 0) 
                    {
                        Logging.PrintWarning(Logging.HttpListener, "HttpListener#" + 
                                ValidationHelper.HashString(this) + "::CheckSpn() Service Name collection was emtpy!"); 
                    }
                    else 
                    {
                        Logging.PrintInfo(Logging.HttpListener, "HttpListener#" +
                                ValidationHelper.HashString(this) + "::CheckSpn() Dumping Acceptable Service Names");
 
                        foreach (string serviceName in serviceNames)
                        { 
                            Logging.PrintInfo(Logging.HttpListener, "HttpListener#" + 
                                    ValidationHelper.HashString(this) + "::CheckSpn() \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
                { 
                    bool extendedProtectionFailure;
                    string outBlob = null;
                    ChannelBinding binding = GetChannelBinding(connectionId, isSecureConnection, policy, out extendedProtectionFailure);
 
                    if (!extendedProtectionFailure)
                    { 
                        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