1 //------------------------------------------------------------------------------
2 // <copyright file="HttpListenerRequest.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.Collections.Specialized;
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;
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;
30 internal enum ListenerClientCertState {
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;
42 internal NativeOverlapped* NativeOverlapped
50 internal UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* RequestBlob
58 private static readonly IOCompletionCallback s_IOCallback = new IOCompletionCallback(WaitCallback);
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
66 internal void Reset(uint size) {
73 Overlapped.Free(m_pOverlapped);
80 m_BackingBuffer = null;
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);
90 internal unsafe void IOCompleted(uint errorCode, uint numBytes)
92 IOCompleted(this, errorCode, numBytes);
95 private static unsafe void IOCompleted(ListenerClientCertAsyncResult asyncResult, uint errorCode, uint numBytes)
97 HttpListenerRequest httpListenerRequest = (HttpListenerRequest) asyncResult.AsyncObject;
100 if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
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.
106 UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.RequestBlob;
107 asyncResult.Reset(numBytes + pClientCertInfo->CertEncodedSize);
109 uint bytesReceived = 0;
111 UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate(
112 httpListenerRequest.HttpListenerContext.RequestQueueHandle,
113 httpListenerRequest.m_ConnectionId,
114 (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE,
115 asyncResult.m_MemoryBlob,
118 asyncResult.m_pOverlapped);
120 if(errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING ||
121 (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && !HttpListener.SkipIOCPCallbackOnSuccess))
127 if (errorCode!=UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) {
128 asyncResult.ErrorCode = (int)errorCode;
129 result = new HttpListenerException((int)errorCode);
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) {
142 byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize];
143 Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length);
144 result = httpListenerRequest.ClientCertificate = new X509Certificate2(certEncoded);
146 catch (CryptographicException exception) {
147 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(httpListenerRequest) + "::ProcessClientCertificate() caught CryptographicException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception));
150 catch (SecurityException exception) {
151 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(httpListenerRequest) + "::ProcessClientCertificate() caught SecurityException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception));
155 httpListenerRequest.SetClientCertificateError((int)pClientCertInfo->CertFlags);
160 // complete the async IO and invoke the callback
161 GlobalLog.Print("ListenerClientCertAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::WaitCallback() calling Complete()");
163 catch (Exception exception)
165 if (NclUtilities.IsFatal(exception)) throw;
169 if(errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING){
170 httpListenerRequest.ClientCertState = ListenerClientCertState.Completed;
174 asyncResult.InvokeCallback(result);
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;
182 GlobalLog.Print("ListenerClientCertAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::WaitCallback() errorCode:[" + errorCode.ToString() + "] numBytes:[" + numBytes.ToString() + "] nativeOverlapped:[" + ((long)nativeOverlapped).ToString() + "]");
184 IOCompleted(asyncResult, errorCode, numBytes);
187 // Will be called from the base class upon InvokeCallback()
188 protected override void Cleanup()
190 if (m_pOverlapped != null)
193 Overlapped.Free(m_pOverlapped);
194 m_pOverlapped = null;
196 GC.SuppressFinalize(this);
200 ~ListenerClientCertAsyncResult()
202 if (m_pOverlapped != null && !NclUtilities.HasShutdownStarted)
204 Overlapped.Free(m_pOverlapped);
205 m_pOverlapped = null; // Must do this in case application calls GC.ReRegisterForFinalize().
211 public sealed unsafe class HttpListenerRequest/* BaseHttpRequest, */ {
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;
243 private enum SslStatus : byte
250 internal HttpListenerRequest(HttpListenerContext httpContext, RequestContextBase memoryBlob)
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;
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);
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);
272 if (cookedUrl.pAbsPath != null && cookedUrl.AbsPathLength > 0) {
273 m_CookedUrlPath = Marshal.PtrToStringUni((IntPtr)cookedUrl.pAbsPath, cookedUrl.AbsPathLength / 2);
275 if (cookedUrl.pQueryString != null && cookedUrl.QueryStringLength > 0) {
276 m_CookedUrlQuery = Marshal.PtrToStringUni((IntPtr)cookedUrl.pQueryString, cookedUrl.QueryStringLength / 2);
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));
285 StringBuilder sb = new StringBuilder("HttpListenerRequest Headers:\n");
286 for (int i=0; i<Headers.Count; i++) {
288 sb.Append(Headers.GetKey(i));
290 sb.Append(Headers.Get(i));
293 Logging.PrintInfo(Logging.HttpListener, this, ".ctor", sb.ToString());
297 internal HttpListenerContext HttpListenerContext {
299 return m_HttpContext;
303 internal byte[] RequestBuffer
308 return m_MemoryBlob.RequestBuffer;
312 internal IntPtr OriginalBlobAddress
317 return m_MemoryBlob.OriginalBlobAddress;
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
323 internal void DetachBlob(RequestContextBase memoryBlob)
325 if (memoryBlob != null && (object) memoryBlob == (object) m_MemoryBlob)
331 // Finalizes ownership of the memory blob. DetachBlob can't be called after this.
332 internal void ReleasePins()
334 m_MemoryBlob.ReleasePins();
337 public Guid RequestTraceIdentifier {
339 Guid guid = new Guid();
340 *(1+ (ulong *) &guid) = RequestId;
345 internal ulong RequestId {
351 public /* override */ string[] AcceptTypes {
353 return Helpers.ParseMultivalueHeader(GetKnownHeader(HttpRequestHeader.Accept));
357 public /* override */ Encoding ContentEncoding {
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) {
363 return Encoding.GetEncoding(postDataCharset);
365 catch (ArgumentException) {
370 if (ContentType!=null) {
371 string charSet = Helpers.GetAttributeFromHeader(ContentType, "charset");
374 return Encoding.GetEncoding(charSet);
376 catch (ArgumentException) {
381 return Encoding.Default;
385 public /* override */ long ContentLength64 {
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;
395 m_BoundaryType = BoundaryType.ContentLength;
396 string length = GetKnownHeader(HttpRequestHeader.ContentLength);
398 bool success = long.TryParse(length, NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out m_ContentLength);
401 m_BoundaryType = BoundaryType.Invalid;
406 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ContentLength_get() returning m_ContentLength:" + m_ContentLength + " m_BoundaryType:" + m_BoundaryType);
407 return m_ContentLength;
411 public /* override */ string ContentType {
413 return GetKnownHeader(HttpRequestHeader.ContentType);
417 public /* override */ NameValueCollection Headers {
419 if (m_WebHeaders==null) {
420 m_WebHeaders = UnsafeNclNativeMethods.HttpApi.GetHeaders(RequestBuffer, OriginalBlobAddress);
422 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::Headers_get() returning#" + ValidationHelper.HashString(m_WebHeaders));
427 public /* override */ string HttpMethod {
429 if (m_HttpMethod==null) {
430 m_HttpMethod = UnsafeNclNativeMethods.HttpApi.GetVerb(RequestBuffer, OriginalBlobAddress);
432 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::HttpMethod_get() returning m_HttpMethod:" + ValidationHelper.ToString(m_HttpMethod));
437 public /* override */ Stream InputStream {
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;
443 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "InputStream_get", "");
444 return m_RequestStream;
448 // Requires ControlPrincipal permission if the request was authenticated with Negotiate, NTLM, or Digest.
449 public /* override */ bool IsAuthenticated {
451 IPrincipal user = HttpListenerContext.User;
452 return user != null && user.Identity != null && user.Identity.IsAuthenticated;
456 public /* override */ bool IsLocal {
458 return LocalEndPoint.Address.Equals(RemoteEndPoint.Address);
462 public /* override */ bool IsSecureConnection {
464 return m_SslStatus != SslStatus.Insecure;
468 public bool IsWebSocketRequest
472 if (!WebSocketProtocolComponent.IsSupported)
477 bool foundConnectionUpgradeHeader = false;
478 if (string.IsNullOrEmpty(this.Headers[HttpKnownHeaderNames.Connection]) || string.IsNullOrEmpty(this.Headers[HttpKnownHeaderNames.Upgrade]))
483 foreach (string connection in this.Headers.GetValues(HttpKnownHeaderNames.Connection))
485 if (string.Compare(connection, HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) == 0)
487 foundConnectionUpgradeHeader = true;
492 if (!foundConnectionUpgradeHeader)
497 foreach (string upgrade in this.Headers.GetValues(HttpKnownHeaderNames.Upgrade))
499 if (string.Compare(upgrade, WebSocketHelpers.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase) == 0)
509 public /* override */ NameValueCollection QueryString {
511 NameValueCollection queryString = new NameValueCollection();
512 Helpers.FillFromString(queryString, Url.Query, true, ContentEncoding);
517 public /* override */ string RawUrl {
523 public string ServiceName
525 get { return m_ServiceName; }
526 internal set { m_ServiceName = value; }
529 public /* override */ Uri Url {
535 public /* override */ Uri UrlReferrer {
537 string referrer = GetKnownHeader(HttpRequestHeader.Referer);
538 if (referrer==null) {
542 bool success = Uri.TryCreate(referrer, UriKind.RelativeOrAbsolute, out urlReferrer);
543 return success ? urlReferrer : null;
547 public /* override */ string UserAgent {
549 return GetKnownHeader(HttpRequestHeader.UserAgent);
553 public /* override */ string UserHostAddress {
555 return LocalEndPoint.ToString();
559 public /* override */ string UserHostName {
561 return GetKnownHeader(HttpRequestHeader.Host);
565 public /* override */ string[] UserLanguages {
567 return Helpers.ParseMultivalueHeader(GetKnownHeader(HttpRequestHeader.AcceptLanguage));
571 public int ClientCertificateError {
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()"));
578 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ClientCertificateError_get() returning ClientCertificateError:" + ValidationHelper.ToString(m_ClientCertificateError));
579 return m_ClientCertificateError;
583 internal X509Certificate2 ClientCertificate {
585 m_ClientCertificate = value;
589 internal ListenerClientCertState ClientCertState {
591 m_ClientCertState = value;
595 internal void SetClientCertificateError(int clientCertificateError)
597 m_ClientCertificateError = clientCertificateError;
600 public X509Certificate2 GetClientCertificate() {
601 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "GetClientCertificate", "");
603 ProcessClientCertificate();
604 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::GetClientCertificate() returning m_ClientCertificate:" + ValidationHelper.ToString(m_ClientCertificate));
606 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "GetClientCertificate", ValidationHelper.ToString(m_ClientCertificate));
608 return m_ClientCertificate;
611 public IAsyncResult BeginGetClientCertificate(AsyncCallback requestCallback, object state) {
612 if(Logging.On)Logging.PrintInfo(Logging.HttpListener, this, "BeginGetClientCertificate", "");
613 return AsyncProcessClientCertificate(requestCallback, state);
616 public X509Certificate2 EndGetClientCertificate(IAsyncResult asyncResult) {
617 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "EndGetClientCertificate", "");
618 X509Certificate2 clientCertificate = null;
620 if (asyncResult==null) {
621 throw new ArgumentNullException("asyncResult");
623 ListenerClientCertAsyncResult clientCertAsyncResult = asyncResult as ListenerClientCertAsyncResult;
624 if (clientCertAsyncResult==null || clientCertAsyncResult.AsyncObject!=this) {
625 throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult");
627 if (clientCertAsyncResult.EndCalled) {
628 throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndGetClientCertificate"));
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));
634 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "EndGetClientCertificate", ValidationHelper.HashString(clientCertificate));
636 return clientCertificate;
639 //************* Task-based async public methods *************************
640 [HostProtection(ExternalThreading = true)]
641 public Task<X509Certificate2> GetClientCertificateAsync()
643 return Task<X509Certificate2>.Factory.FromAsync(BeginGetClientCertificate, EndGetClientCertificate, null);
647 public TransportContext TransportContext
651 return new HttpListenerRequestContext(this);
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);
660 Cookie cookie = parser.GetServer();
661 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ParseCookies() CookieParser returned cookie:" + ValidationHelper.ToString(cookie));
666 if (cookie.Name.Length==0) {
669 cookies.InternalAdd(cookie, true);
674 public CookieCollection Cookies {
676 if (m_Cookies==null) {
677 string cookieString = GetKnownHeader(HttpRequestHeader.Cookie);
678 if (cookieString!=null && cookieString.Length>0) {
679 m_Cookies = ParseCookies(RequestUri, cookieString);
681 if (m_Cookies==null) {
682 m_Cookies = new CookieCollection();
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;
696 public Version ProtocolVersion {
702 public /* override */ bool HasEntityBody {
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;
710 public /* override */ bool KeepAlive
714 if (m_KeepAlive == TriState.Unspecified)
716 string header = Headers[HttpKnownHeaderNames.ProxyConnection];
717 if (string.IsNullOrEmpty(header))
719 header = GetKnownHeader(HttpRequestHeader.Connection);
721 if (string.IsNullOrEmpty(header))
723 if (ProtocolVersion >= HttpVersion.Version11)
725 m_KeepAlive = TriState.True;
729 header = GetKnownHeader(HttpRequestHeader.KeepAlive);
730 m_KeepAlive = string.IsNullOrEmpty(header) ? TriState.False : TriState.True;
735 header = header.ToLower(CultureInfo.InvariantCulture);
736 m_KeepAlive = header.IndexOf("close") < 0 || header.IndexOf("keep-alive") >= 0 ? TriState.True : TriState.False;
740 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::KeepAlive_get() returning:" + m_KeepAlive);
741 return m_KeepAlive == TriState.True;
745 public /* override */ IPEndPoint RemoteEndPoint {
747 if (m_RemoteEndPoint==null) {
748 m_RemoteEndPoint = UnsafeNclNativeMethods.HttpApi.GetRemoteEndPoint(RequestBuffer, OriginalBlobAddress);
750 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::RemoteEndPoint_get() returning:" + m_RemoteEndPoint);
751 return m_RemoteEndPoint;
755 public /* override */ IPEndPoint LocalEndPoint {
757 if (m_LocalEndPoint==null) {
758 m_LocalEndPoint = UnsafeNclNativeMethods.HttpApi.GetLocalEndPoint(RequestBuffer, OriginalBlobAddress);
760 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::LocalEndPoint_get() returning:" + m_LocalEndPoint);
761 return m_LocalEndPoint;
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)
775 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Close", "");
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();
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
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
816 if (m_SslStatus != SslStatus.Insecure)
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);
826 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() calling UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate size:" + size);
827 uint bytesReceived = 0;
830 UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate(
831 HttpListenerContext.RequestQueueHandle,
833 (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE,
834 asyncResult.RequestBlob,
837 asyncResult.NativeOverlapped);
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)
842 UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.RequestBlob;
843 size = bytesReceived + pClientCertInfo->CertEncodedSize;
844 asyncResult.Reset(size);
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);
855 if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS &&
856 HttpListener.SkipIOCPCallbackOnSuccess)
858 asyncResult.IOCompleted(statusCode, bytesReceived);
864 if (asyncResult!=null) {
865 asyncResult.InternalCleanup();
870 asyncResult = new ListenerClientCertAsyncResult(this, state, requestCallback, 0);
871 asyncResult.InvokeCallback();
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
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
911 if (m_SslStatus != SslStatus.Insecure)
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;
919 byte[] clientCertInfoBlob = new byte[checked((int) size)];
920 fixed (byte* pClientCertInfoBlob = clientCertInfoBlob)
922 UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = (UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO*) pClientCertInfoBlob;
924 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() calling UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate size:" + size);
925 uint bytesReceived = 0;
928 UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate(
929 HttpListenerContext.RequestQueueHandle,
931 (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE,
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;
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) {
952 byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize];
953 Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length);
954 m_ClientCertificate = new X509Certificate2(certEncoded);
956 catch (CryptographicException exception) {
957 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() caught CryptographicException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception));
959 catch (SecurityException exception) {
960 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() caught SecurityException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception));
963 m_ClientCertificateError = (int)pClientCertInfo->CertFlags;
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);
973 m_ClientCertState = ListenerClientCertState.Completed;
976 private string RequestScheme {
978 return IsSecureConnection ? "https" : "http";
982 private Uri RequestUri {
984 if (m_RequestUri == null) {
986 m_RequestUri = HttpListenerRequestUriBuilder.GetRequestUri(
987 m_RawUrl, RequestScheme, m_CookedUrlHost, m_CookedUrlPath, m_CookedUrlQuery);
990 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::RequestUri_get() returning m_RequestUri:" + ValidationHelper.ToString(m_RequestUri));
996 private DateTime IfModifiedSince {
998 string headerValue = GetKnownHeader(HttpRequestHeader.IfModifiedSince);
999 if (headerValue==null) {
1000 return DateTime.Now;
1002 return DateTime.Parse(headerValue, CultureInfo.InvariantCulture);
1007 private string GetKnownHeader(HttpRequestHeader header) {
1008 return UnsafeNclNativeMethods.HttpApi.GetKnownHeader(RequestBuffer, OriginalBlobAddress, (int) header);
1011 internal ChannelBinding GetChannelBinding()
1013 return HttpListenerContext.Listener.GetChannelBindingFromTls(m_ConnectionId);
1016 internal IEnumerable<TokenBinding> GetTlsTokenBindings() {
1018 // Try to get the token binding if not created.
1019 if (Volatile.Read(ref m_TokenBindings) == null)
1023 if (Volatile.Read(ref m_TokenBindings) == null)
1025 // If token binding is supported on the machine get it else create empty list.
1026 if (UnsafeNclNativeMethods.TokenBindingOSHelper.SupportsTokenBinding)
1028 ProcessTlsTokenBindings();
1032 m_TokenBindings = new List<TokenBinding>();
1038 // If the cached status is not success throw exception, else return the token binding
1039 if (0 != m_TokenBindingVerifyMessageStatus)
1041 throw new HttpListenerException(m_TokenBindingVerifyMessageStatus);
1045 return m_TokenBindings.AsReadOnly();
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.
1053 private void ProcessTlsTokenBindings() {
1055 Debug.Assert(m_TokenBindings == null);
1057 if (m_TokenBindings != null)
1062 m_TokenBindings = new List<TokenBinding>();
1064 UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO* pTokenBindingInfo = GetTlsTokenBindingRequestInfo();
1066 if (pTokenBindingInfo == null)
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".
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,
1083 if (m_TokenBindingVerifyMessageStatus != 0)
1085 throw new HttpListenerException(m_TokenBindingVerifyMessageStatus);
1088 Debug.Assert(handle != null);
1089 Debug.Assert(!handle.IsInvalid);
1093 UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST* pResultList = (UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST*)handle.DangerousGetHandle();
1094 for (int i = 0; i < pResultList->resultCount; i++)
1096 UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_DATA* pThisResultData = &pResultList->resultData[i];
1098 if (pThisResultData != null)
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);
1106 if (pThisResultData->identifierData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_PROVIDED)
1108 m_TokenBindings.Add(new TokenBinding(TokenBindingType.Provided, retVal));
1110 else if (pThisResultData->identifierData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_REFERRED)
1112 m_TokenBindings.Add(new TokenBinding(TokenBindingType.Referred, retVal));
1119 private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO* GetTlsTokenBindingRequestInfo() {
1120 fixed (byte* pMemoryBlob = RequestBuffer)
1122 UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2* request = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)pMemoryBlob;
1124 for (int i = 0; i < request->RequestInfoCount; i++)
1126 UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_INFO* pThisInfo = &request->pRequestInfo[i];
1127 if (pThisInfo != null && pThisInfo->InfoType == UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslTokenBinding)
1129 return (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO*)pThisInfo->pInfo;
1136 internal void CheckDisposed() {
1138 throw new ObjectDisposedException(this.GetType().FullName);
1147 static class Helpers {
1149 // Get attribute off header value
1151 internal static String GetAttributeFromHeader(String headerValue, String attrName) {
1152 if (headerValue == null)
1155 int l = headerValue.Length;
1156 int k = attrName.Length;
1158 // find properly separated attribute name
1159 int i = 1; // start searching from 1
1162 i = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, i, CompareOptions.IgnoreCase);
1168 char chPrev = headerValue[i-1];
1169 char chNext = headerValue[i+k];
1170 if ((chPrev == ';' || chPrev == ',' || Char.IsWhiteSpace(chPrev)) && (chNext == '=' || Char.IsWhiteSpace(chNext)))
1176 if (i < 0 || i >= l)
1179 // skip to '=' and the following whitespaces
1181 while (i < l && Char.IsWhiteSpace(headerValue[i]))
1183 if (i >= l || headerValue[i] != '=')
1186 while (i < l && Char.IsWhiteSpace(headerValue[i]))
1192 String attrValue = null;
1196 if (i < l && headerValue[i] == '"') {
1199 j = headerValue.IndexOf('"', i+1);
1200 if (j < 0 || j == i+1)
1203 attrValue = headerValue.Substring(i+1, j-i-1).Trim();
1206 for (j = i; j < l; j++) {
1207 if (headerValue[j] == ' ' || headerValue[j] == ',')
1214 attrValue = headerValue.Substring(i, j-i).Trim();
1220 internal static String[] ParseMultivalueHeader(String s) {
1226 // collect comma-separated values into list
1228 ArrayList values = new ArrayList();
1233 int ci = s.IndexOf(',', i);
1237 // append corresponding server value
1238 values.Add(s.Substring(i, ci-i));
1243 // skip leading space
1244 if (i < l && s[i] == ' ')
1248 // return list as array of strings
1250 int n = values.Count;
1253 // if n is 0 that means s was empty string
1256 strings = new String[1];
1257 strings[0] = String.Empty;
1260 strings = new String[n];
1261 values.CopyTo(0, strings, 0, n);
1267 private static string UrlDecodeStringFromStringInternal(string s, Encoding e) {
1268 int count = s.Length;
1269 UrlDecoder helper = new UrlDecoder(count, e);
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
1275 for (int pos = 0; pos < count; pos++) {
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]);
1288 if (h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0) { // valid 4 hex chars
1289 ch = (char)((h1 << 12) | (h2 << 8) | (h3 << 4) | h4);
1298 int h1 = HexToInt(s[pos+1]);
1299 int h2 = HexToInt(s[pos+2]);
1301 if (h1 >= 0 && h2 >= 0) { // valid 2 hex chars
1302 byte b = (byte)((h1 << 4) | h2);
1305 // don't add as char
1312 if ((ch & 0xFF80) == 0)
1313 helper.AddByte((byte)ch); // 7 bit have to go as bytes because of Unicode
1318 return helper.GetString();
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 :
1328 private class UrlDecoder {
1329 private int _bufferSize;
1331 // Accumulate characters in a special array
1332 private int _numChars;
1333 private char[] _charBuffer;
1335 // Accumulate bytes for decoding into characters in a special array
1336 private int _numBytes;
1337 private byte[] _byteBuffer;
1339 // Encoding to convert chars to bytes
1340 private Encoding _encoding;
1342 private void FlushBytes() {
1343 if (_numBytes > 0) {
1344 _numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars);
1349 internal UrlDecoder(int bufferSize, Encoding encoding) {
1350 _bufferSize = bufferSize;
1351 _encoding = encoding;
1353 _charBuffer = new char[bufferSize];
1354 // byte buffer created on demand
1357 internal void AddChar(char ch) {
1361 _charBuffer[_numChars++] = ch;
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
1368 if (_numBytes == 0 && ((b & 0x80) == 0)) {
1374 if (_byteBuffer == null)
1375 _byteBuffer = new byte[_bufferSize];
1377 _byteBuffer[_numBytes++] = b;
1381 internal String GetString() {
1386 return new String(_charBuffer, 0, _numChars);
1388 return String.Empty;
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;
1398 // find next & while noting first = on the way (and if there are more)
1410 else if (ch == '&') {
1417 // extract the name / value pair
1420 String value = null;
1423 name = s.Substring(si, ti-si);
1424 value = s.Substring(ti+1, i-ti-1);
1427 value = s.Substring(si, i-si);
1430 // add name / value pair to the collection
1434 name == null ? null : UrlDecodeStringFromStringInternal(name, encoding),
1435 UrlDecodeStringFromStringInternal(value, encoding));
1437 nvc.Add(name, value);
1441 if (i == l-1 && s[i] == '&')