Updates referencesource to .NET 4.7
[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         // Note: RequestBuffer may get moved in memory. If you dereference a pointer from inside the RequestBuffer, 
304         // you must use 'OriginalBlobAddress' below to adjust the location of the pointer to match the location of
305         // RequestBuffer.
306         // 
307
308         internal byte[] RequestBuffer
309         {
310             get
311             {
312                 CheckDisposed();
313                 return m_MemoryBlob.RequestBuffer;
314             }
315         }
316
317         internal IntPtr OriginalBlobAddress
318         {
319             get
320             {
321                 CheckDisposed();
322                 return m_MemoryBlob.OriginalBlobAddress;
323             }
324         }
325
326         // Use this to save the blob from dispose if this object was never used (never given to a user) and is about to be
327         // disposed.
328         internal void DetachBlob(RequestContextBase memoryBlob)
329         {
330             if (memoryBlob != null && (object) memoryBlob == (object) m_MemoryBlob)
331             {
332                 m_MemoryBlob = null;
333             }
334         }
335
336         // Finalizes ownership of the memory blob.  DetachBlob can't be called after this.
337         internal void ReleasePins()
338         {
339             m_MemoryBlob.ReleasePins();
340         }
341
342         public Guid RequestTraceIdentifier {
343             get {
344                 Guid guid = new Guid();
345                 *(1+ (ulong *) &guid) = RequestId;
346                 return guid;
347             }
348         }
349
350         internal ulong RequestId {
351             get {
352                 return m_RequestId;
353             }
354         }
355
356         public /* override */ string[] AcceptTypes {
357             get {
358                 return Helpers.ParseMultivalueHeader(GetKnownHeader(HttpRequestHeader.Accept));
359             }
360         }
361
362         public /* override */ Encoding ContentEncoding {
363             get {
364                 if (UserAgent!=null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP")) {
365                     string postDataCharset = Headers["x-up-devcap-post-charset"];
366                     if (postDataCharset!=null && postDataCharset.Length>0) {
367                         try {
368                             return Encoding.GetEncoding(postDataCharset);
369                         }
370                         catch (ArgumentException) {
371                         }
372                     }
373                 }
374                 if (HasEntityBody) {
375                     if (ContentType!=null) {
376                         string charSet = Helpers.GetAttributeFromHeader(ContentType, "charset");
377                         if (charSet!=null) {
378                             try {
379                                 return Encoding.GetEncoding(charSet);
380                             }
381                             catch (ArgumentException) {
382                             }
383                         }
384                     }
385                 }
386                 return Encoding.Default;
387             }
388         }
389
390         public /* override */ long ContentLength64 {
391             get {
392                 if (m_BoundaryType==BoundaryType.None) {
393                     if (HttpWebRequest.ChunkedHeader.Equals(GetKnownHeader(HttpRequestHeader.TransferEncoding), 
394                         StringComparison.OrdinalIgnoreCase)) {
395                         m_BoundaryType = BoundaryType.Chunked;
396                         m_ContentLength = -1;
397                     }
398                     else {
399                         m_ContentLength = 0;
400                         m_BoundaryType = BoundaryType.ContentLength;
401                         string length = GetKnownHeader(HttpRequestHeader.ContentLength);
402                         if (length!=null) {
403                             bool success = long.TryParse(length, NumberStyles.None,  CultureInfo.InvariantCulture.NumberFormat, out m_ContentLength);
404                             if (!success) {
405                                 m_ContentLength = 0;
406                                 m_BoundaryType = BoundaryType.Invalid;
407                             }
408                         }
409                     }
410                 }
411                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ContentLength_get() returning m_ContentLength:" + m_ContentLength + " m_BoundaryType:" + m_BoundaryType);
412                 return m_ContentLength;
413             }
414         }
415
416         public /* override */ string ContentType {
417             get {
418                 return GetKnownHeader(HttpRequestHeader.ContentType);
419             }
420         }
421
422         public /* override */ NameValueCollection Headers {
423             get {
424                 if (m_WebHeaders==null) {
425                     m_WebHeaders = UnsafeNclNativeMethods.HttpApi.GetHeaders(RequestBuffer, OriginalBlobAddress);
426                 }
427                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::Headers_get() returning#" + ValidationHelper.HashString(m_WebHeaders));
428                 return m_WebHeaders;
429             }
430         }
431
432         public /* override */ string HttpMethod {
433             get {
434                 if (m_HttpMethod==null) {
435                     m_HttpMethod = UnsafeNclNativeMethods.HttpApi.GetVerb(RequestBuffer, OriginalBlobAddress);
436                 }
437                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::HttpMethod_get() returning m_HttpMethod:" + ValidationHelper.ToString(m_HttpMethod));
438                 return m_HttpMethod;
439             }
440         }
441
442         public /* override */ Stream InputStream {
443             get {
444                 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "InputStream_get", "");
445                 if (m_RequestStream==null) {
446                     m_RequestStream = HasEntityBody ? new HttpRequestStream(HttpListenerContext) : Stream.Null;
447                 }
448                 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "InputStream_get", "");
449                 return m_RequestStream;
450             }
451         }
452
453         // Requires ControlPrincipal permission if the request was authenticated with Negotiate, NTLM, or Digest.
454         public /* override */ bool IsAuthenticated {
455             get {
456                 IPrincipal user = HttpListenerContext.User;
457                 return user != null && user.Identity != null && user.Identity.IsAuthenticated;
458             }
459         }
460
461         public /* override */ bool IsLocal {
462             get {
463                 return LocalEndPoint.Address.Equals(RemoteEndPoint.Address);
464             }
465         }
466
467         public /* override */ bool IsSecureConnection {
468             get {
469                 return m_SslStatus != SslStatus.Insecure;
470             }
471         }
472
473         public bool IsWebSocketRequest 
474         {
475             get
476             {
477                 if (!WebSocketProtocolComponent.IsSupported)
478                 {
479                     return false;
480                 }
481
482                 bool foundConnectionUpgradeHeader = false;
483                 if (string.IsNullOrEmpty(this.Headers[HttpKnownHeaderNames.Connection]) || string.IsNullOrEmpty(this.Headers[HttpKnownHeaderNames.Upgrade]))
484                 {
485                     return false; 
486                 }
487
488                 foreach (string connection in this.Headers.GetValues(HttpKnownHeaderNames.Connection)) 
489                 {
490                     if (string.Compare(connection, HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) == 0)
491                     {
492                         foundConnectionUpgradeHeader = true;
493                         break;
494                     }
495                 }
496
497                 if (!foundConnectionUpgradeHeader)
498                 {
499                     return false; 
500                 }
501
502                 foreach (string upgrade in this.Headers.GetValues(HttpKnownHeaderNames.Upgrade))
503                 {
504                     if (string.Compare(upgrade, WebSocketHelpers.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase) == 0)
505                     {
506                         return true;
507                     }
508                 }
509
510                 return false; 
511             }
512         }
513
514         public /* override */ NameValueCollection QueryString {
515             get {
516                 NameValueCollection queryString = new NameValueCollection();
517                 Helpers.FillFromString(queryString, Url.Query, true, ContentEncoding);
518                 return queryString;
519             }
520         }
521
522         public /* override */ string RawUrl {
523             get {
524                 return m_RawUrl;
525             }
526         }
527
528         public string ServiceName
529         {
530             get { return m_ServiceName; }
531             internal set { m_ServiceName = value; }
532         }
533
534         public /* override */ Uri Url {
535             get {
536                 return RequestUri;
537             }
538         }
539
540         public /* override */ Uri UrlReferrer {
541             get {
542                 string referrer = GetKnownHeader(HttpRequestHeader.Referer);
543                 if (referrer==null) {
544                     return null;
545                 }
546                 Uri urlReferrer;
547                 bool success = Uri.TryCreate(referrer, UriKind.RelativeOrAbsolute, out urlReferrer);
548                 return success ? urlReferrer : null;
549             }
550         }
551
552         public /* override */ string UserAgent {
553             get {
554                 return GetKnownHeader(HttpRequestHeader.UserAgent);
555             }
556         }
557
558         public /* override */ string UserHostAddress {
559             get {
560                 return LocalEndPoint.ToString();
561             }
562         }
563
564         public /* override */ string UserHostName {
565             get {
566                 return GetKnownHeader(HttpRequestHeader.Host);
567             }
568         }
569
570         public /* override */ string[] UserLanguages {
571             get {
572                 return Helpers.ParseMultivalueHeader(GetKnownHeader(HttpRequestHeader.AcceptLanguage));
573             }
574         }
575
576         public int ClientCertificateError {
577             get {
578                 if (m_ClientCertState == ListenerClientCertState.NotInitialized)
579                     throw new InvalidOperationException(SR.GetString(SR.net_listener_mustcall, "GetClientCertificate()/BeginGetClientCertificate()"));
580                 else if (m_ClientCertState == ListenerClientCertState.InProgress)
581                     throw new InvalidOperationException(SR.GetString(SR.net_listener_mustcompletecall, "GetClientCertificate()/BeginGetClientCertificate()"));
582
583                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ClientCertificateError_get() returning ClientCertificateError:" + ValidationHelper.ToString(m_ClientCertificateError));
584                 return m_ClientCertificateError;
585             }
586         }
587
588         internal X509Certificate2 ClientCertificate {
589             set {
590                 m_ClientCertificate = value;
591             }
592         }
593
594         internal ListenerClientCertState ClientCertState {
595             set {
596                 m_ClientCertState = value;
597             }
598         }
599
600         internal void SetClientCertificateError(int clientCertificateError)
601         {
602             m_ClientCertificateError = clientCertificateError;
603         }
604
605         public X509Certificate2 GetClientCertificate() {
606             if(Logging.On)Logging.Enter(Logging.HttpListener, this, "GetClientCertificate", "");
607             try {
608                 ProcessClientCertificate();
609                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::GetClientCertificate() returning m_ClientCertificate:" + ValidationHelper.ToString(m_ClientCertificate));
610             } finally {
611                 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "GetClientCertificate", ValidationHelper.ToString(m_ClientCertificate));
612             }
613             return m_ClientCertificate;
614         }
615
616         public IAsyncResult BeginGetClientCertificate(AsyncCallback requestCallback, object state) {
617             if(Logging.On)Logging.PrintInfo(Logging.HttpListener, this, "BeginGetClientCertificate", "");
618             return AsyncProcessClientCertificate(requestCallback, state);
619         }
620
621         public X509Certificate2 EndGetClientCertificate(IAsyncResult asyncResult) {
622             if(Logging.On)Logging.Enter(Logging.HttpListener, this, "EndGetClientCertificate", "");
623             X509Certificate2 clientCertificate = null;
624             try {
625                 if (asyncResult==null) {
626                     throw new ArgumentNullException("asyncResult");
627                 }
628                 ListenerClientCertAsyncResult clientCertAsyncResult = asyncResult as ListenerClientCertAsyncResult;
629                 if (clientCertAsyncResult==null || clientCertAsyncResult.AsyncObject!=this) {
630                     throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult");
631                 }
632                 if (clientCertAsyncResult.EndCalled) {
633                     throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndGetClientCertificate"));
634                 }
635                 clientCertAsyncResult.EndCalled = true;
636                 clientCertificate = clientCertAsyncResult.InternalWaitForCompletion() as X509Certificate2;
637                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::EndGetClientCertificate() returning m_ClientCertificate:" + ValidationHelper.ToString(m_ClientCertificate));
638             } finally {
639                 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "EndGetClientCertificate", ValidationHelper.HashString(clientCertificate));
640             }
641             return clientCertificate;
642         }
643
644         //************* Task-based async public methods *************************
645         [HostProtection(ExternalThreading = true)]
646         public Task<X509Certificate2> GetClientCertificateAsync()
647         {
648             return Task<X509Certificate2>.Factory.FromAsync(BeginGetClientCertificate, EndGetClientCertificate, null);
649         }
650
651
652         public TransportContext TransportContext
653         {
654             get
655             {
656                 return new HttpListenerRequestContext(this);
657             }
658         }
659
660         private CookieCollection ParseCookies(Uri uri, string setCookieHeader) {
661             GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ParseCookies() uri:" + uri + " setCookieHeader:" + setCookieHeader);
662             CookieCollection cookies = new CookieCollection();
663             CookieParser parser = new CookieParser(setCookieHeader);
664             for (;;) {
665                 Cookie cookie = parser.GetServer();
666                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ParseCookies() CookieParser returned cookie:" + ValidationHelper.ToString(cookie));
667                 if (cookie==null) {
668                     // EOF, done.
669                     break;
670                 }
671                 if (cookie.Name.Length==0) {
672                     continue;
673                 }
674                 cookies.InternalAdd(cookie, true);
675             }
676             return cookies;
677         }
678
679         public CookieCollection Cookies {
680             get {
681                 if (m_Cookies==null) {
682                     string cookieString = GetKnownHeader(HttpRequestHeader.Cookie);
683                     if (cookieString!=null && cookieString.Length>0) {
684                         m_Cookies = ParseCookies(RequestUri, cookieString);
685                     }
686                     if (m_Cookies==null) {
687                         m_Cookies = new CookieCollection();
688                     }
689                     if (HttpListenerContext.PromoteCookiesToRfc2965) {
690                         for (int index=0; index<m_Cookies.Count; index++) {
691                             if (m_Cookies[index].Variant==CookieVariant.Rfc2109) {
692                                 m_Cookies[index].Variant = CookieVariant.Rfc2965;
693                             }
694                         }
695                     }
696                 }
697                 return m_Cookies;
698             }
699         }
700
701         public Version ProtocolVersion {
702             get {
703                 return m_Version;
704             }
705         }
706
707         public /* override */ bool HasEntityBody {
708             get {
709                // accessing the ContentLength property delay creates m_BoundaryType
710                 return (ContentLength64 > 0 && m_BoundaryType == BoundaryType.ContentLength) ||
711                     m_BoundaryType == BoundaryType.Chunked || m_BoundaryType == BoundaryType.Multipart;
712             }
713         }
714
715         public /* override */ bool KeepAlive
716         {
717             get
718             {
719                 if (m_KeepAlive == TriState.Unspecified)
720                 {
721                     string header = Headers[HttpKnownHeaderNames.ProxyConnection];
722                     if (string.IsNullOrEmpty(header))
723                     {
724                         header = GetKnownHeader(HttpRequestHeader.Connection);
725                     }
726                     if (string.IsNullOrEmpty(header))
727                     {
728                         if (ProtocolVersion >= HttpVersion.Version11)
729                         {
730                             m_KeepAlive = TriState.True;
731                         }
732                         else
733                         {
734                             header = GetKnownHeader(HttpRequestHeader.KeepAlive);
735                             m_KeepAlive = string.IsNullOrEmpty(header) ? TriState.False : TriState.True;
736                         }
737                     }
738                     else
739                     {
740                         header = header.ToLower(CultureInfo.InvariantCulture);
741                         m_KeepAlive = header.IndexOf("close") < 0 || header.IndexOf("keep-alive") >= 0 ? TriState.True : TriState.False;
742                     }
743                 }
744
745                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::KeepAlive_get() returning:" + m_KeepAlive);
746                 return m_KeepAlive == TriState.True;
747             }
748         }
749
750         public /* override */ IPEndPoint RemoteEndPoint {
751             get {
752                 if (m_RemoteEndPoint==null) {
753                     m_RemoteEndPoint = UnsafeNclNativeMethods.HttpApi.GetRemoteEndPoint(RequestBuffer, OriginalBlobAddress);
754                 }
755                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::RemoteEndPoint_get() returning:" + m_RemoteEndPoint);
756                 return m_RemoteEndPoint;
757             }
758         }
759
760         public /* override */ IPEndPoint LocalEndPoint {
761             get {
762                 if (m_LocalEndPoint==null) {
763                     m_LocalEndPoint = UnsafeNclNativeMethods.HttpApi.GetLocalEndPoint(RequestBuffer, OriginalBlobAddress);
764                 }
765                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::LocalEndPoint_get() returning:" + m_LocalEndPoint);
766                 return m_LocalEndPoint;
767             }
768         }
769
770         //should only be called from httplistenercontext
771         internal void Close() {
772             if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Close", "");
773             RequestContextBase memoryBlob = m_MemoryBlob;
774             if (memoryBlob != null)
775             {
776                 memoryBlob.Close();
777                 m_MemoryBlob = null;
778             }
779             m_IsDisposed = true;
780             if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Close", "");
781         }
782
783
784         private ListenerClientCertAsyncResult AsyncProcessClientCertificate(AsyncCallback requestCallback, object state) {
785             if (m_ClientCertState == ListenerClientCertState.InProgress)
786                 throw new InvalidOperationException(SR.GetString(SR.net_listener_callinprogress, "GetClientCertificate()/BeginGetClientCertificate()"));
787             m_ClientCertState = ListenerClientCertState.InProgress;
788             HttpListenerContext.EnsureBoundHandle();
789
790             ListenerClientCertAsyncResult asyncResult = null;
791                         //--------------------------------------------------------------------
792                         //When you configure the HTTP.SYS with a flag value 2
793                         //which means require client certificates, when the client makes the
794                         //initial SSL connection, server (HTTP.SYS) demands the client certificate
795                         //
796                         //Some apps may not want to demand the client cert at the beginning
797                         //perhaps server the default.htm. In this case the HTTP.SYS is configured
798                         //with a flag value other than 2, whcih means that the client certificate is
799                         //optional.So intially when SSL is established HTTP.SYS won't ask for client
800                         //certificate. This works fine for the default.htm in the case above
801                         //However, if the app wants to demand a client certficate at a later time
802                         //perhaps showing "YOUR ORDERS" page, then the server wans to demand
803                         //Client certs. this will inturn makes HTTP.SYS to do the
804                         //SEC_I_RENOGOTIATE through which the client cert demand is made
805                         //
806                         //THE 
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821             if (m_SslStatus != SslStatus.Insecure)
822             {
823                 // at this point we know that DefaultFlags has the 2 bit set (Negotiate Client certificate)
824                 // the cert, though might or might not be there. try to retrieve it
825                 // this number is the same that IIS decided to use
826                 uint size = CertBoblSize;
827                 asyncResult = new ListenerClientCertAsyncResult(this, state, requestCallback, size);
828                 try {
829                     while (true)
830                     {
831                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() calling UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate size:" + size);
832                         uint bytesReceived = 0;
833
834                         uint statusCode =
835                             UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate(
836                                 HttpListenerContext.RequestQueueHandle,
837                                 m_ConnectionId,
838                                 (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE,
839                                 asyncResult.RequestBlob,
840                                 size,
841                                 &bytesReceived,
842                                 asyncResult.NativeOverlapped);
843
844                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() call to UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate returned:" + statusCode + " bytesReceived:" + bytesReceived);
845                         if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
846                         {
847                             UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.RequestBlob;
848                             size = bytesReceived + pClientCertInfo->CertEncodedSize;
849                             asyncResult.Reset(size);
850                             continue;
851                         }
852                         if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS &&
853                             statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) {
854                             // someother bad error, possible(?) return values are:
855                             // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED
856                             // Also ERROR_BAD_DATA if we got it twice or it reported smaller size buffer required.
857                             throw new HttpListenerException((int)statusCode);
858                         }
859
860                         if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS &&
861                             HttpListener.SkipIOCPCallbackOnSuccess)
862                         {
863                             asyncResult.IOCompleted(statusCode, bytesReceived);
864                         }
865                         break;
866                     }
867                 }
868                 catch {
869                     if (asyncResult!=null) {
870                         asyncResult.InternalCleanup();
871                     }
872                     throw;
873                 }
874             } else {
875                 asyncResult = new ListenerClientCertAsyncResult(this, state, requestCallback, 0);
876                 asyncResult.InvokeCallback();
877             }
878             return asyncResult;
879         }
880
881         private void ProcessClientCertificate() {
882             if (m_ClientCertState == ListenerClientCertState.InProgress)
883                 throw new InvalidOperationException(SR.GetString(SR.net_listener_callinprogress, "GetClientCertificate()/BeginGetClientCertificate()"));
884             m_ClientCertState = ListenerClientCertState.InProgress;
885             GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate()");
886                         //--------------------------------------------------------------------
887                         //When you configure the HTTP.SYS with a flag value 2
888                         //which means require client certificates, when the client makes the
889                         //initial SSL connection, server (HTTP.SYS) demands the client certificate
890                         //
891                         //Some apps may not want to demand the client cert at the beginning
892                         //perhaps server the default.htm. In this case the HTTP.SYS is configured
893                         //with a flag value other than 2, whcih means that the client certificate is
894                         //optional.So intially when SSL is established HTTP.SYS won't ask for client
895                         //certificate. This works fine for the default.htm in the case above
896                         //However, if the app wants to demand a client certficate at a later time
897                         //perhaps showing "YOUR ORDERS" page, then the server wans to demand
898                         //Client certs. this will inturn makes HTTP.SYS to do the
899                         //SEC_I_RENOGOTIATE through which the client cert demand is made
900                         //
901                         //THE 
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916             if (m_SslStatus != SslStatus.Insecure)
917             {
918                 // at this point we know that DefaultFlags has the 2 bit set (Negotiate Client certificate)
919                 // the cert, though might or might not be there. try to retrieve it
920                 // this number is the same that IIS decided to use
921                 uint size = CertBoblSize;
922                 while (true)
923                 {
924                     byte[] clientCertInfoBlob = new byte[checked((int) size)];
925                     fixed (byte* pClientCertInfoBlob = clientCertInfoBlob)
926                     {
927                         UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = (UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO*) pClientCertInfoBlob;
928
929                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() calling UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate size:" + size);
930                         uint bytesReceived = 0;
931
932                         uint statusCode =
933                             UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate(
934                                 HttpListenerContext.RequestQueueHandle,
935                                 m_ConnectionId,
936                                 (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE,
937                                 pClientCertInfo,
938                                 size,
939                                 &bytesReceived,
940                                 null);
941
942                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() call to UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate returned:" + statusCode + " bytesReceived:" + bytesReceived);
943                         if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) {
944                             size = bytesReceived + pClientCertInfo->CertEncodedSize;
945                             continue;
946                         }
947                         else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) {
948                             if (pClientCertInfo!=null) {
949                                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() pClientCertInfo:" + ValidationHelper.ToString((IntPtr)pClientCertInfo)
950                                     + " pClientCertInfo->CertFlags:" + ValidationHelper.ToString(pClientCertInfo->CertFlags)
951                                     + " pClientCertInfo->CertEncodedSize:" + ValidationHelper.ToString(pClientCertInfo->CertEncodedSize)
952                                     + " pClientCertInfo->pCertEncoded:" + ValidationHelper.ToString((IntPtr)pClientCertInfo->pCertEncoded)
953                                     + " pClientCertInfo->Token:" + ValidationHelper.ToString((IntPtr)pClientCertInfo->Token)
954                                     + " pClientCertInfo->CertDeniedByMapper:" + ValidationHelper.ToString(pClientCertInfo->CertDeniedByMapper));
955                                 if (pClientCertInfo->pCertEncoded!=null) {
956                                     try {
957                                         byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize];
958                                         Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length);
959                                         m_ClientCertificate = new X509Certificate2(certEncoded);
960                                     }
961                                     catch (CryptographicException exception) {
962                                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() caught CryptographicException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception));
963                                     }
964                                     catch (SecurityException exception) {
965                                         GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() caught SecurityException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception));
966                                     }
967                                 }
968                                 m_ClientCertificateError = (int)pClientCertInfo->CertFlags;
969                             }
970                         }
971                         else {
972                             GlobalLog.Assert(statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND, "HttpListenerRequest#{0}::ProcessClientCertificate()|Call to UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate() failed with statusCode {1}.", ValidationHelper.HashString(this), statusCode);
973                         }
974                     }
975                     break;
976                 }
977             }
978             m_ClientCertState = ListenerClientCertState.Completed;
979         }
980
981         private string RequestScheme {
982             get {
983                 return IsSecureConnection ? "https" : "http";
984             }
985         }
986
987         private Uri RequestUri {
988             get {
989                 if (m_RequestUri == null) {
990
991                     m_RequestUri = HttpListenerRequestUriBuilder.GetRequestUri(
992                         m_RawUrl, RequestScheme, m_CookedUrlHost, m_CookedUrlPath, m_CookedUrlQuery);
993                 }
994
995                 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::RequestUri_get() returning m_RequestUri:" + ValidationHelper.ToString(m_RequestUri));
996                 return m_RequestUri;
997             }
998         }
999
1000         /*
1001         private DateTime IfModifiedSince {
1002             get {
1003                 string headerValue = GetKnownHeader(HttpRequestHeader.IfModifiedSince);
1004                 if (headerValue==null) {
1005                     return DateTime.Now;
1006                 }
1007                 return DateTime.Parse(headerValue, CultureInfo.InvariantCulture);
1008             }
1009         }
1010         */
1011
1012         private string GetKnownHeader(HttpRequestHeader header) {
1013             return UnsafeNclNativeMethods.HttpApi.GetKnownHeader(RequestBuffer, OriginalBlobAddress, (int) header);
1014         }
1015
1016         internal ChannelBinding GetChannelBinding()
1017         {
1018             return HttpListenerContext.Listener.GetChannelBindingFromTls(m_ConnectionId);
1019         }
1020
1021         internal IEnumerable<TokenBinding> GetTlsTokenBindings() {
1022         
1023             // Try to get the token binding if not created.
1024             if (Volatile.Read(ref m_TokenBindings) == null)
1025             {
1026                 lock (m_Lock)
1027                 {
1028                     if (Volatile.Read(ref m_TokenBindings) == null)
1029                     {
1030                         // If token binding is supported on the machine get it else create empty list.
1031                         if (UnsafeNclNativeMethods.TokenBindingOSHelper.SupportsTokenBinding)
1032                         {
1033                             ProcessTlsTokenBindings();
1034                         }
1035                         else
1036                         {
1037                             m_TokenBindings = new List<TokenBinding>();
1038                         }
1039                     }
1040                 }
1041             }
1042
1043             // If the cached status is not success throw exception, else return the token binding
1044             if (0 != m_TokenBindingVerifyMessageStatus)
1045             {
1046                 throw new HttpListenerException(m_TokenBindingVerifyMessageStatus);
1047             }
1048             else
1049             {
1050                 return m_TokenBindings.AsReadOnly();
1051             }
1052         }
1053
1054         /// <summary>
1055         /// Process the token binding information in the request and populate m_TokenBindings 
1056         /// This method should be called once only as token binding information is cached in m_TokenBindings for further use.
1057         /// </summary>
1058         private void ProcessTlsTokenBindings() {
1059
1060             Debug.Assert(m_TokenBindings == null);
1061
1062             if (m_TokenBindings != null)
1063             {
1064                 return;
1065             }
1066
1067             m_TokenBindings = new List<TokenBinding>();
1068             UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO* pTokenBindingInfo = UnsafeNclNativeMethods.HttpApi.GetTlsTokenBindingRequestInfo(RequestBuffer, OriginalBlobAddress);
1069             UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO_V1* pTokenBindingInfo_V1 = null;
1070             bool useV1TokenBinding = false; 
1071
1072             // Only try to collect the old binding information if there is no V2 binding information available
1073             if (pTokenBindingInfo == null)
1074             {
1075                 pTokenBindingInfo_V1 = UnsafeNclNativeMethods.HttpApi.GetTlsTokenBindingRequestInfo_V1(RequestBuffer, OriginalBlobAddress);
1076                 useV1TokenBinding = true;
1077             }
1078
1079             if (pTokenBindingInfo == null && pTokenBindingInfo_V1 == null)
1080             {
1081                 // The current request isn't over TLS or the client or server doesn't support the token binding
1082                 // protocol. This isn't an error; just return "nothing here".
1083                 return;
1084             }
1085
1086             UnsafeNclNativeMethods.HttpApi.HeapAllocHandle handle = null;
1087             m_TokenBindingVerifyMessageStatus = -1;
1088                         
1089             fixed (byte* pMemoryBlob = RequestBuffer){
1090                 UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2* request = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)pMemoryBlob;
1091                 long fixup = pMemoryBlob - (byte*) OriginalBlobAddress;
1092                 
1093                 if (useV1TokenBinding && pTokenBindingInfo_V1 != null)
1094                 {
1095                     // Old V1 Token Binding protocol is still being used, so we need to verify the binding message using the old API
1096                     m_TokenBindingVerifyMessageStatus = UnsafeNclNativeMethods.HttpApi.TokenBindingVerifyMessage_V1(
1097                         pTokenBindingInfo_V1->TokenBinding + fixup,
1098                         pTokenBindingInfo_V1->TokenBindingSize,
1099                         (IntPtr)((byte*)(pTokenBindingInfo_V1->KeyType) + fixup),
1100                         pTokenBindingInfo_V1->TlsUnique + fixup,
1101                         pTokenBindingInfo_V1->TlsUniqueSize,
1102                         out handle);
1103                 }
1104                 else
1105                 {
1106                     // Use the V2 token binding behavior 
1107                     m_TokenBindingVerifyMessageStatus =
1108                         UnsafeNclNativeMethods.HttpApi.TokenBindingVerifyMessage(
1109                             pTokenBindingInfo->TokenBinding + fixup,
1110                             pTokenBindingInfo->TokenBindingSize,
1111                             pTokenBindingInfo->KeyType,
1112                             pTokenBindingInfo->TlsUnique + fixup,
1113                             pTokenBindingInfo->TlsUniqueSize,
1114                             out handle);
1115                 }
1116             }
1117
1118             if (m_TokenBindingVerifyMessageStatus != 0)
1119             {
1120                 throw new HttpListenerException(m_TokenBindingVerifyMessageStatus);
1121             }
1122             
1123             Debug.Assert(handle != null);
1124             Debug.Assert(!handle.IsInvalid);
1125
1126             using (handle)
1127             {
1128                 // If we have an old binding, use the old binding behavior 
1129                 if (useV1TokenBinding)
1130                 {
1131                     GenerateTokenBindings_V1(handle);
1132                 }
1133                 else
1134                 {
1135                     GenerateTokenBindings(handle);
1136                 }
1137             }
1138         }
1139
1140         /// <summary>
1141         /// Method to allow current bindings to be returned 
1142         /// </summary>
1143         /// <param name="handle"></param>
1144         private void GenerateTokenBindings(UnsafeNclNativeMethods.HttpApi.HeapAllocHandle handle)
1145         {
1146             UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST* pResultList = (UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST*)handle.DangerousGetHandle();
1147             for (int i = 0; i < pResultList->resultCount; i++)
1148             {
1149                 UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_DATA* pThisResultData = &pResultList->resultData[i];
1150
1151                 if (pThisResultData != null)
1152                 {
1153                     byte[] retVal = new byte[pThisResultData->identifierSize];
1154                     Marshal.Copy((IntPtr)(pThisResultData->identifierData), retVal, 0, retVal.Length);
1155
1156                     if (pThisResultData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_PROVIDED)
1157                     {
1158                         m_TokenBindings.Add(new TokenBinding(TokenBindingType.Provided, retVal));
1159                     }
1160                     else if (pThisResultData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_REFERRED)
1161                     {
1162                         m_TokenBindings.Add(new TokenBinding(TokenBindingType.Referred, retVal));
1163                     }
1164                 }
1165             }
1166         }
1167
1168         /// <summary>
1169         /// Compat method to allow V1 bindings to be returned
1170         /// </summary>
1171         /// <param name="handle"></param>
1172         private void GenerateTokenBindings_V1(UnsafeNclNativeMethods.HttpApi.HeapAllocHandle handle)
1173         {
1174             UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST_V1* pResultList = (UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST_V1*)handle.DangerousGetHandle();
1175             for (int i = 0; i < pResultList->resultCount; i++)
1176             {
1177                 UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_DATA_V1* pThisResultData = &pResultList->resultData[i];
1178
1179                 if (pThisResultData != null)
1180                 {
1181                     // Old V1 Token Binding protocol is still being used, so we need modify the binding message using the old behavior
1182                     
1183                     // Per http://tools.ietf.org/html/draft-ietf-tokbind-protocol-00, Sec. 4,
1184                     // We'll strip off the token binding type and return the remainder as an opaque blob.
1185                     Debug.Assert((long)(&pThisResultData->identifierData->hashAlgorithm) == (long)(pThisResultData->identifierData) + 1 );
1186                     byte[] retVal = new byte[pThisResultData->identifierSize - 1];
1187                     Marshal.Copy((IntPtr)(&pThisResultData->identifierData->hashAlgorithm), retVal, 0, retVal.Length);
1188
1189                     if (pThisResultData->identifierData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_PROVIDED)
1190                     {
1191                         m_TokenBindings.Add(new TokenBinding(TokenBindingType.Provided, retVal));
1192                     }
1193                     else if (pThisResultData->identifierData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_REFERRED)
1194                     {
1195                         m_TokenBindings.Add(new TokenBinding(TokenBindingType.Referred, retVal));
1196                     }
1197                 }
1198             }
1199         }
1200
1201         internal void CheckDisposed() {
1202             if (m_IsDisposed) {
1203                 throw new ObjectDisposedException(this.GetType().FullName);
1204             }
1205         }
1206
1207         // <
1208
1209
1210
1211
1212         static class Helpers {
1213             //
1214             // Get attribute off header value
1215             //
1216             internal static String GetAttributeFromHeader(String headerValue, String attrName) {
1217                 if (headerValue == null)
1218                     return null;
1219
1220                 int l = headerValue.Length;
1221                 int k = attrName.Length;
1222
1223                // find properly separated attribute name
1224                 int i = 1; // start searching from 1
1225
1226                 while (i < l) {
1227                     i = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, i, CompareOptions.IgnoreCase);
1228                     if (i < 0)
1229                         break;
1230                     if (i+k >= l)
1231                         break;
1232
1233                     char chPrev = headerValue[i-1];
1234                     char chNext = headerValue[i+k];
1235                     if ((chPrev == ';' || chPrev == ',' || Char.IsWhiteSpace(chPrev)) && (chNext == '=' || Char.IsWhiteSpace(chNext)))
1236                         break;
1237
1238                     i += k;
1239                 }
1240
1241                 if (i < 0 || i >= l)
1242                     return null;
1243
1244                // skip to '=' and the following whitespaces
1245                 i += k;
1246                 while (i < l && Char.IsWhiteSpace(headerValue[i]))
1247                     i++;
1248                 if (i >= l || headerValue[i] != '=')
1249                     return null;
1250                 i++;
1251                 while (i < l && Char.IsWhiteSpace(headerValue[i]))
1252                     i++;
1253                 if (i >= l)
1254                     return null;
1255
1256                // parse the value
1257                 String attrValue = null;
1258
1259                 int j;
1260
1261                 if (i < l && headerValue[i] == '"') {
1262                     if (i == l-1)
1263                         return null;
1264                     j = headerValue.IndexOf('"', i+1);
1265                     if (j < 0 || j == i+1)
1266                         return null;
1267
1268                     attrValue = headerValue.Substring(i+1, j-i-1).Trim();
1269                 }
1270                 else {
1271                     for (j = i; j < l; j++) {
1272                         if (headerValue[j] == ' ' || headerValue[j] == ',')
1273                             break;
1274                     }
1275
1276                     if (j == i)
1277                         return null;
1278
1279                     attrValue = headerValue.Substring(i, j-i).Trim();
1280                 }
1281
1282                 return attrValue;
1283             }
1284
1285             internal static String[] ParseMultivalueHeader(String s) {
1286                 if (s == null)
1287                     return null;
1288
1289                 int l = s.Length;
1290
1291                // collect comma-separated values into list
1292
1293                 ArrayList values = new ArrayList();
1294                 int i = 0;
1295
1296                 while (i < l) {
1297                    // find next ,
1298                     int ci = s.IndexOf(',', i);
1299                     if (ci < 0)
1300                         ci = l;
1301
1302                    // append corresponding server value
1303                     values.Add(s.Substring(i, ci-i));
1304
1305                    // move to next
1306                     i = ci+1;
1307
1308                    // skip leading space
1309                     if (i < l && s[i] == ' ')
1310                         i++;
1311                 }
1312
1313                // return list as array of strings
1314
1315                 int n = values.Count;
1316                 String[] strings;
1317
1318                 // if n is 0 that means s was empty string
1319
1320                 if (n == 0) {
1321                     strings = new String[1];
1322                     strings[0] = String.Empty;
1323                 }
1324                 else {
1325                     strings = new String[n];
1326                     values.CopyTo(0, strings, 0, n);
1327                 }
1328                 return strings;
1329             }
1330
1331
1332             private static string UrlDecodeStringFromStringInternal(string s, Encoding e) {
1333                 int count = s.Length;
1334                 UrlDecoder helper = new UrlDecoder(count, e);
1335
1336                 // go through the string's chars collapsing %XX and %uXXXX and
1337                 // appending each char as char, with exception of %XX constructs
1338                 // that are appended as bytes
1339
1340                 for (int pos = 0; pos < count; pos++) {
1341                     char ch = s[pos];
1342
1343                     if (ch == '+') {
1344                         ch = ' ';
1345                     }
1346                     else if (ch == '%' && pos < count-2) {
1347                         if (s[pos+1] == 'u' && pos < count-5) {
1348                             int h1 = HexToInt(s[pos+2]);
1349                             int h2 = HexToInt(s[pos+3]);
1350                             int h3 = HexToInt(s[pos+4]);
1351                             int h4 = HexToInt(s[pos+5]);
1352
1353                             if (h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0) {   // valid 4 hex chars
1354                                 ch = (char)((h1 << 12) | (h2 << 8) | (h3 << 4) | h4);
1355                                 pos += 5;
1356
1357                                 // only add as char
1358                                 helper.AddChar(ch);
1359                                 continue;
1360                             }
1361                         }
1362                         else {
1363                             int h1 = HexToInt(s[pos+1]);
1364                             int h2 = HexToInt(s[pos+2]);
1365
1366                             if (h1 >= 0 && h2 >= 0) {     // valid 2 hex chars
1367                                 byte b = (byte)((h1 << 4) | h2);
1368                                 pos += 2;
1369
1370                                 // don't add as char
1371                                 helper.AddByte(b);
1372                                 continue;
1373                             }
1374                         }
1375                     }
1376
1377                     if ((ch & 0xFF80) == 0)
1378                         helper.AddByte((byte)ch); // 7 bit have to go as bytes because of Unicode
1379                     else
1380                         helper.AddChar(ch);
1381                 }
1382
1383                 return helper.GetString();
1384             }
1385
1386             private static int HexToInt(char h) {
1387                 return( h >= '0' && h <= '9' ) ? h - '0' :
1388                 ( h >= 'a' && h <= 'f' ) ? h - 'a' + 10 :
1389                 ( h >= 'A' && h <= 'F' ) ? h - 'A' + 10 :
1390                 -1;
1391             }
1392
1393             private class UrlDecoder {
1394                 private int _bufferSize;
1395
1396                 // Accumulate characters in a special array
1397                 private int _numChars;
1398                 private char[] _charBuffer;
1399
1400                 // Accumulate bytes for decoding into characters in a special array
1401                 private int _numBytes;
1402                 private byte[] _byteBuffer;
1403
1404                 // Encoding to convert chars to bytes
1405                 private Encoding _encoding;
1406
1407                 private void FlushBytes() {
1408                     if (_numBytes > 0) {
1409                         _numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars);
1410                         _numBytes = 0;
1411                     }
1412                 }
1413
1414                 internal UrlDecoder(int bufferSize, Encoding encoding) {
1415                     _bufferSize = bufferSize;
1416                     _encoding = encoding;
1417
1418                     _charBuffer = new char[bufferSize];
1419                     // byte buffer created on demand
1420                 }
1421
1422                 internal void AddChar(char ch) {
1423                     if (_numBytes > 0)
1424                         FlushBytes();
1425
1426                     _charBuffer[_numChars++] = ch;
1427                 }
1428
1429                 internal void AddByte(byte b) {
1430                     // if there are no pending bytes treat 7 bit bytes as characters
1431                     // this optimization is temp disable as it doesn't work for some encodings
1432     /*
1433                     if (_numBytes == 0 && ((b & 0x80) == 0)) {
1434                         AddChar((char)b);
1435                     }
1436                     else
1437     */
1438                     {
1439                         if (_byteBuffer == null)
1440                             _byteBuffer = new byte[_bufferSize];
1441
1442                         _byteBuffer[_numBytes++] = b;
1443                     }
1444                 }
1445
1446                 internal String GetString() {
1447                     if (_numBytes > 0)
1448                         FlushBytes();
1449
1450                     if (_numChars > 0)
1451                         return new String(_charBuffer, 0, _numChars);
1452                     else
1453                         return String.Empty;
1454                 }
1455             }
1456
1457
1458             internal static void FillFromString(NameValueCollection nvc, String s, bool urlencoded, Encoding encoding) {
1459                 int l = (s != null) ? s.Length : 0;
1460                 int i = (s.Length>0 && s[0]=='?') ? 1 : 0;
1461
1462                 while (i < l) {
1463                     // find next & while noting first = on the way (and if there are more)
1464
1465                     int si = i;
1466                     int ti = -1;
1467
1468                     while (i < l) {
1469                         char ch = s[i];
1470
1471                         if (ch == '=') {
1472                             if (ti < 0)
1473                                 ti = i;
1474                         }
1475                         else if (ch == '&') {
1476                             break;
1477                         }
1478
1479                         i++;
1480                     }
1481
1482                     // extract the name / value pair
1483
1484                     String name = null;
1485                     String value = null;
1486
1487                     if (ti >= 0) {
1488                         name = s.Substring(si, ti-si);
1489                         value = s.Substring(ti+1, i-ti-1);
1490                     }
1491                     else {
1492                         value = s.Substring(si, i-si);
1493                     }
1494
1495                     // add name / value pair to the collection
1496
1497                     if (urlencoded)
1498                         nvc.Add(
1499                            name == null ? null : UrlDecodeStringFromStringInternal(name, encoding),
1500                            UrlDecodeStringFromStringInternal(value, encoding));
1501                     else
1502                         nvc.Add(name, value);
1503
1504                     // trailing '&'
1505
1506                     if (i == l-1 && s[i] == '&')
1507                         nvc.Add(null, "");
1508
1509                     i++;
1510                 }
1511             }
1512         }
1513     }
1514 }