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 // 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
308 internal byte[] RequestBuffer
313 return m_MemoryBlob.RequestBuffer;
317 internal IntPtr OriginalBlobAddress
322 return m_MemoryBlob.OriginalBlobAddress;
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
328 internal void DetachBlob(RequestContextBase memoryBlob)
330 if (memoryBlob != null && (object) memoryBlob == (object) m_MemoryBlob)
336 // Finalizes ownership of the memory blob. DetachBlob can't be called after this.
337 internal void ReleasePins()
339 m_MemoryBlob.ReleasePins();
342 public Guid RequestTraceIdentifier {
344 Guid guid = new Guid();
345 *(1+ (ulong *) &guid) = RequestId;
350 internal ulong RequestId {
356 public /* override */ string[] AcceptTypes {
358 return Helpers.ParseMultivalueHeader(GetKnownHeader(HttpRequestHeader.Accept));
362 public /* override */ Encoding ContentEncoding {
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) {
368 return Encoding.GetEncoding(postDataCharset);
370 catch (ArgumentException) {
375 if (ContentType!=null) {
376 string charSet = Helpers.GetAttributeFromHeader(ContentType, "charset");
379 return Encoding.GetEncoding(charSet);
381 catch (ArgumentException) {
386 return Encoding.Default;
390 public /* override */ long ContentLength64 {
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;
400 m_BoundaryType = BoundaryType.ContentLength;
401 string length = GetKnownHeader(HttpRequestHeader.ContentLength);
403 bool success = long.TryParse(length, NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out m_ContentLength);
406 m_BoundaryType = BoundaryType.Invalid;
411 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ContentLength_get() returning m_ContentLength:" + m_ContentLength + " m_BoundaryType:" + m_BoundaryType);
412 return m_ContentLength;
416 public /* override */ string ContentType {
418 return GetKnownHeader(HttpRequestHeader.ContentType);
422 public /* override */ NameValueCollection Headers {
424 if (m_WebHeaders==null) {
425 m_WebHeaders = UnsafeNclNativeMethods.HttpApi.GetHeaders(RequestBuffer, OriginalBlobAddress);
427 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::Headers_get() returning#" + ValidationHelper.HashString(m_WebHeaders));
432 public /* override */ string HttpMethod {
434 if (m_HttpMethod==null) {
435 m_HttpMethod = UnsafeNclNativeMethods.HttpApi.GetVerb(RequestBuffer, OriginalBlobAddress);
437 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::HttpMethod_get() returning m_HttpMethod:" + ValidationHelper.ToString(m_HttpMethod));
442 public /* override */ Stream InputStream {
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;
448 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "InputStream_get", "");
449 return m_RequestStream;
453 // Requires ControlPrincipal permission if the request was authenticated with Negotiate, NTLM, or Digest.
454 public /* override */ bool IsAuthenticated {
456 IPrincipal user = HttpListenerContext.User;
457 return user != null && user.Identity != null && user.Identity.IsAuthenticated;
461 public /* override */ bool IsLocal {
463 return LocalEndPoint.Address.Equals(RemoteEndPoint.Address);
467 public /* override */ bool IsSecureConnection {
469 return m_SslStatus != SslStatus.Insecure;
473 public bool IsWebSocketRequest
477 if (!WebSocketProtocolComponent.IsSupported)
482 bool foundConnectionUpgradeHeader = false;
483 if (string.IsNullOrEmpty(this.Headers[HttpKnownHeaderNames.Connection]) || string.IsNullOrEmpty(this.Headers[HttpKnownHeaderNames.Upgrade]))
488 foreach (string connection in this.Headers.GetValues(HttpKnownHeaderNames.Connection))
490 if (string.Compare(connection, HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) == 0)
492 foundConnectionUpgradeHeader = true;
497 if (!foundConnectionUpgradeHeader)
502 foreach (string upgrade in this.Headers.GetValues(HttpKnownHeaderNames.Upgrade))
504 if (string.Compare(upgrade, WebSocketHelpers.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase) == 0)
514 public /* override */ NameValueCollection QueryString {
516 NameValueCollection queryString = new NameValueCollection();
517 Helpers.FillFromString(queryString, Url.Query, true, ContentEncoding);
522 public /* override */ string RawUrl {
528 public string ServiceName
530 get { return m_ServiceName; }
531 internal set { m_ServiceName = value; }
534 public /* override */ Uri Url {
540 public /* override */ Uri UrlReferrer {
542 string referrer = GetKnownHeader(HttpRequestHeader.Referer);
543 if (referrer==null) {
547 bool success = Uri.TryCreate(referrer, UriKind.RelativeOrAbsolute, out urlReferrer);
548 return success ? urlReferrer : null;
552 public /* override */ string UserAgent {
554 return GetKnownHeader(HttpRequestHeader.UserAgent);
558 public /* override */ string UserHostAddress {
560 return LocalEndPoint.ToString();
564 public /* override */ string UserHostName {
566 return GetKnownHeader(HttpRequestHeader.Host);
570 public /* override */ string[] UserLanguages {
572 return Helpers.ParseMultivalueHeader(GetKnownHeader(HttpRequestHeader.AcceptLanguage));
576 public int ClientCertificateError {
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()"));
583 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ClientCertificateError_get() returning ClientCertificateError:" + ValidationHelper.ToString(m_ClientCertificateError));
584 return m_ClientCertificateError;
588 internal X509Certificate2 ClientCertificate {
590 m_ClientCertificate = value;
594 internal ListenerClientCertState ClientCertState {
596 m_ClientCertState = value;
600 internal void SetClientCertificateError(int clientCertificateError)
602 m_ClientCertificateError = clientCertificateError;
605 public X509Certificate2 GetClientCertificate() {
606 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "GetClientCertificate", "");
608 ProcessClientCertificate();
609 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::GetClientCertificate() returning m_ClientCertificate:" + ValidationHelper.ToString(m_ClientCertificate));
611 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "GetClientCertificate", ValidationHelper.ToString(m_ClientCertificate));
613 return m_ClientCertificate;
616 public IAsyncResult BeginGetClientCertificate(AsyncCallback requestCallback, object state) {
617 if(Logging.On)Logging.PrintInfo(Logging.HttpListener, this, "BeginGetClientCertificate", "");
618 return AsyncProcessClientCertificate(requestCallback, state);
621 public X509Certificate2 EndGetClientCertificate(IAsyncResult asyncResult) {
622 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "EndGetClientCertificate", "");
623 X509Certificate2 clientCertificate = null;
625 if (asyncResult==null) {
626 throw new ArgumentNullException("asyncResult");
628 ListenerClientCertAsyncResult clientCertAsyncResult = asyncResult as ListenerClientCertAsyncResult;
629 if (clientCertAsyncResult==null || clientCertAsyncResult.AsyncObject!=this) {
630 throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult");
632 if (clientCertAsyncResult.EndCalled) {
633 throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndGetClientCertificate"));
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));
639 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "EndGetClientCertificate", ValidationHelper.HashString(clientCertificate));
641 return clientCertificate;
644 //************* Task-based async public methods *************************
645 [HostProtection(ExternalThreading = true)]
646 public Task<X509Certificate2> GetClientCertificateAsync()
648 return Task<X509Certificate2>.Factory.FromAsync(BeginGetClientCertificate, EndGetClientCertificate, null);
652 public TransportContext TransportContext
656 return new HttpListenerRequestContext(this);
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);
665 Cookie cookie = parser.GetServer();
666 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ParseCookies() CookieParser returned cookie:" + ValidationHelper.ToString(cookie));
671 if (cookie.Name.Length==0) {
674 cookies.InternalAdd(cookie, true);
679 public CookieCollection Cookies {
681 if (m_Cookies==null) {
682 string cookieString = GetKnownHeader(HttpRequestHeader.Cookie);
683 if (cookieString!=null && cookieString.Length>0) {
684 m_Cookies = ParseCookies(RequestUri, cookieString);
686 if (m_Cookies==null) {
687 m_Cookies = new CookieCollection();
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;
701 public Version ProtocolVersion {
707 public /* override */ bool HasEntityBody {
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;
715 public /* override */ bool KeepAlive
719 if (m_KeepAlive == TriState.Unspecified)
721 string header = Headers[HttpKnownHeaderNames.ProxyConnection];
722 if (string.IsNullOrEmpty(header))
724 header = GetKnownHeader(HttpRequestHeader.Connection);
726 if (string.IsNullOrEmpty(header))
728 if (ProtocolVersion >= HttpVersion.Version11)
730 m_KeepAlive = TriState.True;
734 header = GetKnownHeader(HttpRequestHeader.KeepAlive);
735 m_KeepAlive = string.IsNullOrEmpty(header) ? TriState.False : TriState.True;
740 header = header.ToLower(CultureInfo.InvariantCulture);
741 m_KeepAlive = header.IndexOf("close") < 0 || header.IndexOf("keep-alive") >= 0 ? TriState.True : TriState.False;
745 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::KeepAlive_get() returning:" + m_KeepAlive);
746 return m_KeepAlive == TriState.True;
750 public /* override */ IPEndPoint RemoteEndPoint {
752 if (m_RemoteEndPoint==null) {
753 m_RemoteEndPoint = UnsafeNclNativeMethods.HttpApi.GetRemoteEndPoint(RequestBuffer, OriginalBlobAddress);
755 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::RemoteEndPoint_get() returning:" + m_RemoteEndPoint);
756 return m_RemoteEndPoint;
760 public /* override */ IPEndPoint LocalEndPoint {
762 if (m_LocalEndPoint==null) {
763 m_LocalEndPoint = UnsafeNclNativeMethods.HttpApi.GetLocalEndPoint(RequestBuffer, OriginalBlobAddress);
765 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::LocalEndPoint_get() returning:" + m_LocalEndPoint);
766 return m_LocalEndPoint;
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)
780 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Close", "");
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();
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
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
821 if (m_SslStatus != SslStatus.Insecure)
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);
831 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() calling UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate size:" + size);
832 uint bytesReceived = 0;
835 UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate(
836 HttpListenerContext.RequestQueueHandle,
838 (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE,
839 asyncResult.RequestBlob,
842 asyncResult.NativeOverlapped);
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)
847 UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.RequestBlob;
848 size = bytesReceived + pClientCertInfo->CertEncodedSize;
849 asyncResult.Reset(size);
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);
860 if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS &&
861 HttpListener.SkipIOCPCallbackOnSuccess)
863 asyncResult.IOCompleted(statusCode, bytesReceived);
869 if (asyncResult!=null) {
870 asyncResult.InternalCleanup();
875 asyncResult = new ListenerClientCertAsyncResult(this, state, requestCallback, 0);
876 asyncResult.InvokeCallback();
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
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
916 if (m_SslStatus != SslStatus.Insecure)
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;
924 byte[] clientCertInfoBlob = new byte[checked((int) size)];
925 fixed (byte* pClientCertInfoBlob = clientCertInfoBlob)
927 UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = (UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO*) pClientCertInfoBlob;
929 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() calling UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate size:" + size);
930 uint bytesReceived = 0;
933 UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate(
934 HttpListenerContext.RequestQueueHandle,
936 (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE,
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;
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) {
957 byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize];
958 Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length);
959 m_ClientCertificate = new X509Certificate2(certEncoded);
961 catch (CryptographicException exception) {
962 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() caught CryptographicException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception));
964 catch (SecurityException exception) {
965 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() caught SecurityException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception));
968 m_ClientCertificateError = (int)pClientCertInfo->CertFlags;
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);
978 m_ClientCertState = ListenerClientCertState.Completed;
981 private string RequestScheme {
983 return IsSecureConnection ? "https" : "http";
987 private Uri RequestUri {
989 if (m_RequestUri == null) {
991 m_RequestUri = HttpListenerRequestUriBuilder.GetRequestUri(
992 m_RawUrl, RequestScheme, m_CookedUrlHost, m_CookedUrlPath, m_CookedUrlQuery);
995 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::RequestUri_get() returning m_RequestUri:" + ValidationHelper.ToString(m_RequestUri));
1001 private DateTime IfModifiedSince {
1003 string headerValue = GetKnownHeader(HttpRequestHeader.IfModifiedSince);
1004 if (headerValue==null) {
1005 return DateTime.Now;
1007 return DateTime.Parse(headerValue, CultureInfo.InvariantCulture);
1012 private string GetKnownHeader(HttpRequestHeader header) {
1013 return UnsafeNclNativeMethods.HttpApi.GetKnownHeader(RequestBuffer, OriginalBlobAddress, (int) header);
1016 internal ChannelBinding GetChannelBinding()
1018 return HttpListenerContext.Listener.GetChannelBindingFromTls(m_ConnectionId);
1021 internal IEnumerable<TokenBinding> GetTlsTokenBindings() {
1023 // Try to get the token binding if not created.
1024 if (Volatile.Read(ref m_TokenBindings) == null)
1028 if (Volatile.Read(ref m_TokenBindings) == null)
1030 // If token binding is supported on the machine get it else create empty list.
1031 if (UnsafeNclNativeMethods.TokenBindingOSHelper.SupportsTokenBinding)
1033 ProcessTlsTokenBindings();
1037 m_TokenBindings = new List<TokenBinding>();
1043 // If the cached status is not success throw exception, else return the token binding
1044 if (0 != m_TokenBindingVerifyMessageStatus)
1046 throw new HttpListenerException(m_TokenBindingVerifyMessageStatus);
1050 return m_TokenBindings.AsReadOnly();
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.
1058 private void ProcessTlsTokenBindings() {
1060 Debug.Assert(m_TokenBindings == null);
1062 if (m_TokenBindings != null)
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;
1072 // Only try to collect the old binding information if there is no V2 binding information available
1073 if (pTokenBindingInfo == null)
1075 pTokenBindingInfo_V1 = UnsafeNclNativeMethods.HttpApi.GetTlsTokenBindingRequestInfo_V1(RequestBuffer, OriginalBlobAddress);
1076 useV1TokenBinding = true;
1079 if (pTokenBindingInfo == null && pTokenBindingInfo_V1 == null)
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".
1086 UnsafeNclNativeMethods.HttpApi.HeapAllocHandle handle = null;
1087 m_TokenBindingVerifyMessageStatus = -1;
1089 fixed (byte* pMemoryBlob = RequestBuffer){
1090 UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2* request = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)pMemoryBlob;
1091 long fixup = pMemoryBlob - (byte*) OriginalBlobAddress;
1093 if (useV1TokenBinding && pTokenBindingInfo_V1 != null)
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,
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,
1118 if (m_TokenBindingVerifyMessageStatus != 0)
1120 throw new HttpListenerException(m_TokenBindingVerifyMessageStatus);
1123 Debug.Assert(handle != null);
1124 Debug.Assert(!handle.IsInvalid);
1128 // If we have an old binding, use the old binding behavior
1129 if (useV1TokenBinding)
1131 GenerateTokenBindings_V1(handle);
1135 GenerateTokenBindings(handle);
1141 /// Method to allow current bindings to be returned
1143 /// <param name="handle"></param>
1144 private void GenerateTokenBindings(UnsafeNclNativeMethods.HttpApi.HeapAllocHandle handle)
1146 UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST* pResultList = (UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST*)handle.DangerousGetHandle();
1147 for (int i = 0; i < pResultList->resultCount; i++)
1149 UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_DATA* pThisResultData = &pResultList->resultData[i];
1151 if (pThisResultData != null)
1153 byte[] retVal = new byte[pThisResultData->identifierSize];
1154 Marshal.Copy((IntPtr)(pThisResultData->identifierData), retVal, 0, retVal.Length);
1156 if (pThisResultData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_PROVIDED)
1158 m_TokenBindings.Add(new TokenBinding(TokenBindingType.Provided, retVal));
1160 else if (pThisResultData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_REFERRED)
1162 m_TokenBindings.Add(new TokenBinding(TokenBindingType.Referred, retVal));
1169 /// Compat method to allow V1 bindings to be returned
1171 /// <param name="handle"></param>
1172 private void GenerateTokenBindings_V1(UnsafeNclNativeMethods.HttpApi.HeapAllocHandle handle)
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++)
1177 UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_DATA_V1* pThisResultData = &pResultList->resultData[i];
1179 if (pThisResultData != null)
1181 // Old V1 Token Binding protocol is still being used, so we need modify the binding message using the old behavior
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);
1189 if (pThisResultData->identifierData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_PROVIDED)
1191 m_TokenBindings.Add(new TokenBinding(TokenBindingType.Provided, retVal));
1193 else if (pThisResultData->identifierData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_REFERRED)
1195 m_TokenBindings.Add(new TokenBinding(TokenBindingType.Referred, retVal));
1201 internal void CheckDisposed() {
1203 throw new ObjectDisposedException(this.GetType().FullName);
1212 static class Helpers {
1214 // Get attribute off header value
1216 internal static String GetAttributeFromHeader(String headerValue, String attrName) {
1217 if (headerValue == null)
1220 int l = headerValue.Length;
1221 int k = attrName.Length;
1223 // find properly separated attribute name
1224 int i = 1; // start searching from 1
1227 i = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, i, CompareOptions.IgnoreCase);
1233 char chPrev = headerValue[i-1];
1234 char chNext = headerValue[i+k];
1235 if ((chPrev == ';' || chPrev == ',' || Char.IsWhiteSpace(chPrev)) && (chNext == '=' || Char.IsWhiteSpace(chNext)))
1241 if (i < 0 || i >= l)
1244 // skip to '=' and the following whitespaces
1246 while (i < l && Char.IsWhiteSpace(headerValue[i]))
1248 if (i >= l || headerValue[i] != '=')
1251 while (i < l && Char.IsWhiteSpace(headerValue[i]))
1257 String attrValue = null;
1261 if (i < l && headerValue[i] == '"') {
1264 j = headerValue.IndexOf('"', i+1);
1265 if (j < 0 || j == i+1)
1268 attrValue = headerValue.Substring(i+1, j-i-1).Trim();
1271 for (j = i; j < l; j++) {
1272 if (headerValue[j] == ' ' || headerValue[j] == ',')
1279 attrValue = headerValue.Substring(i, j-i).Trim();
1285 internal static String[] ParseMultivalueHeader(String s) {
1291 // collect comma-separated values into list
1293 ArrayList values = new ArrayList();
1298 int ci = s.IndexOf(',', i);
1302 // append corresponding server value
1303 values.Add(s.Substring(i, ci-i));
1308 // skip leading space
1309 if (i < l && s[i] == ' ')
1313 // return list as array of strings
1315 int n = values.Count;
1318 // if n is 0 that means s was empty string
1321 strings = new String[1];
1322 strings[0] = String.Empty;
1325 strings = new String[n];
1326 values.CopyTo(0, strings, 0, n);
1332 private static string UrlDecodeStringFromStringInternal(string s, Encoding e) {
1333 int count = s.Length;
1334 UrlDecoder helper = new UrlDecoder(count, e);
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
1340 for (int pos = 0; pos < count; pos++) {
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]);
1353 if (h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0) { // valid 4 hex chars
1354 ch = (char)((h1 << 12) | (h2 << 8) | (h3 << 4) | h4);
1363 int h1 = HexToInt(s[pos+1]);
1364 int h2 = HexToInt(s[pos+2]);
1366 if (h1 >= 0 && h2 >= 0) { // valid 2 hex chars
1367 byte b = (byte)((h1 << 4) | h2);
1370 // don't add as char
1377 if ((ch & 0xFF80) == 0)
1378 helper.AddByte((byte)ch); // 7 bit have to go as bytes because of Unicode
1383 return helper.GetString();
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 :
1393 private class UrlDecoder {
1394 private int _bufferSize;
1396 // Accumulate characters in a special array
1397 private int _numChars;
1398 private char[] _charBuffer;
1400 // Accumulate bytes for decoding into characters in a special array
1401 private int _numBytes;
1402 private byte[] _byteBuffer;
1404 // Encoding to convert chars to bytes
1405 private Encoding _encoding;
1407 private void FlushBytes() {
1408 if (_numBytes > 0) {
1409 _numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars);
1414 internal UrlDecoder(int bufferSize, Encoding encoding) {
1415 _bufferSize = bufferSize;
1416 _encoding = encoding;
1418 _charBuffer = new char[bufferSize];
1419 // byte buffer created on demand
1422 internal void AddChar(char ch) {
1426 _charBuffer[_numChars++] = ch;
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
1433 if (_numBytes == 0 && ((b & 0x80) == 0)) {
1439 if (_byteBuffer == null)
1440 _byteBuffer = new byte[_bufferSize];
1442 _byteBuffer[_numBytes++] = b;
1446 internal String GetString() {
1451 return new String(_charBuffer, 0, _numChars);
1453 return String.Empty;
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;
1463 // find next & while noting first = on the way (and if there are more)
1475 else if (ch == '&') {
1482 // extract the name / value pair
1485 String value = null;
1488 name = s.Substring(si, ti-si);
1489 value = s.Substring(ti+1, i-ti-1);
1492 value = s.Substring(si, i-si);
1495 // add name / value pair to the collection
1499 name == null ? null : UrlDecodeStringFromStringInternal(name, encoding),
1500 UrlDecodeStringFromStringInternal(value, encoding));
1502 nvc.Add(name, value);
1506 if (i == l-1 && s[i] == '&')