[coop] Temporarily restore MonoThreadInfo when TLS destructor runs. Fixes #43099
[mono.git] / mcs / class / referencesource / System / net / System / Net / HttpListenerRequest.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="HttpListenerRequest.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net {
8     using System;
9     using System.Collections;
10     using System.Collections.Generic;
11     using System.Collections.Specialized;
12     using System.IO;
13     using System.Net.Sockets;
14     using System.Net.WebSockets;
15     using System.Runtime.InteropServices;
16     using System.Security.Authentication.ExtendedProtection;
17     using System.Security.Permissions;
18     using System.Globalization;
19     using System.Text;
20     using System.Threading;
21     using System.Threading.Tasks;
22     using System.ComponentModel;
23     using System.Diagnostics;
24     using System.Security;
25     using System.Security.Principal;
26     using System.Security.Cryptography;
27     using System.Security.Cryptography.X509Certificates;
28   
29
30     internal enum ListenerClientCertState {
31         NotInitialized,
32         InProgress,
33         Completed
34     }
35
36     unsafe internal class ListenerClientCertAsyncResult : LazyAsyncResult {
37         private NativeOverlapped* m_pOverlapped;
38         private byte[] m_BackingBuffer;
39         private UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* m_MemoryBlob;
40         private uint m_Size;
41
42         internal NativeOverlapped* NativeOverlapped
43         {
44             get
45             {
46                 return m_pOverlapped;
47             }
48         }
49
50         internal UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* RequestBlob
51         {
52             get
53             {
54                 return m_MemoryBlob;
55             }
56         }
57
58         private static readonly IOCompletionCallback s_IOCallback = new IOCompletionCallback(WaitCallback);
59
60         internal ListenerClientCertAsyncResult(object asyncObject, object userState, AsyncCallback callback, uint size) : base(asyncObject, userState, callback) {
61             // we will use this overlapped structure to issue async IO to ul
62             // the event handle will be put in by the BeginHttpApi2.ERROR_SUCCESS() method
63             Reset(size);
64         }
65
66         internal void Reset(uint size) {
67             if (size == m_Size)
68             {
69                 return;
70             }
71             if (m_Size != 0)
72             {
73                 Overlapped.Free(m_pOverlapped);
74             }
75             m_Size = size;
76             if (size == 0)
77             {
78                 m_pOverlapped = null;
79                 m_MemoryBlob = null;
80                 m_BackingBuffer = null;
81                 return;
82             }
83             m_BackingBuffer = new byte[checked((int) size)];
84             Overlapped overlapped = new Overlapped();
85             overlapped.AsyncResult = this;
86             m_pOverlapped = overlapped.Pack(s_IOCallback, m_BackingBuffer);
87             m_MemoryBlob = (UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO*) Marshal.UnsafeAddrOfPinnedArrayElement(m_BackingBuffer, 0);
88         }
89
90         internal unsafe void IOCompleted(uint errorCode, uint numBytes)
91         {
92             IOCompleted(this, errorCode, numBytes);
93         }
94
95         private static unsafe void IOCompleted(ListenerClientCertAsyncResult asyncResult, uint errorCode, uint numBytes)
96         {
97             HttpListenerRequest httpListenerRequest = (HttpListenerRequest) asyncResult.AsyncObject;
98             object result = null;
99             try {
100                 if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
101                {
102                     //There is a bug that has existed in http.sys since w2k3.  Bytesreceived will only
103                     //return the size of the inital cert structure.  To get the full size,
104                     //we need to add the certificate encoding size as well.
105
106                     UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.RequestBlob;
107                     asyncResult.Reset(numBytes + pClientCertInfo->CertEncodedSize);
108
109                     uint bytesReceived = 0;
110                     errorCode =
111                         UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate(
112                             httpListenerRequest.HttpListenerContext.RequestQueueHandle,
113                             httpListenerRequest.m_ConnectionId,
114                             (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE,
115                             asyncResult.m_MemoryBlob,
116                             asyncResult.m_Size,
117                             &bytesReceived,
118                             asyncResult.m_pOverlapped);
119
120                     if(errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING ||
121                        (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && !HttpListener.SkipIOCPCallbackOnSuccess))
122                     {
123                         return;
124                     }
125                 }
126                 
127                 if (errorCode!=UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) {
128                     asyncResult.ErrorCode = (int)errorCode;
129                     result = new HttpListenerException((int)errorCode);
130                 }
131                 else {
132                     UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.m_MemoryBlob;
133                     if (pClientCertInfo!=null) {
134                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(httpListenerRequest) + "::ProcessClientCertificate() pClientCertInfo:" + ValidationHelper.ToString((IntPtr)pClientCertInfo)
135                             + " pClientCertInfo->CertFlags:" + ValidationHelper.ToString(pClientCertInfo->CertFlags)
136                             + " pClientCertInfo->CertEncodedSize:" + ValidationHelper.ToString(pClientCertInfo->CertEncodedSize)
137                             + " pClientCertInfo->pCertEncoded:" + ValidationHelper.ToString((IntPtr)pClientCertInfo->pCertEncoded)
138                             + " pClientCertInfo->Token:" + ValidationHelper.ToString((IntPtr)pClientCertInfo->Token)
139                             + " pClientCertInfo->CertDeniedByMapper:" + ValidationHelper.ToString(pClientCertInfo->CertDeniedByMapper));
140                         if (pClientCertInfo->pCertEncoded!=null) {
141                             try {
142                                 byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize];
143                                 Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length);
144                                 result = httpListenerRequest.ClientCertificate = new X509Certificate2(certEncoded);
145                             }
146                             catch (CryptographicException exception) {
147                                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(httpListenerRequest) + "::ProcessClientCertificate() caught CryptographicException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception));
148                                 result = exception;
149                             }
150                             catch (SecurityException exception) {
151                                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(httpListenerRequest) + "::ProcessClientCertificate() caught SecurityException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception));
152                                 result = exception;
153                             }
154                         }
155                         httpListenerRequest.SetClientCertificateError((int)pClientCertInfo->CertFlags);
156                     }
157
158                 }
159
160                 // complete the async IO and invoke the callback
161                 GlobalLog.Print("ListenerClientCertAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::WaitCallback() calling Complete()");
162             }
163             catch (Exception exception)
164             {
165                 if (NclUtilities.IsFatal(exception)) throw;
166                 result = exception;
167             }
168             finally {
169                 if(errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING){
170                     httpListenerRequest.ClientCertState = ListenerClientCertState.Completed;
171                 }
172             }
173
174             asyncResult.InvokeCallback(result);
175         }
176
177         private static unsafe void WaitCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) {
178             // take the ListenerClientCertAsyncResult object from the state
179             Overlapped callbackOverlapped = Overlapped.Unpack(nativeOverlapped);
180             ListenerClientCertAsyncResult asyncResult = (ListenerClientCertAsyncResult) callbackOverlapped.AsyncResult;
181
182             GlobalLog.Print("ListenerClientCertAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::WaitCallback() errorCode:[" + errorCode.ToString() + "] numBytes:[" + numBytes.ToString() + "] nativeOverlapped:[" + ((long)nativeOverlapped).ToString() + "]");
183
184             IOCompleted(asyncResult, errorCode, numBytes);
185         }
186
187         // Will be called from the base class upon InvokeCallback()
188         protected override void Cleanup()
189         {
190             if (m_pOverlapped != null)
191             {
192                 m_MemoryBlob = null;
193                 Overlapped.Free(m_pOverlapped);
194                 m_pOverlapped = null;
195             }
196             GC.SuppressFinalize(this);
197             base.Cleanup();
198         }
199
200         ~ListenerClientCertAsyncResult()
201         {
202             if (m_pOverlapped != null && !NclUtilities.HasShutdownStarted)
203             {
204                 Overlapped.Free(m_pOverlapped);
205                 m_pOverlapped = null;  // Must do this in case application calls GC.ReRegisterForFinalize().
206             }
207         }
208     }
209
210
211     public sealed unsafe class HttpListenerRequest/* BaseHttpRequest, */  {
212
213         private Uri m_RequestUri;
214         private ulong m_RequestId;
215         internal ulong m_ConnectionId;
216         private SslStatus m_SslStatus;
217         private string m_RawUrl;
218         private string m_CookedUrlHost;
219         private string m_CookedUrlPath;
220         private string m_CookedUrlQuery;
221         private long m_ContentLength;
222         private Stream m_RequestStream;
223         private string m_HttpMethod;
224         private TriState m_KeepAlive;
225         private Version m_Version;
226         private WebHeaderCollection m_WebHeaders;
227         private IPEndPoint m_LocalEndPoint;
228         private IPEndPoint m_RemoteEndPoint;
229         private BoundaryType m_BoundaryType;
230         private ListenerClientCertState m_ClientCertState;
231         private X509Certificate2 m_ClientCertificate;
232         private int m_ClientCertificateError;
233         private RequestContextBase m_MemoryBlob;
234         private CookieCollection m_Cookies;
235         private HttpListenerContext m_HttpContext;
236         private bool m_IsDisposed = false;
237         internal const uint CertBoblSize = 1500;
238         private string m_ServiceName;
239         private object m_Lock = new object();
240         private List<TokenBinding> m_TokenBindings = null;
241         private int m_TokenBindingVerifyMessageStatus = 0;
242
243         private enum SslStatus : byte
244         {
245             Insecure,
246             NoClientCert,
247             ClientCert
248         }
249
250         internal HttpListenerRequest(HttpListenerContext httpContext, RequestContextBase memoryBlob)
251         {
252             if (Logging.On) Logging.PrintInfo(Logging.HttpListener, this, ".ctor", "httpContext#" + ValidationHelper.HashString(httpContext) + " memoryBlob# " + ValidationHelper.HashString((IntPtr) memoryBlob.RequestBlob));
253             if(Logging.On)Logging.Associate(Logging.HttpListener, this, httpContext);
254             m_HttpContext = httpContext;
255             m_MemoryBlob = memoryBlob;
256             m_BoundaryType = BoundaryType.None;
257
258             // Set up some of these now to avoid refcounting on memory blob later.
259             m_RequestId = memoryBlob.RequestBlob->RequestId;
260             m_ConnectionId = memoryBlob.RequestBlob->ConnectionId;
261             m_SslStatus = memoryBlob.RequestBlob->pSslInfo == null ? SslStatus.Insecure :
262                 memoryBlob.RequestBlob->pSslInfo->SslClientCertNegotiated == 0 ? SslStatus.NoClientCert :
263                 SslStatus.ClientCert;
264             if (memoryBlob.RequestBlob->pRawUrl != null && memoryBlob.RequestBlob->RawUrlLength > 0) {
265                 m_RawUrl = Marshal.PtrToStringAnsi((IntPtr) memoryBlob.RequestBlob->pRawUrl, memoryBlob.RequestBlob->RawUrlLength);
266             }
267             
268             UnsafeNclNativeMethods.HttpApi.HTTP_COOKED_URL cookedUrl = memoryBlob.RequestBlob->CookedUrl;
269             if (cookedUrl.pHost != null && cookedUrl.HostLength > 0) {
270                 m_CookedUrlHost = Marshal.PtrToStringUni((IntPtr)cookedUrl.pHost, cookedUrl.HostLength / 2);
271             }
272             if (cookedUrl.pAbsPath != null && cookedUrl.AbsPathLength > 0) {
273                 m_CookedUrlPath = Marshal.PtrToStringUni((IntPtr)cookedUrl.pAbsPath, cookedUrl.AbsPathLength / 2);
274             }
275             if (cookedUrl.pQueryString != null && cookedUrl.QueryStringLength > 0) {
276                 m_CookedUrlQuery = Marshal.PtrToStringUni((IntPtr)cookedUrl.pQueryString, cookedUrl.QueryStringLength / 2);
277             }
278             m_Version = new Version(memoryBlob.RequestBlob->Version.MajorVersion, memoryBlob.RequestBlob->Version.MinorVersion);
279             m_ClientCertState = ListenerClientCertState.NotInitialized;
280             m_KeepAlive = TriState.Unspecified;
281             GlobalLog.Print("HttpListenerContext#" + ValidationHelper.HashString(this) + "::.ctor() RequestId:" + RequestId + " ConnectionId:" + m_ConnectionId + " RawConnectionId:" + memoryBlob.RequestBlob->RawConnectionId + " UrlContext:" + memoryBlob.RequestBlob->UrlContext + " RawUrl:" + m_RawUrl + " Version:" + m_Version.ToString() + " Secure:" + m_SslStatus.ToString());
282             if(Logging.On)Logging.PrintInfo(Logging.HttpListener, this, ".ctor", "httpContext#"+ ValidationHelper.HashString(httpContext)+ " RequestUri:" + ValidationHelper.ToString(RequestUri) + " Content-Length:" + ValidationHelper.ToString(ContentLength64) + " HTTP Method:" + ValidationHelper.ToString(HttpMethod));
283             // Log headers
284             if(Logging.On) {
285                 StringBuilder sb = new StringBuilder("HttpListenerRequest Headers:\n");
286                 for (int i=0; i<Headers.Count; i++) {
287                     sb.Append("\t");
288                     sb.Append(Headers.GetKey(i));
289                     sb.Append(" : ");
290                     sb.Append(Headers.Get(i));
291                     sb.Append("\n");
292                 }
293                 Logging.PrintInfo(Logging.HttpListener, this, ".ctor", sb.ToString());
294             }
295         }
296
297         internal HttpListenerContext HttpListenerContext {
298             get {
299                 return m_HttpContext;
300             }
301         }
302
303         internal byte[] RequestBuffer
304         {
305             get
306             {
307                 CheckDisposed();
308                 return m_MemoryBlob.RequestBuffer;
309             }
310         }
311
312         internal IntPtr OriginalBlobAddress
313         {
314             get
315             {
316                 CheckDisposed();
317                 return m_MemoryBlob.OriginalBlobAddress;
318             }
319         }
320
321         // Use this to save the blob from dispose if this object was never used (never given to a user) and is about to be
322         // disposed.
323         internal void DetachBlob(RequestContextBase memoryBlob)
324         {
325             if (memoryBlob != null && (object) memoryBlob == (object) m_MemoryBlob)
326             {
327                 m_MemoryBlob = null;
328             }
329         }
330
331         // Finalizes ownership of the memory blob.  DetachBlob can't be called after this.
332         internal void ReleasePins()
333         {
334             m_MemoryBlob.ReleasePins();
335         }
336
337         public Guid RequestTraceIdentifier {
338             get {
339                 Guid guid = new Guid();
340                 *(1+ (ulong *) &guid) = RequestId;
341                 return guid;
342             }
343         }
344
345         internal ulong RequestId {
346             get {
347                 return m_RequestId;
348             }
349         }
350
351         public /* override */ string[] AcceptTypes {
352             get {
353                 return Helpers.ParseMultivalueHeader(GetKnownHeader(HttpRequestHeader.Accept));
354             }
355         }
356
357         public /* override */ Encoding ContentEncoding {
358             get {
359                 if (UserAgent!=null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP")) {
360                     string postDataCharset = Headers["x-up-devcap-post-charset"];
361                     if (postDataCharset!=null && postDataCharset.Length>0) {
362                         try {
363                             return Encoding.GetEncoding(postDataCharset);
364                         }
365                         catch (ArgumentException) {
366                         }
367                     }
368                 }
369                 if (HasEntityBody) {
370                     if (ContentType!=null) {
371                         string charSet = Helpers.GetAttributeFromHeader(ContentType, "charset");
372                         if (charSet!=null) {
373                             try {
374                                 return Encoding.GetEncoding(charSet);
375                             }
376                             catch (ArgumentException) {
377                             }
378                         }
379                     }
380                 }
381                 return Encoding.Default;
382             }
383         }
384
385         public /* override */ long ContentLength64 {
386             get {
387                 if (m_BoundaryType==BoundaryType.None) {
388                     if (HttpWebRequest.ChunkedHeader.Equals(GetKnownHeader(HttpRequestHeader.TransferEncoding), 
389                         StringComparison.OrdinalIgnoreCase)) {
390                         m_BoundaryType = BoundaryType.Chunked;
391                         m_ContentLength = -1;
392                     }
393                     else {
394                         m_ContentLength = 0;
395                         m_BoundaryType = BoundaryType.ContentLength;
396                         string length = GetKnownHeader(HttpRequestHeader.ContentLength);
397                         if (length!=null) {
398                             bool success = long.TryParse(length, NumberStyles.None,  CultureInfo.InvariantCulture.NumberFormat, out m_ContentLength);
399                             if (!success) {
400                                 m_ContentLength = 0;
401                                 m_BoundaryType = BoundaryType.Invalid;
402                             }
403                         }
404                     }
405                 }
406                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ContentLength_get() returning m_ContentLength:" + m_ContentLength + " m_BoundaryType:" + m_BoundaryType);
407                 return m_ContentLength;
408             }
409         }
410
411         public /* override */ string ContentType {
412             get {
413                 return GetKnownHeader(HttpRequestHeader.ContentType);
414             }
415         }
416
417         public /* override */ NameValueCollection Headers {
418             get {
419                 if (m_WebHeaders==null) {
420                     m_WebHeaders = UnsafeNclNativeMethods.HttpApi.GetHeaders(RequestBuffer, OriginalBlobAddress);
421                 }
422                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::Headers_get() returning#" + ValidationHelper.HashString(m_WebHeaders));
423                 return m_WebHeaders;
424             }
425         }
426
427         public /* override */ string HttpMethod {
428             get {
429                 if (m_HttpMethod==null) {
430                     m_HttpMethod = UnsafeNclNativeMethods.HttpApi.GetVerb(RequestBuffer, OriginalBlobAddress);
431                 }
432                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::HttpMethod_get() returning m_HttpMethod:" + ValidationHelper.ToString(m_HttpMethod));
433                 return m_HttpMethod;
434             }
435         }
436
437         public /* override */ Stream InputStream {
438             get {
439                 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "InputStream_get", "");
440                 if (m_RequestStream==null) {
441                     m_RequestStream = HasEntityBody ? new HttpRequestStream(HttpListenerContext) : Stream.Null;
442                 }
443                 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "InputStream_get", "");
444                 return m_RequestStream;
445             }
446         }
447
448         // Requires ControlPrincipal permission if the request was authenticated with Negotiate, NTLM, or Digest.
449         public /* override */ bool IsAuthenticated {
450             get {
451                 IPrincipal user = HttpListenerContext.User;
452                 return user != null && user.Identity != null && user.Identity.IsAuthenticated;
453             }
454         }
455
456         public /* override */ bool IsLocal {
457             get {
458                 return LocalEndPoint.Address.Equals(RemoteEndPoint.Address);
459             }
460         }
461
462         public /* override */ bool IsSecureConnection {
463             get {
464                 return m_SslStatus != SslStatus.Insecure;
465             }
466         }
467
468         public bool IsWebSocketRequest 
469         {
470             get
471             {
472                 if (!WebSocketProtocolComponent.IsSupported)
473                 {
474                     return false;
475                 }
476
477                 bool foundConnectionUpgradeHeader = false;
478                 if (string.IsNullOrEmpty(this.Headers[HttpKnownHeaderNames.Connection]) || string.IsNullOrEmpty(this.Headers[HttpKnownHeaderNames.Upgrade]))
479                 {
480                     return false; 
481                 }
482
483                 foreach (string connection in this.Headers.GetValues(HttpKnownHeaderNames.Connection)) 
484                 {
485                     if (string.Compare(connection, HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) == 0)
486                     {
487                         foundConnectionUpgradeHeader = true;
488                         break;
489                     }
490                 }
491
492                 if (!foundConnectionUpgradeHeader)
493                 {
494                     return false; 
495                 }
496
497                 foreach (string upgrade in this.Headers.GetValues(HttpKnownHeaderNames.Upgrade))
498                 {
499                     if (string.Compare(upgrade, WebSocketHelpers.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase) == 0)
500                     {
501                         return true;
502                     }
503                 }
504
505                 return false; 
506             }
507         }
508
509         public /* override */ NameValueCollection QueryString {
510             get {
511                 NameValueCollection queryString = new NameValueCollection();
512                 Helpers.FillFromString(queryString, Url.Query, true, ContentEncoding);
513                 return queryString;
514             }
515         }
516
517         public /* override */ string RawUrl {
518             get {
519                 return m_RawUrl;
520             }
521         }
522
523         public string ServiceName
524         {
525             get { return m_ServiceName; }
526             internal set { m_ServiceName = value; }
527         }
528
529         public /* override */ Uri Url {
530             get {
531                 return RequestUri;
532             }
533         }
534
535         public /* override */ Uri UrlReferrer {
536             get {
537                 string referrer = GetKnownHeader(HttpRequestHeader.Referer);
538                 if (referrer==null) {
539                     return null;
540                 }
541                 Uri urlReferrer;
542                 bool success = Uri.TryCreate(referrer, UriKind.RelativeOrAbsolute, out urlReferrer);
543                 return success ? urlReferrer : null;
544             }
545         }
546
547         public /* override */ string UserAgent {
548             get {
549                 return GetKnownHeader(HttpRequestHeader.UserAgent);
550             }
551         }
552
553         public /* override */ string UserHostAddress {
554             get {
555                 return LocalEndPoint.ToString();
556             }
557         }
558
559         public /* override */ string UserHostName {
560             get {
561                 return GetKnownHeader(HttpRequestHeader.Host);
562             }
563         }
564
565         public /* override */ string[] UserLanguages {
566             get {
567                 return Helpers.ParseMultivalueHeader(GetKnownHeader(HttpRequestHeader.AcceptLanguage));
568             }
569         }
570
571         public int ClientCertificateError {
572             get {
573                 if (m_ClientCertState == ListenerClientCertState.NotInitialized)
574                     throw new InvalidOperationException(SR.GetString(SR.net_listener_mustcall, "GetClientCertificate()/BeginGetClientCertificate()"));
575                 else if (m_ClientCertState == ListenerClientCertState.InProgress)
576                     throw new InvalidOperationException(SR.GetString(SR.net_listener_mustcompletecall, "GetClientCertificate()/BeginGetClientCertificate()"));
577
578                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ClientCertificateError_get() returning ClientCertificateError:" + ValidationHelper.ToString(m_ClientCertificateError));
579                 return m_ClientCertificateError;
580             }
581         }
582
583         internal X509Certificate2 ClientCertificate {
584             set {
585                 m_ClientCertificate = value;
586             }
587         }
588
589         internal ListenerClientCertState ClientCertState {
590             set {
591                 m_ClientCertState = value;
592             }
593         }
594
595         internal void SetClientCertificateError(int clientCertificateError)
596         {
597             m_ClientCertificateError = clientCertificateError;
598         }
599
600         public X509Certificate2 GetClientCertificate() {
601             if(Logging.On)Logging.Enter(Logging.HttpListener, this, "GetClientCertificate", "");
602             try {
603                 ProcessClientCertificate();
604                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::GetClientCertificate() returning m_ClientCertificate:" + ValidationHelper.ToString(m_ClientCertificate));
605             } finally {
606                 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "GetClientCertificate", ValidationHelper.ToString(m_ClientCertificate));
607             }
608             return m_ClientCertificate;
609         }
610
611         public IAsyncResult BeginGetClientCertificate(AsyncCallback requestCallback, object state) {
612             if(Logging.On)Logging.PrintInfo(Logging.HttpListener, this, "BeginGetClientCertificate", "");
613             return AsyncProcessClientCertificate(requestCallback, state);
614         }
615
616         public X509Certificate2 EndGetClientCertificate(IAsyncResult asyncResult) {
617             if(Logging.On)Logging.Enter(Logging.HttpListener, this, "EndGetClientCertificate", "");
618             X509Certificate2 clientCertificate = null;
619             try {
620                 if (asyncResult==null) {
621                     throw new ArgumentNullException("asyncResult");
622                 }
623                 ListenerClientCertAsyncResult clientCertAsyncResult = asyncResult as ListenerClientCertAsyncResult;
624                 if (clientCertAsyncResult==null || clientCertAsyncResult.AsyncObject!=this) {
625                     throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult");
626                 }
627                 if (clientCertAsyncResult.EndCalled) {
628                     throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndGetClientCertificate"));
629                 }
630                 clientCertAsyncResult.EndCalled = true;
631                 clientCertificate = clientCertAsyncResult.InternalWaitForCompletion() as X509Certificate2;
632                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::EndGetClientCertificate() returning m_ClientCertificate:" + ValidationHelper.ToString(m_ClientCertificate));
633             } finally {
634                 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "EndGetClientCertificate", ValidationHelper.HashString(clientCertificate));
635             }
636             return clientCertificate;
637         }
638
639         //************* Task-based async public methods *************************
640         [HostProtection(ExternalThreading = true)]
641         public Task<X509Certificate2> GetClientCertificateAsync()
642         {
643             return Task<X509Certificate2>.Factory.FromAsync(BeginGetClientCertificate, EndGetClientCertificate, null);
644         }
645
646
647         public TransportContext TransportContext
648         {
649             get
650             {
651                 return new HttpListenerRequestContext(this);
652             }
653         }
654
655         private CookieCollection ParseCookies(Uri uri, string setCookieHeader) {
656             GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ParseCookies() uri:" + uri + " setCookieHeader:" + setCookieHeader);
657             CookieCollection cookies = new CookieCollection();
658             CookieParser parser = new CookieParser(setCookieHeader);
659             for (;;) {
660                 Cookie cookie = parser.GetServer();
661                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ParseCookies() CookieParser returned cookie:" + ValidationHelper.ToString(cookie));
662                 if (cookie==null) {
663                     // EOF, done.
664                     break;
665                 }
666                 if (cookie.Name.Length==0) {
667                     continue;
668                 }
669                 cookies.InternalAdd(cookie, true);
670             }
671             return cookies;
672         }
673
674         public CookieCollection Cookies {
675             get {
676                 if (m_Cookies==null) {
677                     string cookieString = GetKnownHeader(HttpRequestHeader.Cookie);
678                     if (cookieString!=null && cookieString.Length>0) {
679                         m_Cookies = ParseCookies(RequestUri, cookieString);
680                     }
681                     if (m_Cookies==null) {
682                         m_Cookies = new CookieCollection();
683                     }
684                     if (HttpListenerContext.PromoteCookiesToRfc2965) {
685                         for (int index=0; index<m_Cookies.Count; index++) {
686                             if (m_Cookies[index].Variant==CookieVariant.Rfc2109) {
687                                 m_Cookies[index].Variant = CookieVariant.Rfc2965;
688                             }
689                         }
690                     }
691                 }
692                 return m_Cookies;
693             }
694         }
695
696         public Version ProtocolVersion {
697             get {
698                 return m_Version;
699             }
700         }
701
702         public /* override */ bool HasEntityBody {
703             get {
704                // accessing the ContentLength property delay creates m_BoundaryType
705                 return (ContentLength64 > 0 && m_BoundaryType == BoundaryType.ContentLength) ||
706                     m_BoundaryType == BoundaryType.Chunked || m_BoundaryType == BoundaryType.Multipart;
707             }
708         }
709
710         public /* override */ bool KeepAlive
711         {
712             get
713             {
714                 if (m_KeepAlive == TriState.Unspecified)
715                 {
716                     string header = Headers[HttpKnownHeaderNames.ProxyConnection];
717                     if (string.IsNullOrEmpty(header))
718                     {
719                         header = GetKnownHeader(HttpRequestHeader.Connection);
720                     }
721                     if (string.IsNullOrEmpty(header))
722                     {
723                         if (ProtocolVersion >= HttpVersion.Version11)
724                         {
725                             m_KeepAlive = TriState.True;
726                         }
727                         else
728                         {
729                             header = GetKnownHeader(HttpRequestHeader.KeepAlive);
730                             m_KeepAlive = string.IsNullOrEmpty(header) ? TriState.False : TriState.True;
731                         }
732                     }
733                     else
734                     {
735                         header = header.ToLower(CultureInfo.InvariantCulture);
736                         m_KeepAlive = header.IndexOf("close") < 0 || header.IndexOf("keep-alive") >= 0 ? TriState.True : TriState.False;
737                     }
738                 }
739
740                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::KeepAlive_get() returning:" + m_KeepAlive);
741                 return m_KeepAlive == TriState.True;
742             }
743         }
744
745         public /* override */ IPEndPoint RemoteEndPoint {
746             get {
747                 if (m_RemoteEndPoint==null) {
748                     m_RemoteEndPoint = UnsafeNclNativeMethods.HttpApi.GetRemoteEndPoint(RequestBuffer, OriginalBlobAddress);
749                 }
750                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::RemoteEndPoint_get() returning:" + m_RemoteEndPoint);
751                 return m_RemoteEndPoint;
752             }
753         }
754
755         public /* override */ IPEndPoint LocalEndPoint {
756             get {
757                 if (m_LocalEndPoint==null) {
758                     m_LocalEndPoint = UnsafeNclNativeMethods.HttpApi.GetLocalEndPoint(RequestBuffer, OriginalBlobAddress);
759                 }
760                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::LocalEndPoint_get() returning:" + m_LocalEndPoint);
761                 return m_LocalEndPoint;
762             }
763         }
764
765         //should only be called from httplistenercontext
766         internal void Close() {
767             if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Close", "");
768             RequestContextBase memoryBlob = m_MemoryBlob;
769             if (memoryBlob != null)
770             {
771                 memoryBlob.Close();
772                 m_MemoryBlob = null;
773             }
774             m_IsDisposed = true;
775             if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Close", "");
776         }
777
778
779         private ListenerClientCertAsyncResult AsyncProcessClientCertificate(AsyncCallback requestCallback, object state) {
780             if (m_ClientCertState == ListenerClientCertState.InProgress)
781                 throw new InvalidOperationException(SR.GetString(SR.net_listener_callinprogress, "GetClientCertificate()/BeginGetClientCertificate()"));
782             m_ClientCertState = ListenerClientCertState.InProgress;
783             HttpListenerContext.EnsureBoundHandle();
784
785             ListenerClientCertAsyncResult asyncResult = null;
786                         //--------------------------------------------------------------------
787                         //When you configure the HTTP.SYS with a flag value 2
788                         //which means require client certificates, when the client makes the
789                         //initial SSL connection, server (HTTP.SYS) demands the client certificate
790                         //
791                         //Some apps may not want to demand the client cert at the beginning
792                         //perhaps server the default.htm. In this case the HTTP.SYS is configured
793                         //with a flag value other than 2, whcih means that the client certificate is
794                         //optional.So intially when SSL is established HTTP.SYS won't ask for client
795                         //certificate. This works fine for the default.htm in the case above
796                         //However, if the app wants to demand a client certficate at a later time
797                         //perhaps showing "YOUR ORDERS" page, then the server wans to demand
798                         //Client certs. this will inturn makes HTTP.SYS to do the
799                         //SEC_I_RENOGOTIATE through which the client cert demand is made
800                         //
801                         //THE 
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816             if (m_SslStatus != SslStatus.Insecure)
817             {
818                 // at this point we know that DefaultFlags has the 2 bit set (Negotiate Client certificate)
819                 // the cert, though might or might not be there. try to retrieve it
820                 // this number is the same that IIS decided to use
821                 uint size = CertBoblSize;
822                 asyncResult = new ListenerClientCertAsyncResult(this, state, requestCallback, size);
823                 try {
824                     while (true)
825                     {
826                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() calling UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate size:" + size);
827                         uint bytesReceived = 0;
828
829                         uint statusCode =
830                             UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate(
831                                 HttpListenerContext.RequestQueueHandle,
832                                 m_ConnectionId,
833                                 (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE,
834                                 asyncResult.RequestBlob,
835                                 size,
836                                 &bytesReceived,
837                                 asyncResult.NativeOverlapped);
838
839                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() call to UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate returned:" + statusCode + " bytesReceived:" + bytesReceived);
840                         if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
841                         {
842                             UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.RequestBlob;
843                             size = bytesReceived + pClientCertInfo->CertEncodedSize;
844                             asyncResult.Reset(size);
845                             continue;
846                         }
847                         if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS &&
848                             statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) {
849                             // someother bad error, possible(?) return values are:
850                             // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED
851                             // Also ERROR_BAD_DATA if we got it twice or it reported smaller size buffer required.
852                             throw new HttpListenerException((int)statusCode);
853                         }
854
855                         if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS &&
856                             HttpListener.SkipIOCPCallbackOnSuccess)
857                         {
858                             asyncResult.IOCompleted(statusCode, bytesReceived);
859                         }
860                         break;
861                     }
862                 }
863                 catch {
864                     if (asyncResult!=null) {
865                         asyncResult.InternalCleanup();
866                     }
867                     throw;
868                 }
869             } else {
870                 asyncResult = new ListenerClientCertAsyncResult(this, state, requestCallback, 0);
871                 asyncResult.InvokeCallback();
872             }
873             return asyncResult;
874         }
875
876         private void ProcessClientCertificate() {
877             if (m_ClientCertState == ListenerClientCertState.InProgress)
878                 throw new InvalidOperationException(SR.GetString(SR.net_listener_callinprogress, "GetClientCertificate()/BeginGetClientCertificate()"));
879             m_ClientCertState = ListenerClientCertState.InProgress;
880             GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate()");
881                         //--------------------------------------------------------------------
882                         //When you configure the HTTP.SYS with a flag value 2
883                         //which means require client certificates, when the client makes the
884                         //initial SSL connection, server (HTTP.SYS) demands the client certificate
885                         //
886                         //Some apps may not want to demand the client cert at the beginning
887                         //perhaps server the default.htm. In this case the HTTP.SYS is configured
888                         //with a flag value other than 2, whcih means that the client certificate is
889                         //optional.So intially when SSL is established HTTP.SYS won't ask for client
890                         //certificate. This works fine for the default.htm in the case above
891                         //However, if the app wants to demand a client certficate at a later time
892                         //perhaps showing "YOUR ORDERS" page, then the server wans to demand
893                         //Client certs. this will inturn makes HTTP.SYS to do the
894                         //SEC_I_RENOGOTIATE through which the client cert demand is made
895                         //
896                         //THE 
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911             if (m_SslStatus != SslStatus.Insecure)
912             {
913                 // at this point we know that DefaultFlags has the 2 bit set (Negotiate Client certificate)
914                 // the cert, though might or might not be there. try to retrieve it
915                 // this number is the same that IIS decided to use
916                 uint size = CertBoblSize;
917                 while (true)
918                 {
919                     byte[] clientCertInfoBlob = new byte[checked((int) size)];
920                     fixed (byte* pClientCertInfoBlob = clientCertInfoBlob)
921                     {
922                         UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = (UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO*) pClientCertInfoBlob;
923
924                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() calling UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate size:" + size);
925                         uint bytesReceived = 0;
926
927                         uint statusCode =
928                             UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate(
929                                 HttpListenerContext.RequestQueueHandle,
930                                 m_ConnectionId,
931                                 (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE,
932                                 pClientCertInfo,
933                                 size,
934                                 &bytesReceived,
935                                 null);
936
937                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() call to UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate returned:" + statusCode + " bytesReceived:" + bytesReceived);
938                         if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) {
939                             size = bytesReceived + pClientCertInfo->CertEncodedSize;
940                             continue;
941                         }
942                         else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) {
943                             if (pClientCertInfo!=null) {
944                                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() pClientCertInfo:" + ValidationHelper.ToString((IntPtr)pClientCertInfo)
945                                     + " pClientCertInfo->CertFlags:" + ValidationHelper.ToString(pClientCertInfo->CertFlags)
946                                     + " pClientCertInfo->CertEncodedSize:" + ValidationHelper.ToString(pClientCertInfo->CertEncodedSize)
947                                     + " pClientCertInfo->pCertEncoded:" + ValidationHelper.ToString((IntPtr)pClientCertInfo->pCertEncoded)
948                                     + " pClientCertInfo->Token:" + ValidationHelper.ToString((IntPtr)pClientCertInfo->Token)
949                                     + " pClientCertInfo->CertDeniedByMapper:" + ValidationHelper.ToString(pClientCertInfo->CertDeniedByMapper));
950                                 if (pClientCertInfo->pCertEncoded!=null) {
951                                     try {
952                                         byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize];
953                                         Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length);
954                                         m_ClientCertificate = new X509Certificate2(certEncoded);
955                                     }
956                                     catch (CryptographicException exception) {
957                                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() caught CryptographicException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception));
958                                     }
959                                     catch (SecurityException exception) {
960                                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() caught SecurityException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception));
961                                     }
962                                 }
963                                 m_ClientCertificateError = (int)pClientCertInfo->CertFlags;
964                             }
965                         }
966                         else {
967                             GlobalLog.Assert(statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND, "HttpListenerRequest#{0}::ProcessClientCertificate()|Call to UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate() failed with statusCode {1}.", ValidationHelper.HashString(this), statusCode);
968                         }
969                     }
970                     break;
971                 }
972             }
973             m_ClientCertState = ListenerClientCertState.Completed;
974         }
975
976         private string RequestScheme {
977             get {
978                 return IsSecureConnection ? "https" : "http";
979             }
980         }
981
982         private Uri RequestUri {
983             get {
984                 if (m_RequestUri == null) {
985
986                     m_RequestUri = HttpListenerRequestUriBuilder.GetRequestUri(
987                         m_RawUrl, RequestScheme, m_CookedUrlHost, m_CookedUrlPath, m_CookedUrlQuery);
988                 }
989
990                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::RequestUri_get() returning m_RequestUri:" + ValidationHelper.ToString(m_RequestUri));
991                 return m_RequestUri;
992             }
993         }
994
995         /*
996         private DateTime IfModifiedSince {
997             get {
998                 string headerValue = GetKnownHeader(HttpRequestHeader.IfModifiedSince);
999                 if (headerValue==null) {
1000                     return DateTime.Now;
1001                 }
1002                 return DateTime.Parse(headerValue, CultureInfo.InvariantCulture);
1003             }
1004         }
1005         */
1006
1007         private string GetKnownHeader(HttpRequestHeader header) {
1008             return UnsafeNclNativeMethods.HttpApi.GetKnownHeader(RequestBuffer, OriginalBlobAddress, (int) header);
1009         }
1010
1011         internal ChannelBinding GetChannelBinding()
1012         {
1013             return HttpListenerContext.Listener.GetChannelBindingFromTls(m_ConnectionId);
1014         }
1015
1016         internal IEnumerable<TokenBinding> GetTlsTokenBindings() {
1017
1018             // Try to get the token binding if not created.
1019             if (Volatile.Read(ref m_TokenBindings) == null)
1020             {
1021                 lock (m_Lock)
1022                 {
1023                     if (Volatile.Read(ref m_TokenBindings) == null)
1024                     {
1025                         // If token binding is supported on the machine get it else create empty list.
1026                         if (UnsafeNclNativeMethods.TokenBindingOSHelper.SupportsTokenBinding)
1027                         {
1028                             ProcessTlsTokenBindings();
1029                         }
1030                         else
1031                         {
1032                             m_TokenBindings = new List<TokenBinding>();
1033                         }
1034                     }
1035                 }
1036             }
1037
1038             // If the cached status is not success throw exception, else return the token binding
1039             if (0 != m_TokenBindingVerifyMessageStatus)
1040             {
1041                 throw new HttpListenerException(m_TokenBindingVerifyMessageStatus);
1042             }
1043             else
1044             {
1045                 return m_TokenBindings.AsReadOnly();
1046             }
1047         }
1048
1049         /// <summary>
1050         /// Process the token binding information in the request and populate m_TokenBindings 
1051         /// This method should be called once only as token binding information is cached in m_TokenBindings for further use.
1052         /// </summary>
1053         private void ProcessTlsTokenBindings() {
1054
1055             Debug.Assert(m_TokenBindings == null);
1056
1057             if (m_TokenBindings != null)
1058             {
1059                 return;
1060             }
1061
1062             m_TokenBindings = new List<TokenBinding>();
1063
1064             UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO* pTokenBindingInfo = GetTlsTokenBindingRequestInfo();
1065
1066             if (pTokenBindingInfo == null)
1067             {
1068                 // The current request isn't over TLS or the client or server doesn't support the token binding
1069                 // protocol. This isn't an error; just return "nothing here".
1070                 return;
1071             }
1072
1073             UnsafeNclNativeMethods.HttpApi.HeapAllocHandle handle = null;
1074             m_TokenBindingVerifyMessageStatus = -1;
1075             m_TokenBindingVerifyMessageStatus = UnsafeNclNativeMethods.HttpApi.TokenBindingVerifyMessage(
1076                 pTokenBindingInfo->TokenBinding,
1077                 pTokenBindingInfo->TokenBindingSize,
1078                 pTokenBindingInfo->KeyType,
1079                 pTokenBindingInfo->TlsUnique,
1080                 pTokenBindingInfo->TlsUniqueSize,
1081                 out handle);
1082
1083             if (m_TokenBindingVerifyMessageStatus != 0)
1084             {
1085                 throw new HttpListenerException(m_TokenBindingVerifyMessageStatus);
1086             }
1087             
1088             Debug.Assert(handle != null);
1089             Debug.Assert(!handle.IsInvalid);
1090
1091             using (handle)
1092             {
1093                 UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST* pResultList = (UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST*)handle.DangerousGetHandle();
1094                 for (int i = 0; i < pResultList->resultCount; i++)
1095                 {
1096                     UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_DATA* pThisResultData = &pResultList->resultData[i];
1097
1098                     if (pThisResultData != null)
1099                     {
1100                         // Per http://tools.ietf.org/html/draft-ietf-tokbind-protocol-00, Sec. 4,
1101                         // We'll strip off the token binding type and return the remainder as an opaque blob.
1102                         Debug.Assert((long)(&pThisResultData->identifierData->hashAlgorithm) == (long)(pThisResultData->identifierData) + 1);
1103                         byte[] retVal = new byte[pThisResultData->identifierSize - 1];
1104                         Marshal.Copy((IntPtr)(&pThisResultData->identifierData->hashAlgorithm), retVal, 0, retVal.Length);
1105
1106                         if (pThisResultData->identifierData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_PROVIDED)
1107                         {
1108                             m_TokenBindings.Add(new TokenBinding(TokenBindingType.Provided, retVal));
1109                         }
1110                         else if (pThisResultData->identifierData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_REFERRED)
1111                         {
1112                             m_TokenBindings.Add(new TokenBinding(TokenBindingType.Referred, retVal));
1113                         }
1114                     }
1115                 }
1116             }
1117         }
1118
1119         private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO* GetTlsTokenBindingRequestInfo() {
1120             fixed (byte* pMemoryBlob = RequestBuffer)
1121             {
1122                 UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2* request = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)pMemoryBlob;
1123
1124                 for (int i = 0; i < request->RequestInfoCount; i++)
1125                 {
1126                     UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_INFO* pThisInfo = &request->pRequestInfo[i];
1127                     if (pThisInfo != null && pThisInfo->InfoType == UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslTokenBinding)
1128                     {
1129                         return (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO*)pThisInfo->pInfo;
1130                     }
1131                 }
1132             }
1133             return null;
1134         }
1135
1136         internal void CheckDisposed() {
1137             if (m_IsDisposed) {
1138                 throw new ObjectDisposedException(this.GetType().FullName);
1139             }
1140         }
1141
1142         // <
1143
1144
1145
1146
1147         static class Helpers {
1148             //
1149             // Get attribute off header value
1150             //
1151             internal static String GetAttributeFromHeader(String headerValue, String attrName) {
1152                 if (headerValue == null)
1153                     return null;
1154
1155                 int l = headerValue.Length;
1156                 int k = attrName.Length;
1157
1158                // find properly separated attribute name
1159                 int i = 1; // start searching from 1
1160
1161                 while (i < l) {
1162                     i = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, i, CompareOptions.IgnoreCase);
1163                     if (i < 0)
1164                         break;
1165                     if (i+k >= l)
1166                         break;
1167
1168                     char chPrev = headerValue[i-1];
1169                     char chNext = headerValue[i+k];
1170                     if ((chPrev == ';' || chPrev == ',' || Char.IsWhiteSpace(chPrev)) && (chNext == '=' || Char.IsWhiteSpace(chNext)))
1171                         break;
1172
1173                     i += k;
1174                 }
1175
1176                 if (i < 0 || i >= l)
1177                     return null;
1178
1179                // skip to '=' and the following whitespaces
1180                 i += k;
1181                 while (i < l && Char.IsWhiteSpace(headerValue[i]))
1182                     i++;
1183                 if (i >= l || headerValue[i] != '=')
1184                     return null;
1185                 i++;
1186                 while (i < l && Char.IsWhiteSpace(headerValue[i]))
1187                     i++;
1188                 if (i >= l)
1189                     return null;
1190
1191                // parse the value
1192                 String attrValue = null;
1193
1194                 int j;
1195
1196                 if (i < l && headerValue[i] == '"') {
1197                     if (i == l-1)
1198                         return null;
1199                     j = headerValue.IndexOf('"', i+1);
1200                     if (j < 0 || j == i+1)
1201                         return null;
1202
1203                     attrValue = headerValue.Substring(i+1, j-i-1).Trim();
1204                 }
1205                 else {
1206                     for (j = i; j < l; j++) {
1207                         if (headerValue[j] == ' ' || headerValue[j] == ',')
1208                             break;
1209                     }
1210
1211                     if (j == i)
1212                         return null;
1213
1214                     attrValue = headerValue.Substring(i, j-i).Trim();
1215                 }
1216
1217                 return attrValue;
1218             }
1219
1220             internal static String[] ParseMultivalueHeader(String s) {
1221                 if (s == null)
1222                     return null;
1223
1224                 int l = s.Length;
1225
1226                // collect comma-separated values into list
1227
1228                 ArrayList values = new ArrayList();
1229                 int i = 0;
1230
1231                 while (i < l) {
1232                    // find next ,
1233                     int ci = s.IndexOf(',', i);
1234                     if (ci < 0)
1235                         ci = l;
1236
1237                    // append corresponding server value
1238                     values.Add(s.Substring(i, ci-i));
1239
1240                    // move to next
1241                     i = ci+1;
1242
1243                    // skip leading space
1244                     if (i < l && s[i] == ' ')
1245                         i++;
1246                 }
1247
1248                // return list as array of strings
1249
1250                 int n = values.Count;
1251                 String[] strings;
1252
1253                 // if n is 0 that means s was empty string
1254
1255                 if (n == 0) {
1256                     strings = new String[1];
1257                     strings[0] = String.Empty;
1258                 }
1259                 else {
1260                     strings = new String[n];
1261                     values.CopyTo(0, strings, 0, n);
1262                 }
1263                 return strings;
1264             }
1265
1266
1267             private static string UrlDecodeStringFromStringInternal(string s, Encoding e) {
1268                 int count = s.Length;
1269                 UrlDecoder helper = new UrlDecoder(count, e);
1270
1271                 // go through the string's chars collapsing %XX and %uXXXX and
1272                 // appending each char as char, with exception of %XX constructs
1273                 // that are appended as bytes
1274
1275                 for (int pos = 0; pos < count; pos++) {
1276                     char ch = s[pos];
1277
1278                     if (ch == '+') {
1279                         ch = ' ';
1280                     }
1281                     else if (ch == '%' && pos < count-2) {
1282                         if (s[pos+1] == 'u' && pos < count-5) {
1283                             int h1 = HexToInt(s[pos+2]);
1284                             int h2 = HexToInt(s[pos+3]);
1285                             int h3 = HexToInt(s[pos+4]);
1286                             int h4 = HexToInt(s[pos+5]);
1287
1288                             if (h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0) {   // valid 4 hex chars
1289                                 ch = (char)((h1 << 12) | (h2 << 8) | (h3 << 4) | h4);
1290                                 pos += 5;
1291
1292                                 // only add as char
1293                                 helper.AddChar(ch);
1294                                 continue;
1295                             }
1296                         }
1297                         else {
1298                             int h1 = HexToInt(s[pos+1]);
1299                             int h2 = HexToInt(s[pos+2]);
1300
1301                             if (h1 >= 0 && h2 >= 0) {     // valid 2 hex chars
1302                                 byte b = (byte)((h1 << 4) | h2);
1303                                 pos += 2;
1304
1305                                 // don't add as char
1306                                 helper.AddByte(b);
1307                                 continue;
1308                             }
1309                         }
1310                     }
1311
1312                     if ((ch & 0xFF80) == 0)
1313                         helper.AddByte((byte)ch); // 7 bit have to go as bytes because of Unicode
1314                     else
1315                         helper.AddChar(ch);
1316                 }
1317
1318                 return helper.GetString();
1319             }
1320
1321             private static int HexToInt(char h) {
1322                 return( h >= '0' && h <= '9' ) ? h - '0' :
1323                 ( h >= 'a' && h <= 'f' ) ? h - 'a' + 10 :
1324                 ( h >= 'A' && h <= 'F' ) ? h - 'A' + 10 :
1325                 -1;
1326             }
1327
1328             private class UrlDecoder {
1329                 private int _bufferSize;
1330
1331                 // Accumulate characters in a special array
1332                 private int _numChars;
1333                 private char[] _charBuffer;
1334
1335                 // Accumulate bytes for decoding into characters in a special array
1336                 private int _numBytes;
1337                 private byte[] _byteBuffer;
1338
1339                 // Encoding to convert chars to bytes
1340                 private Encoding _encoding;
1341
1342                 private void FlushBytes() {
1343                     if (_numBytes > 0) {
1344                         _numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars);
1345                         _numBytes = 0;
1346                     }
1347                 }
1348
1349                 internal UrlDecoder(int bufferSize, Encoding encoding) {
1350                     _bufferSize = bufferSize;
1351                     _encoding = encoding;
1352
1353                     _charBuffer = new char[bufferSize];
1354                     // byte buffer created on demand
1355                 }
1356
1357                 internal void AddChar(char ch) {
1358                     if (_numBytes > 0)
1359                         FlushBytes();
1360
1361                     _charBuffer[_numChars++] = ch;
1362                 }
1363
1364                 internal void AddByte(byte b) {
1365                     // if there are no pending bytes treat 7 bit bytes as characters
1366                     // this optimization is temp disable as it doesn't work for some encodings
1367     /*
1368                     if (_numBytes == 0 && ((b & 0x80) == 0)) {
1369                         AddChar((char)b);
1370                     }
1371                     else
1372     */
1373                     {
1374                         if (_byteBuffer == null)
1375                             _byteBuffer = new byte[_bufferSize];
1376
1377                         _byteBuffer[_numBytes++] = b;
1378                     }
1379                 }
1380
1381                 internal String GetString() {
1382                     if (_numBytes > 0)
1383                         FlushBytes();
1384
1385                     if (_numChars > 0)
1386                         return new String(_charBuffer, 0, _numChars);
1387                     else
1388                         return String.Empty;
1389                 }
1390             }
1391
1392
1393             internal static void FillFromString(NameValueCollection nvc, String s, bool urlencoded, Encoding encoding) {
1394                 int l = (s != null) ? s.Length : 0;
1395                 int i = (s.Length>0 && s[0]=='?') ? 1 : 0;
1396
1397                 while (i < l) {
1398                     // find next & while noting first = on the way (and if there are more)
1399
1400                     int si = i;
1401                     int ti = -1;
1402
1403                     while (i < l) {
1404                         char ch = s[i];
1405
1406                         if (ch == '=') {
1407                             if (ti < 0)
1408                                 ti = i;
1409                         }
1410                         else if (ch == '&') {
1411                             break;
1412                         }
1413
1414                         i++;
1415                     }
1416
1417                     // extract the name / value pair
1418
1419                     String name = null;
1420                     String value = null;
1421
1422                     if (ti >= 0) {
1423                         name = s.Substring(si, ti-si);
1424                         value = s.Substring(ti+1, i-ti-1);
1425                     }
1426                     else {
1427                         value = s.Substring(si, i-si);
1428                     }
1429
1430                     // add name / value pair to the collection
1431
1432                     if (urlencoded)
1433                         nvc.Add(
1434                            name == null ? null : UrlDecodeStringFromStringInternal(name, encoding),
1435                            UrlDecodeStringFromStringInternal(value, encoding));
1436                     else
1437                         nvc.Add(name, value);
1438
1439                     // trailing '&'
1440
1441                     if (i == l-1 && s[i] == '&')
1442                         nvc.Add(null, "");
1443
1444                     i++;
1445                 }
1446             }
1447         }
1448     }
1449 }