1 //------------------------------------------------------------------------------
2 // <copyright file="HttpRequest.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 /*
8  * Request intrinsic
9  *
10  * Copyright (c) 1998 Microsoft Corporation
11  */
12 
13 namespace System.Web {
14     using System;
15     using System.Collections;
16     using System.Collections.Specialized;
17     using System.Configuration.Assemblies;
18     using System.Diagnostics.CodeAnalysis;
19     using System.Globalization;
20     using System.IO;
21     using System.Runtime.InteropServices;
22     using System.Security.Authentication.ExtendedProtection;
23     using System.Security.Permissions;
24     using System.Security.Principal;
25     using System.Text;
26     using System.Threading;
27     using System.Web.Configuration;
28     using System.Web.Hosting;
29     using System.Web.Management;
30     using System.Web.Routing;
31     using System.Web.Security;
32     using System.Web.SessionState;
33     using System.Web.Util;
34 
35     // enumeration of dynamic server variables
36     internal enum DynamicServerVariable {
37         AUTH_TYPE = 1,
38         AUTH_USER = 2,
39         PATH_INFO = 3,
40         PATH_TRANSLATED = 4,
41         QUERY_STRING = 5,
42         SCRIPT_NAME = 6
43     };
44 
45     internal enum HttpVerb {
46         Unparsed = 0,   // must be 0 so that it's zero-init value is Unparsed
47         Unknown,
48         GET,
49         PUT,
50         HEAD,
51         POST,
52         DEBUG,
53         DELETE,
54     }
55 
56 
57     /// <devdoc>
58     ///    <para>
59     ///       Enables
60     ///       type-safe browser to server communication. Used to gain access to HTTP request data
61     ///       elements supplied by a client.
62     ///    </para>
63     /// </devdoc>
64     public sealed class HttpRequest {
65         // worker request
66         [DoNotReset]
67         private HttpWorkerRequest _wr;
68 
69         // context
70         [DoNotReset]
71         private HttpContext _context;
72 
73         // properties
74         private String _httpMethod;
75         private HttpVerb _httpVerb;
76         private String _requestType;
77         private VirtualPath _path;
78         private String _rewrittenUrl;
79         private bool   _computePathInfo;
80         private VirtualPath _filePath;
81         private VirtualPath _currentExecutionFilePath;
82         private VirtualPath _pathInfo;
83         private String _queryStringText;
84         private bool   _queryStringOverriden;
85         private byte[] _queryStringBytes;
86         private String _pathTranslated;
87         private String _contentType;
88         private int    _contentLength = -1;
89         private String _clientTarget;
90         private String[] _acceptTypes;
91         private String[] _userLanguages;
92         private HttpBrowserCapabilities _browsercaps;
93         private Uri _url;
94         private Uri _referrer;
95         private HttpInputStream _inputStream;
96         private HttpClientCertificate _clientCertificate;
97         private bool _tlsTokenBindingInfoResolved;
98         private ITlsTokenBindingInfo _tlsTokenBindingInfo;
99         private WindowsIdentity _logonUserIdentity;
100         [DoNotReset]
101         private RequestContext _requestContext;
102         private string _rawUrl;
103         private Stream _readEntityBodyStream;
104         private ReadEntityBodyMode _readEntityBodyMode;
105 
106         // collections
107         private UnvalidatedRequestValues _unvalidatedRequestValues;
108         private HttpValueCollection _params;
109         private HttpValueCollection _queryString;
110         private HttpValueCollection _form;
111         private HttpHeaderCollection _headers;
112         private HttpServerVarsCollection _serverVariables;
113         private HttpCookieCollection _cookies;
114         [DoNotReset] // we can't reset this field when transitioning to WebSockets because it's our only remaining reference to the response cookies collection
115         private HttpCookieCollection _storedResponseCookies;
116         private HttpFileCollection _files;
117 
118         // content (to be read once)
119         private HttpRawUploadedContent _rawContent;
120         private bool _needToInsertEntityBody;
121         private MultipartContentElement[] _multipartContentElements;
122 
123         // encoding (for content and query string)
124         private Encoding _encoding;
125 
126         // content filtering
127         private HttpInputStreamFilterSource _filterSource;
128         private Stream _installedFilter;
129         private bool _filterApplied;
130 
131         // Input validation
132         #pragma warning disable 0649
133         private SimpleBitVector32 _flags;
134         #pragma warning restore 0649
135         // const masks into the BitVector32
136         private const int needToValidateQueryString     = 0x0001;
137         private const int needToValidateForm            = 0x0002;
138         private const int needToValidateCookies         = 0x0004;
139         private const int needToValidateHeaders         = 0x0008;
140         private const int needToValidateServerVariables = 0x0010;
141         private const int contentEncodingResolved       = 0x0020;
142         private const int needToValidatePostedFiles     = 0x0040;
143         private const int needToValidateRawUrl          = 0x0080;
144         private const int needToValidatePath            = 0x0100;
145         private const int needToValidatePathInfo        = 0x0200;
146         private const int hasValidateInputBeenCalled    = 0x8000;
147         private const int needToValidateCookielessHeader = 0x10000;
148         // True if granular request validation is enabled (validationmode >= 4.5); false if all collections validated eagerly.
149         private const int granularValidationEnabled     = 0x40000000;
150         // True if request validation is suppressed (validationMode == 0.0); false if validation can be enabled via a call to ValidateInput().
151         private const int requestValidationSuppressed   = unchecked((int)0x80000000);
152 
153 
154         // Browser caps one-time evaluator objects
155         internal static object s_browserLock = new object();
156         internal static bool s_browserCapsEvaled = false;
157 
158         /*
159          * Internal constructor to create requests
160          * that have associated HttpWorkerRequest
161          *
162          * @param wr HttpWorkerRequest
163          */
HttpRequest(HttpWorkerRequest wr, HttpContext context)164         internal HttpRequest(HttpWorkerRequest wr, HttpContext context) {
165             _wr = wr;
166             _context = context;
167         }
168 
169         /*
170          * Public constructor for request that come from arbitrary place
171          *
172          * @param filename physical file name
173          * @param queryString query string
174          */
175 
176         /// <devdoc>
177         ///    <para>
178         ///       Initializes an HttpRequest object.
179         ///    </para>
180         /// </devdoc>
HttpRequest(String filename, String url, String queryString)181         public HttpRequest(String filename, String url, String queryString) {
182             _wr = null;
183             _pathTranslated = filename;
184             _httpMethod = "GET";
185             _url = new Uri(url);
186             _path = VirtualPath.CreateAbsolute(_url.AbsolutePath);
187             _queryStringText = queryString;
188             _queryStringOverriden = true;
189             _queryString = new HttpValueCollection(_queryStringText, true, true, Encoding.Default);
190 
191             PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING);
192         }
193 
HttpRequest(VirtualPath virtualPath, String queryString)194         internal HttpRequest(VirtualPath virtualPath, String queryString) {
195             _wr = null;
196             _pathTranslated = virtualPath.MapPath();
197             _httpMethod = "GET";
198             _url = new Uri("http://localhost" + virtualPath.VirtualPathString);
199             _path = virtualPath;
200             _queryStringText = queryString;
201             _queryStringOverriden = true;
202             _queryString = new HttpValueCollection(_queryStringText, true, true, Encoding.Default);
203 
204             PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING);
205         }
206 
207         internal bool NeedToInsertEntityBody {
208             get { return _needToInsertEntityBody; }
209             set { _needToInsertEntityBody = value; }
210         }
211 
SetRawContent(HttpRawUploadedContent rawContent)212         internal void SetRawContent(HttpRawUploadedContent rawContent) {
213             Debug.Assert(rawContent != null);
214             if (rawContent.Length > 0) {
215                 NeedToInsertEntityBody = true;
216             }
217             _rawContent = rawContent;
218         }
219 
220         internal byte[] EntityBody { get { return NeedToInsertEntityBody ? _rawContent.GetAsByteArray() : null; } }
221 
222         internal string ClientTarget {
223             get {
224                 return (_clientTarget == null) ? String.Empty : _clientTarget;
225             }
226             set {
227                 _clientTarget = value;
228                 // force re-create of browser caps
229                 _browsercaps = null;
230             }
231         }
232 
233         internal HttpContext Context {
234             get { return _context; }
235             set { _context = value; }
236         }
237 
238         public RequestContext RequestContext {
239             get {
240                 // Create an empty request context if we don't have one set
241                 if (_requestContext == null) {
242                     HttpContext context = Context ?? HttpContext.Current;
243                     _requestContext = new RequestContext(new HttpContextWrapper(context), new RouteData());
244                 }
245                 return _requestContext;
246             }
247             set {
248                 _requestContext = value;
249             }
250         }
251 
252         private bool HasTransitionedToWebSocketRequest {
253             get {
254                 return (Context != null && Context.HasWebSocketRequestTransitionCompleted);
255             }
256         }
257 
258         /*
259          * internal response object
260          */
261         internal HttpResponse Response {
262             get {
263                 if (_context == null)
264                     return null;
265                 return _context.Response;
266             }
267         }
268 
269         /*
270          * Public property to determine if request is local
271          */
272 
273         public bool IsLocal {
274             get {
275                 if (_wr != null) {
276                     return _wr.IsLocal();
277                 }
278                 else {
279                     return false;
280                 }
281             }
282         }
283 
284         /*
285          *  Cleanup code
286          */
Dispose()287         internal void Dispose() {
288             if (_serverVariables != null)
289                 _serverVariables.Dispose();  // disconnect from request
290 
291             if (_rawContent != null)
292                 _rawContent.Dispose();  // remove temp file with uploaded content
293 
294 	    //
295 
296 
297 
298 
299 
300 
301         }
302 
303         //
304         // Misc private methods to fill in collections from HttpWorkerRequest
305         // properties
306         //
307 
ParseMultivalueHeader(String s)308         internal static String[] ParseMultivalueHeader(String s) {
309             int l = (s != null) ? s.Length : 0;
310             if (l == 0)
311                 return null;
312 
313             // collect comma-separated values into list
314 
315             ArrayList values = new ArrayList();
316             int i = 0;
317 
318             while (i < l) {
319                 // find next ,
320                 int ci = s.IndexOf(',', i);
321                 if (ci < 0)
322                     ci = l;
323 
324                 // append corresponding server value
325                 values.Add(s.Substring(i, ci-i));
326 
327                 // move to next
328                 i = ci+1;
329 
330                 // skip leading space
331                 if (i < l && s[i] == ' ')
332                     i++;
333             }
334 
335             // return list as array of strings
336 
337             int n = values.Count;
338             if (n == 0)
339                 return null;
340 
341             String[] strings = new String[n];
342             values.CopyTo(0, strings, 0, n);
343             return strings;
344         }
345 
346         //
347         // Query string collection support
348         //
349 
FillInQueryStringCollection()350         private void FillInQueryStringCollection() {
351             // try from raw bytes when available (better for globalization)
352 
353             byte[] rawQueryString = this.QueryStringBytes;
354 
355             if (rawQueryString != null) {
356                 if (rawQueryString.Length != 0)
357                     _queryString.FillFromEncodedBytes(rawQueryString, QueryStringEncoding);
358             }
359             else if (!(String.IsNullOrEmpty(this.QueryStringText))) {
360                 _queryString.FillFromString(this.QueryStringText, true, QueryStringEncoding);
361             }
362         }
363 
364         //
365         // Form collection support
366         //
367 
FillInFormCollection()368         private void FillInFormCollection() {
369             if (_wr == null)
370                 return;
371 
372             if (!_wr.HasEntityBody())
373                 return;
374 
375             String contentType = this.ContentType;
376             if (contentType == null)
377                 return;
378 
379             if (_readEntityBodyMode == ReadEntityBodyMode.Bufferless) {
380                 return;
381             }
382 
383             if (StringUtil.StringStartsWithIgnoreCase(contentType, "application/x-www-form-urlencoded")) {
384                 // regular urlencoded form
385 
386                 byte[] formBytes = null;
387                 HttpRawUploadedContent content = GetEntireRawContent();
388 
389                 if (content != null)
390                     formBytes = content.GetAsByteArray();
391 
392                 if (formBytes != null) {
393                     try {
394                         _form.FillFromEncodedBytes(formBytes, ContentEncoding);
395                     }
396                     catch (Exception e) {
397                         // could be thrown because of malformed data
398                         throw new HttpException(SR.GetString(SR.Invalid_urlencoded_form_data), e);
399                     }
400                 }
401             }
402             else if (StringUtil.StringStartsWithIgnoreCase(contentType, "multipart/form-data")) {
403                 // multipart form
404 
405                 MultipartContentElement[] elements = GetMultipartContent();
406 
407                 if (elements != null) {
408                     for (int i = 0; i < elements.Length; i++) {
409                         if (elements[i].IsFormItem) {
410                             _form.ThrowIfMaxHttpCollectionKeysExceeded();
411                             _form.Add(elements[i].Name, elements[i].GetAsString(ContentEncoding));
412                         }
413                     }
414                 }
415             }
416         }
417 
418         //
419         // Headers collection support
420         //
421 
FillInHeadersCollection()422         private void FillInHeadersCollection() {
423             if (_wr == null)
424                 return;
425 
426             // known headers
427 
428             for (int i = 0; i < HttpWorkerRequest.RequestHeaderMaximum; i++) {
429                 String h = _wr.GetKnownRequestHeader(i);
430 
431                 if (!String.IsNullOrEmpty(h)) {
432                     String name = HttpWorkerRequest.GetKnownRequestHeaderName(i);
433                     _headers.SynchronizeHeader(name, h);
434                 }
435             }
436 
437             // unknown headers
438 
439             String[][] hh = _wr.GetUnknownRequestHeaders();
440 
441             if (hh != null) {
442                 for (int i = 0; i < hh.Length; i++)
443                     _headers.SynchronizeHeader(hh[i][0], hh[i][1]);
444             }
445         }
446 
447         //
448         // Server variables collection support
449         //
450 
ServerVariableNameFromHeader(String header)451         private static String ServerVariableNameFromHeader(String header) {
452             return("HTTP_" + header.ToUpper(CultureInfo.InvariantCulture).Replace('-', '_'));
453         }
454 
CombineAllHeaders(bool asRaw)455         private String CombineAllHeaders(bool asRaw) {
456             if (_wr == null)
457                 return String.Empty;
458 
459             StringBuilder sb = new StringBuilder(256);
460 
461             // known headers
462 
463             for (int i = 0; i < HttpWorkerRequest.RequestHeaderMaximum; i++) {
464                 String h = _wr.GetKnownRequestHeader(i);
465 
466                 if (!String.IsNullOrEmpty(h)) {
467                     String name;
468                     if (!asRaw)
469                         name = HttpWorkerRequest.GetServerVariableNameFromKnownRequestHeaderIndex(i);
470                     else
471                         name = HttpWorkerRequest.GetKnownRequestHeaderName(i);
472 
473                     if (name != null) {
474                         sb.Append(name);
475                         sb.Append(asRaw ? ": " : ":");  // for ASP compat don't add space
476                         sb.Append(h);
477                         sb.Append("\r\n");
478                     }
479                 }
480             }
481 
482             // unknown headers
483 
484             String[][] hh = _wr.GetUnknownRequestHeaders();
485 
486             if (hh != null) {
487                 for (int i = 0; i < hh.Length; i++) {
488                     String name = hh[i][0];
489 
490                     if (!asRaw)
491                         name = ServerVariableNameFromHeader(name);
492 
493                     sb.Append(name);
494                     sb.Append(asRaw ? ": " : ":");  // for ASP compat don't add space
495                     sb.Append(hh[i][1]);
496                     sb.Append("\r\n");
497                 }
498             }
499 
500             return sb.ToString();
501         }
502 
503         // callback to calculate dynamic server variable
CalcDynamicServerVariable(DynamicServerVariable var)504         internal String CalcDynamicServerVariable(DynamicServerVariable var) {
505             String value = null;
506 
507             switch (var) {
508                 case DynamicServerVariable.AUTH_TYPE:
509                     if (_context.User != null && _context.User.Identity.IsAuthenticated)
510                         value = _context.User.Identity.AuthenticationType;
511                     else
512                         value = String.Empty;
513                     break;
514                 case DynamicServerVariable.AUTH_USER:
515                     if (_context.User != null && _context.User.Identity.IsAuthenticated)
516                         value = _context.User.Identity.Name;
517                     else
518                         value = String.Empty;
519                     break;
520                 case DynamicServerVariable.PATH_INFO:
521                     value = this.Path;
522                     break;
523                 case DynamicServerVariable.PATH_TRANSLATED:
524                     value = this.PhysicalPathInternal;
525                     break;
526                 case DynamicServerVariable.QUERY_STRING:
527                     value = this.QueryStringText;
528                     break;
529                 case DynamicServerVariable.SCRIPT_NAME:
530                     value = this.FilePath;
531                     break;
532             }
533 
534             return value;
535         }
536 
AddServerVariableToCollection(String name, DynamicServerVariable var)537         private void AddServerVariableToCollection(String name, DynamicServerVariable var) {
538             // dynamic server var
539             _serverVariables.AddDynamic(name, var);
540         }
541 
AddServerVariableToCollection(String name, String value)542         private void AddServerVariableToCollection(String name, String value) {
543             if (value == null)
544                 value = String.Empty;
545             // static server var
546             _serverVariables.AddStatic(name, value);
547         }
548 
AddServerVariableToCollection(String name)549         private void AddServerVariableToCollection(String name) {
550             // static server var from worker request
551             _serverVariables.AddStatic(name, _wr.GetServerVariable(name));
552         }
553 
FillInServerVariablesCollection()554         internal void FillInServerVariablesCollection() {
555             if (_wr == null)
556                 return;
557 
558             //  Add from hardcoded list
559 
560             AddServerVariableToCollection("ALL_HTTP",           CombineAllHeaders(false));
561             AddServerVariableToCollection("ALL_RAW",            CombineAllHeaders(true));
562 
563             AddServerVariableToCollection("APPL_MD_PATH");
564 
565             AddServerVariableToCollection("APPL_PHYSICAL_PATH", _wr.GetAppPathTranslated());
566 
567             AddServerVariableToCollection("AUTH_TYPE",          DynamicServerVariable.AUTH_TYPE);
568             AddServerVariableToCollection("AUTH_USER",          DynamicServerVariable.AUTH_USER);
569 
570             AddServerVariableToCollection("AUTH_PASSWORD");
571 
572             AddServerVariableToCollection("LOGON_USER");
573             AddServerVariableToCollection("REMOTE_USER",        DynamicServerVariable.AUTH_USER);
574 
575             AddServerVariableToCollection("CERT_COOKIE");
576             AddServerVariableToCollection("CERT_FLAGS");
577             AddServerVariableToCollection("CERT_ISSUER");
578             AddServerVariableToCollection("CERT_KEYSIZE");
579             AddServerVariableToCollection("CERT_SECRETKEYSIZE");
580             AddServerVariableToCollection("CERT_SERIALNUMBER");
581             AddServerVariableToCollection("CERT_SERVER_ISSUER");
582             AddServerVariableToCollection("CERT_SERVER_SUBJECT");
583             AddServerVariableToCollection("CERT_SUBJECT");
584 
585             String clString = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentLength);
586             AddServerVariableToCollection("CONTENT_LENGTH",     (clString != null) ? clString : "0");
587 
588             AddServerVariableToCollection("CONTENT_TYPE",       this.ContentType);
589 
590             AddServerVariableToCollection("GATEWAY_INTERFACE");
591 
592             AddServerVariableToCollection("HTTPS");
593             AddServerVariableToCollection("HTTPS_KEYSIZE");
594             AddServerVariableToCollection("HTTPS_SECRETKEYSIZE");
595             AddServerVariableToCollection("HTTPS_SERVER_ISSUER");
596             AddServerVariableToCollection("HTTPS_SERVER_SUBJECT");
597 
598             AddServerVariableToCollection("INSTANCE_ID");
599             AddServerVariableToCollection("INSTANCE_META_PATH");
600 
601             AddServerVariableToCollection("LOCAL_ADDR",         _wr.GetLocalAddress());
602 
603             AddServerVariableToCollection("PATH_INFO",          DynamicServerVariable.PATH_INFO);
604             AddServerVariableToCollection("PATH_TRANSLATED",    DynamicServerVariable.PATH_TRANSLATED);
605 
606             AddServerVariableToCollection("QUERY_STRING",       DynamicServerVariable.QUERY_STRING);
607 
608             AddServerVariableToCollection("REMOTE_ADDR",        this.UserHostAddress);
609             AddServerVariableToCollection("REMOTE_HOST",        this.UserHostName);
610 
611             AddServerVariableToCollection("REMOTE_PORT");
612 
613             AddServerVariableToCollection("REQUEST_METHOD",     this.HttpMethod);
614 
615             AddServerVariableToCollection("SCRIPT_NAME",        DynamicServerVariable.SCRIPT_NAME);
616 
617             AddServerVariableToCollection("SERVER_NAME",        _wr.GetServerName());
618             AddServerVariableToCollection("SERVER_PORT",        _wr.GetLocalPortAsString());
619 
620             AddServerVariableToCollection("SERVER_PORT_SECURE", _wr.IsSecure() ? "1" : "0");
621 
622             AddServerVariableToCollection("SERVER_PROTOCOL",    _wr.GetHttpVersion());
623             AddServerVariableToCollection("SERVER_SOFTWARE");
624 
625             AddServerVariableToCollection("URL",                DynamicServerVariable.SCRIPT_NAME);
626 
627             // Add all headers in HTTP_XXX format
628 
629             for (int i = 0; i < HttpWorkerRequest.RequestHeaderMaximum; i++) {
630                 String h = _wr.GetKnownRequestHeader(i);
631                 if (!String.IsNullOrEmpty(h))
632                     AddServerVariableToCollection(HttpWorkerRequest.GetServerVariableNameFromKnownRequestHeaderIndex(i), h);
633             }
634 
635             String[][] hh = _wr.GetUnknownRequestHeaders();
636 
637             if (hh != null) {
638                 for (int i = 0; i < hh.Length; i++)
639                     AddServerVariableToCollection(ServerVariableNameFromHeader(hh[i][0]), hh[i][1]);
640             }
641         }
642 
643         //
644         // Cookies collection support
645         //
646 
CreateCookieFromString(String s)647         internal static HttpCookie CreateCookieFromString(String s) {
648             HttpCookie c = new HttpCookie();
649 
650             int l = (s != null) ? s.Length : 0;
651             int i = 0;
652             int ai, ei;
653             bool firstValue = true;
654             int numValues = 1;
655 
656             // Format: cookiename[=key1=val2&key2=val2&...]
657 
658             while (i < l) {
659                 //  find next &
660                 ai = s.IndexOf('&', i);
661                 if (ai < 0)
662                     ai = l;
663 
664                 // first value might contain cookie name before =
665                 if (firstValue) {
666                     ei = s.IndexOf('=', i);
667 
668                     if (ei >= 0 && ei < ai) {
669                         c.Name = s.Substring(i, ei-i);
670                         i = ei+1;
671                     }
672                     else if (ai == l) {
673                         // the whole cookie is just a name
674                         c.Name = s;
675                         break;
676                     }
677 
678                     firstValue = false;
679                 }
680 
681                 // find '='
682                 ei = s.IndexOf('=', i);
683 
684                 if (ei < 0 && ai == l && numValues == 0) {
685                     // simple cookie with simple value
686                     c.Value = s.Substring(i, l-i);
687                 }
688                 else if (ei >= 0 && ei < ai) {
689                     // key=value
690                     c.Values.Add(s.Substring(i, ei-i), s.Substring(ei+1, ai-ei-1));
691                     numValues++;
692                 }
693                 else {
694                     // value without key
695                     c.Values.Add(null, s.Substring(i, ai-i));
696                     numValues++;
697                 }
698 
699                 i = ai+1;
700             }
701 
702             return c;
703         }
704 
FillInCookiesCollection(HttpCookieCollection cookieCollection, bool includeResponse)705         internal void FillInCookiesCollection(HttpCookieCollection cookieCollection, bool includeResponse) {
706             if (_wr == null)
707                 return;
708 
709             String s = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderCookie);
710 
711             // Parse the cookie server variable.
712             // Format: c1=k1=v1&k2=v2; c2=...
713 
714             int l = (s != null) ? s.Length : 0;
715             int i = 0;
716             int j;
717             char ch;
718 
719             HttpCookie lastCookie = null;
720 
721             while (i < l) {
722                 // find next ';' (don't look to ',' as per 91884)
723                 j = i;
724                 while (j < l) {
725                     ch = s[j];
726                     if (ch == ';')
727                         break;
728                     j++;
729                 }
730 
731                 // create cookie form string
732                 String cookieString = s.Substring(i, j-i).Trim();
733                 i = j+1; // next cookie start
734 
735                 if (cookieString.Length == 0)
736                     continue;
737 
738                 HttpCookie cookie = CreateCookieFromString(cookieString);
739 
740                 // some cookies starting with '$' are really attributes of the last cookie
741                 if (lastCookie != null) {
742                     String name = cookie.Name;
743 
744                     // add known attribute to the last cookie (if any)
745                     if (name != null && name.Length > 0 && name[0] == '$') {
746                         if (StringUtil.EqualsIgnoreCase(name, "$Path"))
747                             lastCookie.Path = cookie.Value;
748                         else if (StringUtil.EqualsIgnoreCase(name, "$Domain"))
749                             lastCookie.Domain = cookie.Value;
750 
751                         continue;
752                     }
753                 }
754 
755                 // regular cookie
756                 cookieCollection.AddCookie(cookie, true);
757                 lastCookie = cookie;
758 
759                 // goto next cookie
760             }
761 
762             // Append response cookies
763             if (includeResponse) {
764                 // If we have a reference to the response cookies collection, use it directly
765                 // rather than going through the Response object (which might not be available, e.g.
766                 // if we have already transitioned to a WebSockets request).
767                 HttpCookieCollection storedResponseCookies = _storedResponseCookies;
768                 if (storedResponseCookies == null && !HasTransitionedToWebSocketRequest && Response != null) {
769                     storedResponseCookies = Response.GetCookiesNoCreate();
770                 }
771 
772                 if (storedResponseCookies != null) {
773                     cookieCollection.Append(storedResponseCookies);
774                 }
775 
776                 // release any stored reference to the response cookie collection
777                 _storedResponseCookies = null;
778             }
779         }
780 
StoreReferenceToResponseCookies(HttpCookieCollection responseCookies)781         internal void StoreReferenceToResponseCookies(HttpCookieCollection responseCookies) {
782             _storedResponseCookies = responseCookies;
783         }
784 
785         // Params collection support
FillInParamsCollection()786         private void FillInParamsCollection() {
787             _params.Add(this.QueryString);
788             _params.Add(this.Form);
789             _params.Add(this.Cookies);
790             _params.Add(this.ServerVariables);
791         }
792 
793         //
794         // Files collection support
795         //
796 
FillInFilesCollection()797         private void FillInFilesCollection() {
798             if (_wr == null)
799                 return;
800 
801             if (!StringUtil.StringStartsWithIgnoreCase(ContentType, "multipart/form-data"))
802                 return;
803 
804             MultipartContentElement[] elements = GetMultipartContent();
805             if (elements == null)
806                 return;
807 
808             for (int i = 0; i < elements.Length; i++) {
809                 if (elements[i].IsFile) {
810                     HttpPostedFile p = elements[i].GetAsPostedFile();
811                     _files.AddFile(elements[i].Name, p);
812                 }
813             }
814         }
815 
816         //
817         // Reading posted content ...
818         //
819 
820         /*
821          * Get attribute off header value
822          */
GetAttributeFromHeader(String headerValue, String attrName)823         private static String GetAttributeFromHeader(String headerValue, String attrName) {
824             if (headerValue == null)
825                 return null;
826 
827             int l = headerValue.Length;
828             int k = attrName.Length;
829 
830             // find properly separated attribute name
831             int i = 1; // start searching from 1
832 
833             while (i < l) {
834                 i = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, i, CompareOptions.IgnoreCase);
835                 if (i < 0)
836                     break;
837                 if (i+k >= l)
838                     break;
839 
840                 char chPrev = headerValue[i-1];
841                 char chNext = headerValue[i+k];
842                 if ((chPrev == ';' || chPrev == ',' || Char.IsWhiteSpace(chPrev)) && (chNext == '=' || Char.IsWhiteSpace(chNext)))
843                     break;
844 
845                 i += k;
846             }
847 
848             if (i < 0 || i >= l)
849                 return null;
850 
851             // skip to '=' and the following whitespaces
852             i += k;
853             while (i < l && Char.IsWhiteSpace(headerValue[i]))
854                 i++;
855             if (i >= l || headerValue[i] != '=')
856                 return null;
857             i++;
858             while (i < l && Char.IsWhiteSpace(headerValue[i]))
859                 i++;
860             if (i >= l)
861                 return null;
862 
863             // parse the value
864             String attrValue = null;
865 
866             int j;
867 
868             if (i < l && headerValue[i] == '"') {
869                 if (i == l-1)
870                     return null;
871                 j = headerValue.IndexOf('"', i+1);
872                 if (j < 0 || j == i+1)
873                     return null;
874 
875                 attrValue = headerValue.Substring(i+1, j-i-1).Trim();
876             }
877             else {
878                 for (j = i; j < l; j++) {
879                     if (headerValue[j] == ' ' || headerValue[j] == ',')
880                         break;
881                 }
882 
883                 if (j == i)
884                     return null;
885 
886                 attrValue = headerValue.Substring(i, j-i).Trim();
887             }
888 
889             return attrValue;
890         }
891 
892         /*
893          * In case content-type header contains encoding it should override the config
894          */
GetEncodingFromHeaders()895         private Encoding GetEncodingFromHeaders() {
896 
897             if (UserAgent != null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP")) {
898                 String postDataCharset = Headers["x-up-devcap-post-charset"];
899                 if (!String.IsNullOrEmpty(postDataCharset)) {
900                     try {
901                         return Encoding.GetEncoding(postDataCharset);
902                     }
903                     catch {
904                         // Exception may be thrown when charset is not valid.
905                         // In this case, do nothing, and let the framework
906                         // use the configured RequestEncoding setting.
907                     }
908                 }
909             }
910 
911 
912             if (!_wr.HasEntityBody())
913                 return null;
914 
915             String contentType = this.ContentType;
916             if (contentType == null)
917                 return null;
918 
919             String charSet = GetAttributeFromHeader(contentType, "charset");
920             if (charSet == null)
921                 return null;
922 
923             Encoding encoding = null;
924 
925             try {
926                 encoding = Encoding.GetEncoding(charSet);
927             }
928             catch {
929                 // bad encoding string throws an exception that needs to be consumed
930             }
931 
932             return encoding;
933         }
934 
935         /*
936          * Read entire raw content as byte array
937          */
GetEntireRawContent()938         private HttpRawUploadedContent GetEntireRawContent() {
939             if (_wr == null)
940                 return null;
941 
942             if (_rawContent != null) {
943                 // if _rawContent was set by HttpBufferlessInputStream, then we will apply the filter here
944                 if (_installedFilter != null && !_filterApplied) {
945                     ApplyFilter(ref _rawContent, RuntimeConfig.GetConfig(_context).HttpRuntime.RequestLengthDiskThresholdBytes);
946                 }
947                 return _rawContent;
948             }
949 
950             if (_readEntityBodyMode == ReadEntityBodyMode.None) {
951                 _readEntityBodyMode = ReadEntityBodyMode.Classic;
952             }
953             else if (_readEntityBodyMode == ReadEntityBodyMode.Buffered) {
954                 // _rawContent should have been set already
955                 throw new InvalidOperationException(SR.GetString(SR.Invalid_operation_with_get_buffered_input_stream));
956             }
957             else if (_readEntityBodyMode == ReadEntityBodyMode.Bufferless) {
958                 throw new HttpException(SR.GetString(SR.Incompatible_with_get_bufferless_input_stream));
959             }
960 
961             // enforce the limit
962             HttpRuntimeSection cfg = RuntimeConfig.GetConfig(_context).HttpRuntime;
963             int limit = cfg.MaxRequestLengthBytes;
964             if (ContentLength > limit) {
965                 if ( !(_wr is IIS7WorkerRequest) ) {
966                     Response.CloseConnectionAfterError();
967                 }
968                 throw new HttpException(SR.GetString(SR.Max_request_length_exceeded),
969                                         null, WebEventCodes.RuntimeErrorPostTooLarge);
970             }
971 
972             // threshold to go to file
973 
974             int fileThreshold = cfg.RequestLengthDiskThresholdBytes;
975 
976             // read the preloaded content
977 
978             HttpRawUploadedContent rawContent = new HttpRawUploadedContent(fileThreshold, ContentLength);
979 
980             byte[] preloadedContent = _wr.GetPreloadedEntityBody();
981 
982             if (preloadedContent != null) {
983                 _wr.UpdateRequestCounters(preloadedContent.Length);
984                 rawContent.AddBytes(preloadedContent, 0, preloadedContent.Length);
985             }
986 
987             // read the remaing content
988 
989             if (!_wr.IsEntireEntityBodyIsPreloaded()) {
990                 int remainingBytes = (ContentLength > 0) ? ContentLength - rawContent.Length : Int32.MaxValue;
991 
992                 HttpApplication app = _context.ApplicationInstance;
993                 byte[] buf = (app != null) ? app.EntityBuffer : new byte[8 * 1024];
994                 int numBytesRead = rawContent.Length;
995 
996                 while (remainingBytes > 0) {
997                     int bytesToRead = buf.Length;
998                     if (bytesToRead > remainingBytes)
999                         bytesToRead = remainingBytes;
1000 
1001                     int bytesRead = _wr.ReadEntityBody(buf, bytesToRead);
1002                     if (bytesRead <= 0)
1003                         break;
1004 
1005                     _wr.UpdateRequestCounters(bytesRead);
1006 
1007                     rawContent.AddBytes(buf, 0, bytesRead);
1008 
1009                     remainingBytes -= bytesRead;
1010                     numBytesRead += bytesRead;
1011 
1012                     if (numBytesRead > limit) {
1013                         throw new HttpException(SR.GetString(SR.Max_request_length_exceeded),
1014                                     null, WebEventCodes.RuntimeErrorPostTooLarge);
1015                     }
1016 
1017                     // Fail synchrously if receiving the request content takes too long
1018                     // RequestTimeoutManager is not efficient in case of ThreadPool starvation
1019                     // as the timer callback doing Thread.Abort may not trigger for a long time
1020                     if (remainingBytes > 0 && _context.HasTimeoutExpired) {
1021                         throw new HttpException(SR.GetString(SR.Request_timed_out));
1022                     }
1023                 }
1024             }
1025 
1026             rawContent.DoneAddingBytes();
1027 
1028             // filter content
1029             if (_installedFilter != null) {
1030                 ApplyFilter(ref rawContent, fileThreshold);
1031             }
1032 
1033             SetRawContent(rawContent);
1034             return _rawContent;
1035         }
1036 
ApplyFilter(ref HttpRawUploadedContent rawContent, int fileThreshold)1037         private void ApplyFilter(ref HttpRawUploadedContent rawContent, int fileThreshold) {
1038             if (_installedFilter != null) {
1039                 _filterApplied = true;
1040                 if (rawContent.Length > 0) {
1041                     try {
1042                         try {
1043                             _filterSource.SetContent(rawContent);
1044 
1045                             HttpRawUploadedContent filteredRawContent = new HttpRawUploadedContent(fileThreshold, rawContent.Length);
1046                             HttpApplication app = _context.ApplicationInstance;
1047                             byte[] buf = (app != null) ? app.EntityBuffer : new byte[8 * 1024];
1048 
1049                             for (;;) {
1050                                 int bytesRead = _installedFilter.Read(buf, 0, buf.Length);
1051                                 if (bytesRead == 0)
1052                                     break;
1053                                 filteredRawContent.AddBytes(buf, 0, bytesRead);
1054                             }
1055 
1056                             filteredRawContent.DoneAddingBytes();
1057                             rawContent = filteredRawContent;
1058                         }
1059                         finally {
1060                             _filterSource.SetContent(null);
1061                         }
1062                     }
1063                     catch { // Protect against exception filters
1064                         throw;
1065                     }
1066                 }
1067             }
1068         }
1069 
1070         /*
1071          * Get multipart posted content as array of elements
1072          */
GetMultipartContent()1073         private MultipartContentElement[] GetMultipartContent() {
1074             // already parsed
1075             if (_multipartContentElements != null)
1076                 return _multipartContentElements;
1077 
1078             // check the boundary
1079             byte[] boundary = GetMultipartBoundary();
1080             if (boundary == null)
1081                 return new MultipartContentElement[0];
1082 
1083             // read the content if not read already
1084             HttpRawUploadedContent content = GetEntireRawContent();
1085             if (content == null)
1086                 return new MultipartContentElement[0];
1087 
1088             // do the parsing
1089             _multipartContentElements = HttpMultipartContentTemplateParser.Parse(content, content.Length, boundary, ContentEncoding);
1090             return _multipartContentElements;
1091         }
1092 
1093         /*
1094          * Get boundary for the posted multipart content as byte array
1095          */
1096 
GetMultipartBoundary()1097         private byte[] GetMultipartBoundary() {
1098             // extract boundary value
1099             String b = GetAttributeFromHeader(ContentType, "boundary");
1100             if (b == null)
1101                 return null;
1102 
1103             // prepend with "--" and convert to byte array
1104             b = "--" + b;
1105             return Encoding.ASCII.GetBytes(b.ToCharArray());
1106         }
1107 
1108         //
1109         // Request cookies sometimes are populated from Response
1110         // Here are helper methods to do that.
1111         //
1112 
1113         /*
1114          * Add response cookie to request collection (can override existing)
1115          */
AddResponseCookie(HttpCookie cookie)1116         internal void AddResponseCookie(HttpCookie cookie) {
1117             // cookies collection
1118 
1119             if (_cookies != null)
1120                 _cookies.AddCookie(cookie, true);
1121 
1122             // cookies also go to parameters collection
1123 
1124             if (_params != null) {
1125                 _params.MakeReadWrite();
1126                 _params.Add(cookie.Name, cookie.Value);
1127                 _params.MakeReadOnly();
1128             }
1129         }
1130 
1131         /*
1132          * Clear any cookies response might've added
1133          */
ResetCookies()1134         internal void ResetCookies() {
1135             // cookies collection
1136 
1137             if (_cookies != null) {
1138                 _cookies.Reset();
1139                 FillInCookiesCollection(_cookies, true /*includeResponse*/);
1140             }
1141 
1142             // cookies also go to parameters collection
1143 
1144             if (_params != null) {
1145                 _params.MakeReadWrite();
1146                 _params.Reset();
1147                 FillInParamsCollection();
1148                 _params.MakeReadOnly();
1149             }
1150         }
1151 
1152         /*
1153          * Http method (verb) associated with the current request
1154          */
1155 
1156         /// <devdoc>
1157         ///    <para>Indicates the HTTP data transfer method used by client (GET, POST). This property is read-only.</para>
1158         /// </devdoc>
1159         public String HttpMethod {
1160             get {
1161                 // Directly from worker request
1162                 if (_httpMethod == null) {
1163                     Debug.Assert(_wr != null);
1164                     _httpMethod = _wr.GetHttpVerbName();
1165                 }
1166 
1167                 return _httpMethod;
1168             }
1169         }
1170 
1171         internal HttpVerb HttpVerb {
1172             get {
1173                 if (_httpVerb == HttpVerb.Unparsed) {
1174                     _httpVerb = HttpVerb.Unknown;
1175                     string method = HttpMethod;
1176                     if (method != null) {
1177                         switch (method.Length) {
1178                             case 3:
1179                                 if (method == "GET") {
1180                                     _httpVerb = HttpVerb.GET;
1181                                 }
1182                                 else if (method == "PUT") {
1183                                     _httpVerb = HttpVerb.PUT;
1184                                 }
1185                                 break;
1186 
1187                             case 4:
1188                                 if (method == "POST") {
1189                                     _httpVerb = HttpVerb.POST;
1190                                 }
1191                                 else if (method == "HEAD") {
1192                                     _httpVerb = HttpVerb.HEAD;
1193                                 }
1194                                 break;
1195 
1196                             case 5:
1197                                 if (method == "DEBUG") {
1198                                     _httpVerb = HttpVerb.DEBUG;
1199                                 }
1200                                 break;
1201 
1202                             case 6:
1203                                 if (method == "DELETE") {
1204                                     _httpVerb = HttpVerb.DELETE;
1205                                 }
1206                                 break;
1207                         }
1208                     }
1209                 }
1210 
1211                 return _httpVerb;
1212             }
1213         }
1214 
1215         // Check whether this is a DEBUG verb request
1216         internal bool IsDebuggingRequest {
1217             get {
1218                 return (HttpVerb == HttpVerb.DEBUG);
1219             }
1220         }
1221 
1222         /*
1223          * RequestType default to verb, but can be changed
1224          */
1225 
1226         /// <devdoc>
1227         ///    Indicates the HTTP data transfer method used by client
1228         ///    (GET, POST).
1229         /// </devdoc>
1230         public String RequestType {
1231             get {
1232                 return(_requestType != null) ? _requestType : this.HttpMethod;
1233             }
1234 
1235             set {
1236                 _requestType = value;
1237             }
1238         }
1239 
1240         /*
1241           * Content-type of the content posted with the current request
1242           */
1243 
1244         /// <devdoc>
1245         ///    <para>Indicates the MIME content type of incoming request. This property is read-only.</para>
1246         /// </devdoc>
1247         public String ContentType {
1248             get {
1249                 if (_contentType == null) {
1250                     if (_wr != null)
1251                         _contentType = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentType);
1252 
1253                     if (_contentType == null)
1254                         _contentType = String.Empty;
1255                 }
1256 
1257                 return _contentType;
1258             }
1259 
1260             set {
1261                 _contentType = value;
1262             }
1263         }
1264 
1265 
1266 
1267         /// <devdoc>
1268         ///    <para>Indicates the content length of incoming request. This property is read-only.</para>
1269         /// </devdoc>
1270         public int ContentLength {
1271             get {
1272                 if (_contentLength == -1) {
1273                     if (_wr != null) {
1274                         String s = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentLength);
1275 
1276                         if (s != null) {
1277                             try {
1278                                 _contentLength = Int32.Parse(s, CultureInfo.InvariantCulture);
1279                             }
1280                             catch {
1281                             }
1282                         }
1283                         else {
1284                             // no content-length header, but there is data
1285                             if (_wr.IsEntireEntityBodyIsPreloaded()) {
1286                                 byte[] preloadedContent = _wr.GetPreloadedEntityBody();
1287 
1288                                 if (preloadedContent != null)
1289                                     _contentLength = preloadedContent.Length;
1290                             }
1291                         }
1292                     }
1293                 }
1294 
1295                 return (_contentLength >= 0) ? _contentLength : 0;
1296             }
1297         }
1298 
1299         /*
1300          * Encoding to read posted text content
1301          */
1302 
1303         /// <devdoc>
1304         ///    <para>Indicates the character set of data supplied by client. This property is read-only.</para>
1305         /// </devdoc>
1306         public Encoding ContentEncoding {
1307             get {
1308                 if(_flags[contentEncodingResolved] && _encoding != null) {
1309                     return _encoding;
1310                 }
1311 
1312                 _encoding = GetEncodingFromHeaders();
1313 
1314                 // DevDiv #351560 - UTF-7 is dangerous and should be forbidden by default.
1315                 // The application developer can choose to allow it if desired.
1316                 if (_encoding is UTF7Encoding && !AppSettings.AllowUtf7RequestContentEncoding) {
1317                     _encoding = null;
1318                 }
1319 
1320                 if (_encoding == null) {
1321                     // WOS 1953542: No Event Is Logged When App Config is Corrupt
1322                     GlobalizationSection globConfig = RuntimeConfig.GetLKGConfig(_context).Globalization;
1323                     _encoding = globConfig.RequestEncoding;
1324                 }
1325 
1326                 _flags.Set(contentEncodingResolved);
1327                 return _encoding;
1328             }
1329 
1330             set {
1331                 _encoding = value;
1332                 _flags.Set(contentEncodingResolved);
1333             }
1334         }
1335 
1336         internal Encoding QueryStringEncoding {
1337             get {
1338                 Encoding e = ContentEncoding;
1339                 // query string is never unicode - use utf-8 if instead
1340                 return e.Equals(Encoding.Unicode) ? Encoding.UTF8 : e;
1341             }
1342         }
1343 
1344         /*
1345          * Parsed Accept header as array of strings
1346          */
1347 
1348         /// <devdoc>
1349         ///    <para>Returns a string array of client-supported MIME accept types. This property is read-only.</para>
1350         /// </devdoc>
1351         public String[] AcceptTypes {
1352             get {
1353                 if (_acceptTypes == null) {
1354                     if (_wr != null)
1355                         _acceptTypes = ParseMultivalueHeader(_wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderAccept));
1356                 }
1357 
1358                 return _acceptTypes;
1359             }
1360         }
1361 
1362         // Is the request authenticated?
1363         public bool IsAuthenticated {
1364             get {
1365                 return(_context.User != null && _context.User.Identity != null && _context.User.Identity.IsAuthenticated);
1366             }
1367         }
1368 
1369         // Is using HTTPS?
1370         //    Indicates whether the HTTP connection is secure (that is, HTTPS). This property is read-only.
1371         public bool IsSecureConnection {
1372             get {
1373                 if (_wr != null)
1374                     return _wr.IsSecure();
1375                 else
1376                     return false;
1377             }
1378         }
1379 
1380 
1381         /*
1382          * Virtual path corresponding to the requested Url
1383          */
1384 
1385         /// <devdoc>
1386         ///    <para>Indicates the virtual path of the current
1387         ///       request, including the path PathInfo. This property is read-only.</para>
1388         /// </devdoc>
1389         public String Path {
1390             get {
1391                 string path = GetUnvalidatedPath();
1392                 if (_flags[needToValidatePath]) {
1393                     _flags.Clear(needToValidatePath);
1394                     ValidateString(path, null, RequestValidationSource.Path);
1395                 }
1396                 return path;
1397             }
1398         }
1399 
1400         internal VirtualPath PathObject {
1401             get {
1402                 if (_path == null) {
1403                     // Directly from worker request
1404 
1405                     Debug.Assert(_wr != null);
1406 
1407                     // Don't allow malformed paths for security reasons
1408                     _path = VirtualPath.Create(_wr.GetUriPath(),
1409                         VirtualPathOptions.AllowAbsolutePath);
1410                 }
1411 
1412                 return _path;
1413             }
1414         }
1415 
1416         // Gets the Path property but does not hook up validation.
GetUnvalidatedPath()1417         internal string GetUnvalidatedPath() {
1418             return PathObject.VirtualPathString;
1419         }
1420 
1421         [DoNotReset]
1422         private string _anonymousId;
1423 
1424         public string AnonymousID {
1425             get { return _anonymousId; }
1426             internal set { _anonymousId = value; }
1427         }
1428 
1429         internal String PathWithQueryString {
1430             get {
1431                 String qs = QueryStringText;
1432                 return (!String.IsNullOrEmpty(qs)) ? (Path + "?" + qs) : Path;
1433             }
1434         }
1435 
1436         // The virtual file path where the client browsers think we are.
1437         // However, in the case of cookieless session, ClientFilePath does *not* include the session id.
1438         private VirtualPath _clientFilePath;
1439         internal VirtualPath ClientFilePath {
1440             get {
1441                 if (_clientFilePath == null) {
1442                     string uri = RawUrl;
1443 
1444                     // remove query string if it exists
1445                     int qsIndex = uri.IndexOf('?');
1446                     if (qsIndex > -1) {
1447                         uri = uri.Substring(0, qsIndex);
1448                     }
1449                     _clientFilePath = VirtualPath.Create(uri, VirtualPathOptions.AllowAbsolutePath);
1450                 }
1451 
1452                 Debug.Trace("ClientUrl", "*** ClientFilePath --> " + _clientFilePath + " ***");
1453                 return _clientFilePath;
1454             }
1455             set {
1456                 _clientFilePath = value;
1457             }
1458         }
1459 
1460         // The base dir of the client virtual file path.
1461         // However, in the case of cookieless session, ClientFilePath does *not* include the session id.
1462         private VirtualPath _clientBaseDir;
1463 
1464         // VSWhidbey 560283 : The ClientBaseDir represents the directory of the current
1465         // request to the client.
1466         //
1467         // the request is   ClientBaseDir   FilePathObject (FilePathObject.Parent)
1468         // 1. /app/sub/     /app/sub/       /app/
1469         // 2. /app/sub      /app/           /app/
1470         internal VirtualPath ClientBaseDir {
1471             get {
1472                 if (_clientBaseDir == null) {
1473                     // client virtual path before the last '/'
1474                     if (ClientFilePath.HasTrailingSlash) {
1475                         _clientBaseDir = ClientFilePath;
1476                     }
1477                     else {
1478                         _clientBaseDir = ClientFilePath.Parent;
1479                     }
1480                 }
1481 
1482                 return _clientBaseDir;
1483             }
1484         }
1485 
1486         /*
1487          * File path corresponding to the requested Url
1488          */
1489 
1490         /// <devdoc>
1491         ///    <para>Indicates the virtual path of the current request, but without the PathInfo.
1492         ///         This property is read-only.</para>
1493         /// </devdoc>
1494         public String FilePath {
1495             get {
1496                 return VirtualPath.GetVirtualPathString(FilePathObject);
1497             }
1498         }
1499 
1500         internal VirtualPath FilePathObject {
1501             get {
1502                 if (_filePath != null) {
1503                     return _filePath;
1504                 }
1505 
1506                 if (!_computePathInfo) {
1507                     // Directly from worker request
1508 
1509                     if (_wr != null) {
1510                         _filePath = _wr.GetFilePathObject();
1511                     }
1512                     else {
1513                         _filePath = PathObject;
1514                     }
1515                 }
1516                 else if (_context != null) {
1517                     // From config
1518                     //
1519                     //          RAID#93378
1520                     //          Config system relies on FilePath for lookups so we should not
1521                     //          be calling it while _filePath is null or it will lead to
1522                     //          infinite recursion.
1523                     //
1524                     //          It is safe to set _filePath to Path as longer path would still
1525                     //          yield correct configuration, just a little slower.
1526 
1527                     _filePath = PathObject;
1528 
1529                     int filePathLen = _context.GetFilePathData().Path.VirtualPathStringNoTrailingSlash.Length;
1530 
1531                     // case could be wrong in config (_path has the correct case)
1532                     string path = Path;
1533                     int pathLength = path.Length;
1534                     // If path is extensionless, _filePath should be equal to path--trailing slash should not be removed.
1535                     if (pathLength != filePathLen
1536                         && (pathLength - filePathLen != 1
1537                             || path[pathLength-1] != '/'
1538                             || path.IndexOf('.') > -1)
1539                         )
1540                         _filePath = VirtualPath.CreateAbsolute(Path.Substring(0, filePathLen));
1541                 }
1542 
1543                 return _filePath;
1544             }
1545         }
1546 
1547         /*
1548          * Normally the same as ClientFilePath.  The difference is that when doing a
1549          * Server.Execute, ClientFilePath doesn't change, while this changes to the
1550          * currently executing virtual path
1551          */
1552 
1553         public string CurrentExecutionFilePath {
1554             get {
1555                 return CurrentExecutionFilePathObject.VirtualPathString;
1556             }
1557         }
1558 
1559         public string CurrentExecutionFilePathExtension {
1560             get {
1561                 return UrlPath.GetExtension(CurrentExecutionFilePathObject.VirtualPathString);
1562             }
1563         }
1564 
1565         internal VirtualPath CurrentExecutionFilePathObject {
1566             get {
1567                 if (_currentExecutionFilePath != null)
1568                     return _currentExecutionFilePath;
1569 
1570                 return FilePathObject;
1571             }
1572         }
1573 
SwitchCurrentExecutionFilePath(VirtualPath path)1574         internal VirtualPath SwitchCurrentExecutionFilePath(VirtualPath path) {
1575             VirtualPath oldPath = _currentExecutionFilePath;
1576             _currentExecutionFilePath = path;
1577             return oldPath;
1578         }
1579 
1580 
1581         // Same as CurrentExecutionFilePath, but made relative to the application root,
1582         // so it is application-agnostic.
1583         public string AppRelativeCurrentExecutionFilePath {
1584             get {
1585                 return UrlPath.MakeVirtualPathAppRelative(CurrentExecutionFilePath);
1586             }
1587         }
1588 
1589         // Path-info corresponding to the requested Url
1590         //    Indicates additional path information for a resource with a URL extension. i.e. for the URL
1591         //       /virdir/page.html/tail, the PathInfo value is /tail. This property is read-only.</para>
1592         public String PathInfo {
1593             get {
1594                 string pathInfo = GetUnvalidatedPathInfo();
1595                 if (_flags[needToValidatePathInfo]) {
1596                     _flags.Clear(needToValidatePathInfo);
1597                     ValidateString(pathInfo, null, RequestValidationSource.PathInfo);
1598                 }
1599                 return pathInfo;
1600             }
1601         }
1602 
1603         internal VirtualPath PathInfoObject {
1604             get {
1605                 if (_pathInfo != null) {
1606                     return _pathInfo;
1607                 }
1608 
1609                 if (!_computePathInfo) {
1610                     // Directly from worker request
1611 
1612                     if (_wr != null) {
1613                         _pathInfo = VirtualPath.CreateAbsoluteAllowNull(_wr.GetPathInfo());
1614                     }
1615                 }
1616 
1617                 if (_pathInfo == null && _context != null) {
1618                     VirtualPath path = PathObject;
1619                     int pathLength = path.VirtualPathString.Length;
1620                     VirtualPath filePath = FilePathObject;
1621                     int filePathLength = filePath.VirtualPathString.Length;
1622 
1623                     if (filePath == null)
1624                         _pathInfo = path;
1625                     else if (path == null || pathLength <= filePathLength )
1626                         _pathInfo = null;
1627                     else {
1628                         string pathInfoString = path.VirtualPathString.Substring(filePathLength, pathLength - filePathLength);
1629                         _pathInfo = VirtualPath.CreateAbsolute(pathInfoString);
1630                     }
1631                 }
1632 
1633                 return _pathInfo;
1634             }
1635         }
1636 
1637         // Gets the PathInfo property but does not hook up validation.
GetUnvalidatedPathInfo()1638         internal string GetUnvalidatedPathInfo() {
1639             VirtualPath pathInfoObject = PathInfoObject;
1640             return (pathInfoObject == null) ? String.Empty : pathInfoObject.VirtualPathString;
1641         }
1642 
1643         /*
1644          * Physical path corresponding to the requested Url
1645          */
1646 
1647         /// <devdoc>
1648         ///    <para>Gets the physical file system path corresponding
1649         ///       to
1650         ///       the requested URL. This property is read-only.</para>
1651         /// </devdoc>
1652         public String PhysicalPath {
1653             get {
1654                 String path = PhysicalPathInternal;
1655                 InternalSecurityPermissions.PathDiscovery(path).Demand();
1656                 return path;
1657             }
1658         }
1659 
1660         internal String PhysicalPathInternal {
1661             get {
1662                 if (_pathTranslated == null) {
1663                     if (!_computePathInfo) {
1664                         // Directly from worker request
1665                         Debug.Assert(_wr != null);
1666                         _pathTranslated = _wr.GetFilePathTranslated();
1667                         if (HttpRuntime.IsMapPathRelaxed)
1668                             _pathTranslated = HttpRuntime.GetRelaxedMapPathResult(_pathTranslated);
1669                     }
1670 
1671                     if (_pathTranslated == null && _wr != null) {
1672                         // Compute after rewrite
1673                         _pathTranslated = HostingEnvironment.MapPathInternal(FilePath);
1674                     }
1675                 }
1676 
1677                 return _pathTranslated;
1678             }
1679         }
1680 
1681         /*
1682          * Virtual path to the application root
1683          */
1684 
1685         /// <devdoc>
1686         ///    <para>Gets the
1687         ///       virtual path to the currently executing server application.</para>
1688         /// </devdoc>
1689         public String ApplicationPath {
1690             get {
1691                 return HttpRuntime.AppDomainAppVirtualPath;
1692             }
1693         }
1694 
1695         internal VirtualPath ApplicationPathObject {
1696             get {
1697                 return HttpRuntime.AppDomainAppVirtualPathObject;
1698             }
1699         }
1700 
1701         /*
1702          * Physical path to the application root
1703          */
1704 
1705         /// <devdoc>
1706         ///    <para>Gets the physical
1707         ///       file system path of currently executing server application.</para>
1708         /// </devdoc>
1709         public String PhysicalApplicationPath {
1710             get {
1711                 InternalSecurityPermissions.AppPathDiscovery.Demand();
1712 
1713                 if (_wr != null)
1714                     return _wr.GetAppPathTranslated();
1715                 else
1716                     return null;
1717             }
1718         }
1719 
1720         /*
1721          * User agent string
1722          */
1723 
1724         /// <devdoc>
1725         ///    <para>Gets the client
1726         ///       browser's raw User Agent String.</para>
1727         /// </devdoc>
1728         public String UserAgent {
1729             get {
1730                 if (_wr != null)
1731                     return _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderUserAgent);
1732                 else
1733                     return null;
1734             }
1735         }
1736 
1737         /*
1738          * Accepted user languages
1739          */
1740 
1741         /// <devdoc>
1742         ///    <para>Gets a
1743         ///       sorted array of client language preferences.</para>
1744         /// </devdoc>
1745         public String[] UserLanguages {
1746             get {
1747                 if (_userLanguages == null) {
1748                     if (_wr != null)
1749                         _userLanguages = ParseMultivalueHeader(_wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderAcceptLanguage));
1750                 }
1751 
1752                 return _userLanguages;
1753             }
1754         }
1755 
1756         // Browser caps
1757         //    Provides information about incoming client's browser capabilities.
1758         public HttpBrowserCapabilities Browser {
1759             get {
1760                 if(_browsercaps != null) {
1761                     return _browsercaps;
1762                 }
1763 
1764                 if (! s_browserCapsEvaled) {
1765                     lock (s_browserLock) {
1766                         if (! s_browserCapsEvaled) {
1767                             HttpCapabilitiesBase.GetBrowserCapabilities(this);
1768                         }
1769                         s_browserCapsEvaled = true;
1770                     }
1771                 }
1772 
1773                 _browsercaps = (HttpBrowserCapabilities)HttpCapabilitiesBase.GetBrowserCapabilities(this);
1774                 return _browsercaps;
1775             }
1776 
1777             set {
1778                 _browsercaps = value;
1779             }
1780         }
1781 
1782         /*
1783          * Client's host name
1784          */
1785 
1786         /// <devdoc>
1787         ///    <para>Gets the
1788         ///       DNS name of remote client.</para>
1789         /// </devdoc>
1790         public String UserHostName {
1791             get {
1792                 String s = (_wr != null) ? _wr.GetRemoteName() : null;
1793                 if (String.IsNullOrEmpty(s))
1794                     s = UserHostAddress;
1795                 return s;
1796             }
1797         }
1798 
1799         /*
1800          * Client's host address
1801          */
1802 
1803         /// <devdoc>
1804         ///    <para>Gets the
1805         ///       IP host address of remote client.</para>
1806         /// </devdoc>
1807         public String UserHostAddress {
1808             get {
1809                 if (_wr != null)
1810                     return _wr.GetRemoteAddress();
1811                 else
1812                     return null;
1813             }
1814         }
1815 
1816         /*
1817          * The current request's RAW Url (as supplied by worker request)
1818          */
1819 
1820         /// <devdoc>
1821         ///    <para>Gets the URI requsted by the client, which may include PathInfo and QueryString if it exists.
1822         ///    This value is unaffected by any URL rewriting or routing that may occur on the server.</para>
1823         /// </devdoc>
1824         public String RawUrl {
1825             get {
1826                 EnsureRawUrl();
1827 
1828                 if (_flags[needToValidateRawUrl]) {
1829                     _flags.Clear(needToValidateRawUrl);
1830                     ValidateString(_rawUrl, null, RequestValidationSource.RawUrl);
1831                 }
1832                 return _rawUrl;
1833             }
1834             internal set {
1835                 _rawUrl = value;
1836             }
1837         }
1838 
1839         // Populates the RawUrl property but does not hook up validation.
EnsureRawUrl()1840         internal string EnsureRawUrl() {
1841             if (_rawUrl == null) {
1842                 String url;
1843 
1844                 if (_wr != null) {
1845                     url = _wr.GetRawUrl();
1846                 }
1847                 else {
1848                     String p = this.GetUnvalidatedPath();
1849                     String qs = this.QueryStringText;
1850 
1851                     if (!String.IsNullOrEmpty(qs))
1852                         url = p + "?" + qs;
1853                     else
1854                         url = p;
1855                 }
1856                 _rawUrl = url;
1857             }
1858 
1859             return _rawUrl;
1860         }
1861 
1862         // WOS 1953542: No Event Is Logged When App Config is Corrupt
1863         // This should never throw.  Since Uri.ctor can throw if config
1864         // is bad, we cannot use the public Url property.
1865         //
1866         // Security note: This property has not been sanitized and should not
1867         // be used for making decisions at runtime. It should only be used for
1868         // logging or other innocuous things.
1869         internal String UrlInternal {
1870             get {
1871                 string q = QueryStringText;
1872                 if (!String.IsNullOrEmpty(q))
1873                     q = "?" + HttpEncoder.CollapsePercentUFromStringInternal(q, QueryStringEncoding);
1874 
1875                 // Get server name and port from Host header?  Or the old way?
1876                 if (AppSettings.UseHostHeaderForRequestUrl) {
1877                     string serverAndPort = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderHost);
1878                     try {
1879                         if (!String.IsNullOrEmpty(serverAndPort)) {
1880                             // RFC 2732 (Section 2 for format, section 3 for update of RFC 2396 [HTTP 1.1]) mandates that
1881                             // IPv6 addresses in the host header be enclosed in []'s already.  Se we don't need to do the
1882                             // same check for a ---- IPv6 address as we do with _wr.GetServerName() below.
1883                             string u = _wr.GetProtocol() + "://" + serverAndPort + Path + q;
1884                             _url = new Uri(u);
1885                             return u;
1886                         }
1887                     } catch (UriFormatException) { /* Do nothing, leave _url null.  Backup plan will kick in below. */ }
1888                 }
1889 
1890                 // If for some reason the Host Header failed to produce a valid Url, fall back on the old way of doing it.
1891                 String serverName = _wr.GetServerName();
1892                 if (serverName.IndexOf(':') >= 0 && serverName[0] != '[')
1893                     serverName = "[" + serverName + "]"; // IPv6
1894                 if (_wr.GetLocalPortAsString() == "80") {
1895                     return _wr.GetProtocol() + "://" + serverName + Path + q;
1896                 }
1897                 else {
1898                     return _wr.GetProtocol() + "://" + serverName + ":" + _wr.GetLocalPortAsString() + Path + q;
1899                 }
1900             }
1901         }
1902 
1903         // The current request's Url
1904         //    Gets Information regarding URL of current request.
1905         public Uri Url {
1906             get {
1907                 if (_url == null && _wr != null) {
1908                     // The Path is accessed in a deferred way to preserve the execution order that existed
1909                     // before the code in BuildUrl was factored out of this property.
1910                     // While evaluating the Path immediately would probably not have an impact on regular execution
1911                     // it might impact error cases. Consider a situation in which some method in workerRequest throws.
1912                     // If we evaluate Path early, then some other method might throw, thus producing a different
1913                     // error behavior for the same conditions. Passing in a Func preserves the old ordering.
1914                     _url = BuildUrl(() => Path);
1915                 }
1916 
1917                 return _url;
1918             }
1919         }
1920 
BuildUrl(Func<string> pathAccessor)1921         internal Uri BuildUrl(Func<string> pathAccessor) {
1922             Uri url = null;
1923             string q = QueryStringText;
1924             if (!String.IsNullOrEmpty(q))
1925                 q = "?" + HttpEncoder.CollapsePercentUFromStringInternal(q, QueryStringEncoding);
1926 
1927             // Get server name and port from Host header?  Or the old way?
1928             if (AppSettings.UseHostHeaderForRequestUrl) {
1929                 string serverAndPort = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderHost);
1930                 try {
1931                     if (!String.IsNullOrEmpty(serverAndPort)) {
1932                         // RFC 2732 (Section 2 for format, section 3 for update of RFC 2396 [HTTP 1.1]) mandates that
1933                         // IPv6 addresses in the host header be enclosed in []'s already.  Se we don't need to do the
1934                         // same check for a ---- IPv6 address as we do with _wr.GetServerName() below.
1935                         url = UriUtil.BuildUri(_wr.GetProtocol(), Uri.UnescapeDataString(serverAndPort), null /* port */, pathAccessor(), q);
1936                     }
1937                 }
1938                 catch (UriFormatException) { /* Do nothing, leave _url null.  Backup plan will kick in below. */ }
1939             }
1940 
1941             // If for some reason the Host Header failed to produce a valid Url, fall back on the old way of doing it.
1942             if (url == null) {
1943                 String serverName = _wr.GetServerName();
1944                 if (serverName.IndexOf(':') >= 0 && serverName[0] != '[')
1945                     serverName = "[" + serverName + "]"; // IPv6
1946 
1947                 url = UriUtil.BuildUri(_wr.GetProtocol(), Uri.UnescapeDataString(serverName), _wr.GetLocalPortAsString(), pathAccessor(), q);
1948             }
1949             return url;
1950         }
1951 
1952         // Url of the Http referrer
1953         /// Gets information regarding the URL of the client's previous request that linked to the current URL.
1954         public Uri UrlReferrer {
1955             get {
1956                 if (_referrer == null) {
1957                     if (_wr != null) {
1958                         String r = _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderReferer);
1959 
1960                         if (!String.IsNullOrEmpty(r)) {
1961                             try {
1962                                 if (r.IndexOf("://", StringComparison.Ordinal) >= 0)
1963                                     _referrer = new Uri(r);
1964                                  else
1965                                     _referrer = new Uri(this.Url, r);
1966                             }
1967                             catch (HttpException) {
1968                                 // malformed referrer shouldn't crash the request
1969                                 _referrer = null;
1970                             }
1971                         }
1972                     }
1973                 }
1974 
1975                 return _referrer;
1976             }
1977         }
1978 
1979         // special case for perf in output cache module
1980         internal String IfModifiedSince {
1981             get {
1982                 if (_wr == null)
1983                     return null;
1984                 return _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderIfModifiedSince);
1985             }
1986         }
1987 
1988         // special case for perf in output cache module
1989         internal String IfNoneMatch {
1990             get {
1991                 if (_wr == null)
1992                     return null;
1993                 return _wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderIfNoneMatch);
1994             }
1995         }
1996 
1997         // Params collection - combination of query string, form, server vars
1998         //    Gets a combined collection of QueryString+Form+ ServerVariable+Cookies.
1999         public NameValueCollection Params {
2000             get {
2001                 if (HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Low))
2002                     return GetParams();
2003                 else
2004                     return GetParamsWithDemand();
2005             }
2006         }
2007 
2008         // Used in integrated pipeline mode to invalidate the params collection
2009         // after a change is made to the headers or server variables
InvalidateParams()2010         internal void InvalidateParams() {
2011             _params = null;
2012         }
2013 
GetParams()2014         private NameValueCollection GetParams() {
2015             if (_params == null) {
2016                 _params = new HttpValueCollection(64);
2017                 FillInParamsCollection();
2018                 _params.MakeReadOnly();
2019             }
2020             return _params;
2021         }
2022 
2023         [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Low)]
GetParamsWithDemand()2024         private NameValueCollection GetParamsWithDemand()
2025         {
2026             return GetParams();
2027         }
2028 
2029 
2030         // Default property that goes through the collections
2031         //      QueryString, Form, Cookies, ClientCertificate and ServerVariables
2032         public String this[String key] {
2033             get {
2034                 String s;
2035 
2036                 s = QueryString[key];
2037                 if (s != null)
2038                     return s;
2039 
2040                 s = Form[key];
2041                 if (s != null)
2042                     return s;
2043 
2044                 HttpCookie c = Cookies[key];
2045                 if (c != null)
2046                     return c.Value;
2047 
2048                 s = ServerVariables[key];
2049                 if (s != null)
2050                     return s;
2051 
2052                 return null;
2053             }
2054         }
2055 
2056         // Query string as String (private)
2057         internal String QueryStringText {
2058             get {
2059                 if (_queryStringText == null) {
2060                     if (_wr != null) {
2061                         // if raw bytes available use them
2062                         byte[] rawQueryString = this.QueryStringBytes;
2063 
2064                         if (rawQueryString != null) {
2065                             if (rawQueryString.Length > 0)
2066                                 _queryStringText = QueryStringEncoding.GetString(rawQueryString);
2067                             else
2068                                 _queryStringText = String.Empty;
2069                         }
2070                         else {
2071                             _queryStringText = _wr.GetQueryString();
2072                         }
2073                     }
2074 
2075                     if (_queryStringText == null)
2076                         _queryStringText = String.Empty;
2077 
2078                     if (_queryStringText.Length > 0 && !AppSettings.UseLegacyRequestUrlGeneration)
2079                         _queryStringText = _queryStringText.Replace("#", "%23"); // we don't want consumers to perform string concatenation and end up with an unintended fragment
2080                 }
2081 
2082                 return _queryStringText;
2083             }
2084 
2085             set {
2086                 // override the query string
2087                 _queryStringText = value;
2088                 _queryStringOverriden = true;
2089 
2090                 if (_queryString != null) {
2091                     _params=null;
2092                     _queryString.MakeReadWrite();
2093                     _queryString.Reset();
2094                     FillInQueryStringCollection();
2095                     _queryString.MakeReadOnly();
2096                     Unvalidated.InvalidateQueryString();
2097                 }
2098             }
2099         }
2100 
2101         // Query string as byte[] (private) -- for parsing
2102         internal byte[] QueryStringBytes {
2103             get {
2104                 if (_queryStringOverriden)
2105                     return null;
2106 
2107                 if (_queryStringBytes == null) {
2108                     if (_wr != null)
2109                         _queryStringBytes = _wr.GetQueryStringRawBytes();
2110                 }
2111 
2112                 return _queryStringBytes;
2113             }
2114         }
2115 
2116         // Query string collection
2117         //    <para>Gets the collection of QueryString variables.</para>
2118         //
2119         public NameValueCollection QueryString {
2120             get {
2121                 EnsureQueryString();
2122 
2123                 if (_flags[needToValidateQueryString]) {
2124                     _flags.Clear(needToValidateQueryString);
2125                     ValidateHttpValueCollection(_queryString, RequestValidationSource.QueryString);
2126                 }
2127 
2128                 return _queryString;
2129             }
2130         }
2131 
2132         // Populates the QueryString property but does not hook up validation.
EnsureQueryString()2133         internal HttpValueCollection EnsureQueryString() {
2134             if (_queryString == null) {
2135                 _queryString = new HttpValueCollection();
2136 
2137                 if (_wr != null)
2138                     FillInQueryStringCollection();
2139 
2140                 _queryString.MakeReadOnly();
2141             }
2142 
2143             return _queryString;
2144         }
2145 
2146         internal bool HasQueryString {
2147             get {
2148                 if (_queryString != null)
2149                     return (_queryString.Count > 0);
2150 
2151                 byte[] rawQueryString = this.QueryStringBytes;
2152 
2153                 if (rawQueryString != null) {
2154                     return (rawQueryString.Length > 0);
2155                 }
2156                 else {
2157                     return (QueryStringText.Length > 0);
2158                 }
2159             }
2160         }
2161 
2162         // Form collection
2163         ///    Gets a collection of Form variables.
2164         public NameValueCollection Form {
2165             get {
2166                 EnsureForm();
2167 
2168                 if (_flags[needToValidateForm]) {
2169                     _flags.Clear(needToValidateForm);
2170                     ValidateHttpValueCollection(_form, RequestValidationSource.Form);
2171                 }
2172 
2173                 return _form;
2174             }
2175         }
2176 
2177         // Populates the Form property but does not hook up validation.
EnsureForm()2178         internal HttpValueCollection EnsureForm() {
2179             if (_form == null) {
2180                 _form = new HttpValueCollection();
2181 
2182                 if (_wr != null)
2183                     FillInFormCollection();
2184 
2185                 _form.MakeReadOnly();
2186             }
2187 
2188             return _form;
2189         }
2190 
2191         internal bool HasForm {
2192             get {
2193                 if (_form != null) {
2194                     return (_form.Count > 0);
2195                 }
2196                 else {
2197                     if (_wr != null && !_wr.HasEntityBody()) {
2198                         return false;
2199                     }
2200                     else {
2201                         return (Form.Count > 0);
2202                     }
2203                 }
2204             }
2205         }
2206 
2207 
SwitchForm(HttpValueCollection form)2208         internal HttpValueCollection SwitchForm(HttpValueCollection form) {
2209             HttpValueCollection oldForm = _form;
2210             _form = form;
2211             Unvalidated.InvalidateForm();
2212             return oldForm;
2213         }
2214 
2215         // Headers collection
2216         //    Gets a collection of HTTP headers.
2217         public NameValueCollection Headers {
2218             get {
2219                 EnsureHeaders();
2220 
2221                 if (_flags[needToValidateHeaders]) {
2222                     _flags.Clear(needToValidateHeaders);
2223                     ValidateHttpValueCollection(_headers, RequestValidationSource.Headers);
2224                 }
2225                 if (_flags[needToValidateCookielessHeader]) {
2226                     _flags.Clear(needToValidateCookielessHeader);
2227                     ValidateCookielessHeaderIfRequiredByConfig(_headers[CookielessHelperClass.COOKIELESS_SESSION_FILTER_HEADER]);
2228                 }
2229                 return _headers;
2230             }
2231         }
2232 
2233         // Populates the Headers property but does not hook up validation.
EnsureHeaders()2234         internal HttpHeaderCollection EnsureHeaders() {
2235             if (_headers == null) {
2236                 _headers = new HttpHeaderCollection(_wr, this, 8);
2237 
2238                 if (_wr != null)
2239                     FillInHeadersCollection();
2240 
2241                 if (!(_wr is IIS7WorkerRequest)) {
2242                     _headers.MakeReadOnly();
2243                 }
2244             }
2245 
2246             return _headers;
2247         }
2248 
2249         // Allows access to request collections that have not gone through request validation
2250         public UnvalidatedRequestValues Unvalidated {
2251             get {
2252                 if (_unvalidatedRequestValues == null) {
2253                     _unvalidatedRequestValues = new UnvalidatedRequestValues(this);
2254                 }
2255                 return _unvalidatedRequestValues;
2256             }
2257         }
2258 
2259         // Server vars collection
2260         // Gets a collection of web server variables.
2261         public NameValueCollection ServerVariables {
2262             get {
2263                 if (HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Low))
2264                     return GetServerVars();
2265                 else
2266                     return GetServerVarsWithDemand();
2267             }
2268         }
2269 
GetServerVarsWithoutDemand()2270         internal NameValueCollection GetServerVarsWithoutDemand() {
2271             return GetServerVars();
2272         }
2273 
2274         [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Low)]
GetServerVarsWithDemand()2275         private NameValueCollection GetServerVarsWithDemand()
2276         {
2277             return GetServerVars();
2278         }
2279 
GetServerVars()2280         private NameValueCollection GetServerVars()
2281         {
2282             if (_serverVariables == null) {
2283                 _serverVariables = new HttpServerVarsCollection(_wr, this);
2284 
2285                 if ( !(_wr is IIS7WorkerRequest) ) {
2286                     _serverVariables.MakeReadOnly();
2287                 }
2288             }
2289             return _serverVariables;
2290         }
2291 
SetSkipAuthorization(bool value)2292         internal void SetSkipAuthorization(bool value) {
2293             IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
2294             if (wr == null) {
2295                 return;
2296             }
2297 
2298             // If value is true, set server variable to "1".
2299             // If value is false, remove server variable by setting value to null.
2300             // Don't create server variable collection if it's not created yet.
2301 
2302             if (_serverVariables == null) {
2303                 wr.SetServerVariable("IS_LOGIN_PAGE", value ? "1" : null);
2304             }
2305             else {
2306                 _serverVariables.SetNoDemand("IS_LOGIN_PAGE", value ? "1" : null);
2307             }
2308         }
2309 
SetDynamicCompression(bool enable)2310         internal void SetDynamicCompression(bool enable) {
2311             IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
2312             if (wr == null) {
2313                 return;
2314             }
2315 
2316             // If "enable" is true, remove server variable by setting value to null.
2317             // If "enable" is false, set server variable to "0" to disable.
2318             // Don't create server variable collection if it's not created yet.
2319 
2320             if (_serverVariables == null) {
2321                 wr.SetServerVariable("IIS_EnableDynamicCompression", enable ? null : "0");
2322             }
2323             else {
2324                 _serverVariables.SetNoDemand("IIS_EnableDynamicCompression", enable ? null : "0");
2325             }
2326         }
2327 
2328         // WOS 1526602: ASP.Net v2.0: Response.AppendToLog does not work properly in integrated mode
AppendToLogQueryString(string logData)2329         internal void AppendToLogQueryString(string logData) {
2330             IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
2331             if (wr == null ||  String.IsNullOrEmpty(logData)) {
2332                 return;
2333             }
2334 
2335             // Don't create server variable collection if it's not created yet.
2336             if (_serverVariables == null) {
2337                 string currentLogData = wr.GetServerVariable("LOG_QUERY_STRING");
2338                 if (String.IsNullOrEmpty(currentLogData)) {
2339                     wr.SetServerVariable("LOG_QUERY_STRING", QueryStringText + logData);
2340                 }
2341                 else {
2342                     wr.SetServerVariable("LOG_QUERY_STRING", currentLogData + logData);
2343                 }
2344             }
2345             else {
2346                 string currentLogData = _serverVariables.Get("LOG_QUERY_STRING");
2347                 if (String.IsNullOrEmpty(currentLogData)) {
2348                     _serverVariables.SetNoDemand("LOG_QUERY_STRING", QueryStringText + logData);
2349                 }
2350                 else {
2351                     _serverVariables.SetNoDemand("LOG_QUERY_STRING", currentLogData + logData);
2352                 }
2353             }
2354         }
2355 
2356         // Cookie collection associated with current request
2357         //    Gets a collection of client's cookie variables.
2358         public HttpCookieCollection Cookies {
2359             get {
2360                 EnsureCookies();
2361 
2362                 if (_flags[needToValidateCookies]) {
2363                     _flags.Clear(needToValidateCookies);
2364                     ValidateCookieCollection(_cookies);
2365                 }
2366 
2367                 return _cookies;
2368             }
2369         }
2370 
2371         // Populates the Cookies property but does not hook up validation.
EnsureCookies()2372         internal HttpCookieCollection EnsureCookies() {
2373             if (_cookies == null) {
2374                 _cookies = new HttpCookieCollection(null, false);
2375 
2376                 if (_wr != null)
2377                     FillInCookiesCollection(_cookies, true /*includeResponse*/);
2378 
2379                 if (HasTransitionedToWebSocketRequest) // cookies can't be modified after the WebSocket handshake is complete
2380                     _cookies.MakeReadOnly();
2381             }
2382 
2383             return _cookies;
2384         }
2385 
2386         // File collection associated with current request
2387         // Gets the collection of client-uploaded files (Multipart MIME format).
2388         public HttpFileCollection Files {
2389             get {
2390                 EnsureFiles();
2391 
2392                 if (_flags[needToValidatePostedFiles]) {
2393                     _flags.Clear(needToValidatePostedFiles);
2394                     ValidatePostedFileCollection(_files);
2395                 }
2396 
2397                 return _files;
2398             }
2399         }
2400 
2401         // Populates the Files property but does not hook up validation.
EnsureFiles()2402         internal HttpFileCollection EnsureFiles() {
2403             if (_files == null) {
2404                 if (_readEntityBodyMode == ReadEntityBodyMode.Bufferless)
2405                     throw new HttpException(SR.GetString(SR.Incompatible_with_get_bufferless_input_stream));
2406 
2407                 _files = new HttpFileCollection();
2408 
2409                 if (_wr != null)
2410                     FillInFilesCollection();
2411             }
2412 
2413             return _files;
2414         }
2415 
2416         // Stream to read raw content
2417         //   Provides access to the raw contents of the incoming HTTP entity body.
2418         public Stream InputStream {
2419             get {
2420                 if (_inputStream == null) {
2421                     if (_readEntityBodyMode == ReadEntityBodyMode.Bufferless)
2422                         throw new HttpException(SR.GetString(SR.Incompatible_with_get_bufferless_input_stream));
2423 
2424                     HttpRawUploadedContent rawContent = null;
2425 
2426                     if (_wr != null)
2427                         rawContent = GetEntireRawContent();
2428 
2429                     if (rawContent != null) {
2430                         _inputStream = new HttpInputStream(
2431                                                           rawContent,
2432                                                           0,
2433                                                           rawContent.Length
2434                                                           );
2435                     }
2436                     else {
2437                         _inputStream = new HttpInputStream(null, 0, 0);
2438                     }
2439                 }
2440 
2441                 return _inputStream;
2442             }
2443         }
2444 
2445         // ASP classic compat
2446         //       Gets the number of bytes in the current input stream.
2447         public int TotalBytes {
2448             get {
2449                 Stream s = (_readEntityBodyStream != null) ? _readEntityBodyStream : InputStream;
2450                 return(s != null) ? (int)s.Length : 0;
2451             }
2452         }
2453 
2454         // ASP classic compat
2455         //  Performs a binary read of a specified number of bytes from the current input stream.
BinaryRead(int count)2456         public byte[] BinaryRead(int count) {
2457             if (_readEntityBodyMode == ReadEntityBodyMode.Bufferless)
2458                 throw new HttpException(SR.GetString(SR.Incompatible_with_get_bufferless_input_stream));
2459 
2460             if (count < 0 || count > TotalBytes)
2461                 throw new ArgumentOutOfRangeException("count");
2462 
2463             if (count == 0)
2464                 return new byte[0];
2465 
2466             byte[] buffer = new byte[count];
2467             int c = InputStream.Read(buffer, 0, count);
2468 
2469             if (c != count) {
2470                 byte[] b2 = new byte[c];
2471                 if (c > 0)
2472                     Array.Copy(buffer, b2, c);
2473                 buffer = b2;
2474             }
2475 
2476             return buffer;
2477         }
2478 
2479         // Filtering of the input
2480         //   Gets or sets a filter to use when reading the current input stream.
2481         public Stream Filter {
2482             get {
2483                 if (_installedFilter != null)
2484                     return _installedFilter;
2485 
2486                 if (_filterSource == null)
2487                     _filterSource = new HttpInputStreamFilterSource();
2488 
2489                 return _filterSource;
2490             }
2491 
2492             set {
2493                 if (_filterSource == null)  // have to use the source -- null means source wasn't ever asked for
2494                     throw new HttpException(SR.GetString(SR.Invalid_request_filter));
2495 
2496                 _installedFilter = value;
2497             }
2498         }
2499 
2500 
2501         // Client Certificate
2502         //    Gets information on the current request's client security certificate.
2503         public HttpClientCertificate ClientCertificate {
2504             [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Low)]
2505             get {
2506                 if (_clientCertificate == null) {
2507                     _clientCertificate = CreateHttpClientCertificateWithAssert();
2508                 }
2509 
2510                 return _clientCertificate;
2511             }
2512         }
2513 
2514         [PermissionSet(SecurityAction.Assert, Unrestricted=true)]
CreateHttpClientCertificateWithAssert()2515         HttpClientCertificate CreateHttpClientCertificateWithAssert() {
2516             return new HttpClientCertificate(_context);
2517         }
2518 
2519 
2520         //    Gets LOGON_USER as WindowsIdentity
2521         public WindowsIdentity LogonUserIdentity {
2522             [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Medium)]
2523             get {
2524                 if (_logonUserIdentity == null) {
2525                     if (_wr != null) {
2526                         if (_wr is IIS7WorkerRequest && _context.NotificationContext != null &&
2527                             ((_context.NotificationContext.CurrentNotification == RequestNotification.AuthenticateRequest && !_context.NotificationContext.IsPostNotification)
2528                              || (_context.NotificationContext.CurrentNotification < RequestNotification.AuthenticateRequest))) {
2529                             throw new InvalidOperationException(SR.GetString(SR.Invalid_before_authentication));
2530                         }
2531 
2532                         _logonUserIdentity = _wr.GetLogonUserIdentity();
2533                     }
2534                 }
2535 
2536                 return _logonUserIdentity;
2537             }
2538         }
2539 
2540         private bool GranularValidationEnabled {
2541             get { return _flags[granularValidationEnabled]; }
2542         }
2543 
2544         private bool RequestValidationSuppressed {
2545             get { return _flags[requestValidationSuppressed]; }
2546         }
2547 
2548         //  Validate that the input from the browser is safe.
ValidateInput()2549         public void ValidateInput() {
2550             // It doesn't make sense to call this multiple times per request.
2551             // Additionally, if validation was suppressed, no-op now.
2552             if (ValidateInputWasCalled || RequestValidationSuppressed) {
2553                 return;
2554             }
2555 
2556             _flags.Set(hasValidateInputBeenCalled);
2557 
2558             // This is to prevent some XSS (cross site scripting) attacks (ASURT 122278)
2559             _flags.Set(needToValidateQueryString);
2560             _flags.Set(needToValidateForm);
2561             _flags.Set(needToValidateCookies);
2562             _flags.Set(needToValidatePostedFiles);
2563             _flags.Set(needToValidateRawUrl);
2564             _flags.Set(needToValidatePath);
2565             _flags.Set(needToValidatePathInfo);
2566             _flags.Set(needToValidateHeaders);
2567         }
2568 
2569         internal bool ValidateInputWasCalled {
2570             get {
2571                 return _flags[hasValidateInputBeenCalled];
2572             }
2573         }
2574 
2575         // There are a few situations where we do not want to validate the request ever:
CanValidateRequest()2576         private bool CanValidateRequest() {
2577             if (_wr == null) {
2578                 return false;
2579             }
2580 
2581             // 1. State server requests
2582             if (_wr is StateHttpWorkerRequest) {
2583                 return false;
2584             }
2585 
2586             // 2. DevDiv2 162442: When IIS has already rejected the request and we are inside logrequest or end request
2587             if (_wr is IIS7WorkerRequest &&
2588                 (_context.Response.StatusCode == 404 || _context.Response.StatusCode == 400) &&
2589                 (_context.NotificationContext != null) &&
2590                 (_context.NotificationContext.CurrentNotification == RequestNotification.LogRequest ||
2591                 _context.NotificationContext.CurrentNotification == RequestNotification.EndRequest)) {
2592                 return false;
2593             }
2594 
2595             return true;
2596         }
2597 
ValidateInputIfRequiredByConfig()2598         internal void ValidateInputIfRequiredByConfig() {
2599             // Do we need to enable request validation?
2600             RuntimeConfig config = RuntimeConfig.GetConfig(Context);
2601             HttpRuntimeSection runtimeSection = config.HttpRuntime;
2602 
2603             //////////////////////////////////////////////////////////////////////
2604             // Perform Path & QueryString validation checks for non-state_server requests
2605             if (CanValidateRequest()) {
2606                 string requestUrl = Path;
2607 
2608                 //////////////////////////////////////////////////////////////////
2609                 // Verify the URL & QS lengths
2610                 if (requestUrl.Length > runtimeSection.MaxUrlLength) {
2611                     throw new HttpException(400, SR.GetString(SR.Url_too_long));
2612                 }
2613                 if (QueryStringText.Length > runtimeSection.MaxQueryStringLength) {
2614                     throw new HttpException(400, SR.GetString(SR.QueryString_too_long));
2615                 }
2616 
2617                 //////////////////////////////////////////////////////////////////
2618                 // Verify that the URL does not contain invalid chars
2619                 char [] invalidChars = runtimeSection.RequestPathInvalidCharactersArray;
2620                 if (invalidChars != null && invalidChars.Length > 0) {
2621                     int index = requestUrl.IndexOfAny(invalidChars);
2622                     if (index >= 0) {
2623                         string invalidString = new string(requestUrl[index], 1);
2624                         throw new HttpException(400, SR.GetString(SR.Dangerous_input_detected,
2625                                                                   "Request.Path", invalidString));
2626                     }
2627                     _flags.Set(needToValidateCookielessHeader);
2628                 }
2629             }
2630 
2631             // only enable request validation for the entire pipeline in v4.0+ of the framework
2632             Version requestValidationMode = runtimeSection.RequestValidationMode;
2633             if (requestValidationMode == VersionUtil.Framework00) {
2634                 // DevDiv #412689: <httpRuntime requestValidationMode="0.0" /> should suppress validation for
2635                 // the entire request, even if a call to ValidateInput() takes place. The request path
2636                 // characters and cookieless header (see 'needToValidateCookielessHeader') are still validated
2637                 // if necessary. These can be suppressed via <httpRuntime requestPathInvalidChars="" />.
2638                 _flags[requestValidationSuppressed] = true;
2639             }
2640             else if (requestValidationMode >= VersionUtil.Framework40) {
2641                 ValidateInput();
2642 
2643                 // Mode v4.5+ implies granular request validation
2644                 if (requestValidationMode >= VersionUtil.Framework45) {
2645                     EnableGranularRequestValidation();
2646                 }
2647             }
2648         }
2649 
ValidateCookielessHeaderIfRequiredByConfig(string header)2650         internal void ValidateCookielessHeaderIfRequiredByConfig(string header) {
2651             if (string.IsNullOrEmpty(header))
2652                 return;
2653             if (!CanValidateRequest())
2654                 return;
2655             // Verify that the header does not contain invalid chars
2656             char [] invalidChars = RuntimeConfig.GetConfig(Context).HttpRuntime.RequestPathInvalidCharactersArray;
2657             if (invalidChars != null && invalidChars.Length > 0) {
2658                 int index = header.IndexOfAny(invalidChars);
2659                 if (index >= 0) {
2660                     string invalidString = new string(header[index], 1);
2661                     throw new HttpException(400, SR.GetString(SR.Dangerous_input_detected, "Request.Path", invalidString));
2662                 }
2663             }
2664         }
2665 
RemoveNullCharacters(string s)2666         private static string RemoveNullCharacters(string s) {
2667             if (s == null)
2668                 return null;
2669 
2670             // Ignore null characters to prevent attacks (VSWhidbey 85016)
2671             if (s.IndexOf('\0') > -1)
2672                 return s.Replace("\0", String.Empty);
2673 
2674             return s;
2675         }
2676 
ValidateString(string value, string collectionKey, RequestValidationSource requestCollection)2677         private void ValidateString(string value, string collectionKey, RequestValidationSource requestCollection) {
2678 
2679             value = RemoveNullCharacters(value);
2680 
2681             // Only provide the HttpContext if this is an actual HttpRequest; pass null
2682             // if this is simply a shell that exists for WebSockets.
2683             HttpContext contextToProvide = (HasTransitionedToWebSocketRequest) ? null : Context;
2684 
2685             int validationFailureIndex;
2686             if (!RequestValidator.Current.IsValidRequestString(contextToProvide, value, requestCollection, collectionKey, out validationFailureIndex)) {
2687                 // Display only the piece of the string that caused the problem, padded by on each side
2688                 string detectedString = collectionKey + "=\"";
2689                 int startIndex = validationFailureIndex - 10;
2690                 if (startIndex <= 0) {
2691                     startIndex = 0;
2692                 }
2693                 else {
2694                     // Start with "..." to show that this is not the beginning
2695                     detectedString += "...";
2696                 }
2697                 int endIndex = validationFailureIndex + 20;
2698                 if (endIndex >= value.Length) {
2699                     endIndex = value.Length;
2700                     detectedString += value.Substring(startIndex, endIndex - startIndex) + "\"";
2701                 }
2702                 else {
2703                     detectedString += value.Substring(startIndex, endIndex - startIndex) + "...\"";
2704                 }
2705 
2706                 string collectionName = GetRequestValidationSourceName(requestCollection);
2707                 throw new HttpRequestValidationException(SR.GetString(SR.Dangerous_input_detected,
2708                     collectionName, detectedString));
2709             }
2710         }
2711 
EnableGranularRequestValidation()2712         internal void EnableGranularRequestValidation() {
2713             _flags[granularValidationEnabled] = true;
2714         }
2715 
GetRequestValidationSourceName(RequestValidationSource requestCollection)2716         private static string GetRequestValidationSourceName(RequestValidationSource requestCollection) {
2717             switch (requestCollection) {
2718                 case RequestValidationSource.Cookies: return "Request.Cookies";
2719                 case RequestValidationSource.Files: return "Request.Files";
2720                 case RequestValidationSource.Form: return "Request.Form";
2721                 case RequestValidationSource.Headers: return "Request.Headers";
2722                 case RequestValidationSource.Path: return "Request.Path";
2723                 case RequestValidationSource.PathInfo: return "Request.PathInfo";
2724                 case RequestValidationSource.QueryString: return "Request.QueryString";
2725                 case RequestValidationSource.RawUrl: return "Request.RawUrl";
2726 
2727                 default:
2728                     return "Request." + requestCollection.ToString();
2729             }
2730         }
2731 
ValidateHttpValueCollection(HttpValueCollection collection, RequestValidationSource requestCollection)2732         private void ValidateHttpValueCollection(HttpValueCollection collection, RequestValidationSource requestCollection) {
2733             if (GranularValidationEnabled) {
2734                 // Granular request validation is enabled - validate collection entries only as they're accessed.
2735                 collection.EnableGranularValidation((key, value) => ValidateString(value, key, requestCollection));
2736             }
2737             else {
2738                 // Granular request validation is disabled - eagerly validate all collection entries.
2739                 int c = collection.Count;
2740 
2741                 for (int i = 0; i < c; i++) {
2742                     String key = collection.GetKey(i);
2743 
2744                     // Certain fields shouldn't go through validation - see comments in KeyIsCandidateForValidation for more information.
2745                     if (!HttpValueCollection.KeyIsCandidateForValidation(key)) {
2746                         continue;
2747                     }
2748 
2749                     String val = collection.Get(i);
2750 
2751                     if (!String.IsNullOrEmpty(val))
2752                         ValidateString(val, key, requestCollection);
2753                 }
2754             }
2755         }
2756 
ValidateCookieCollection(HttpCookieCollection cc)2757         private void ValidateCookieCollection(HttpCookieCollection cc) {
2758             if (GranularValidationEnabled) {
2759                 // Granular request validation is enabled - validate collection entries only as they're accessed.
2760                 cc.EnableGranularValidation((key, value) => ValidateString(value, key, RequestValidationSource.Cookies));
2761             }
2762             else {
2763                 // Granular request validation is disabled - eagerly validate all collection entries.
2764                 int c = cc.Count;
2765 
2766                 for (int i = 0; i < c; i++) {
2767                     String key = cc.GetKey(i);
2768                     String val = cc.Get(i).Value;
2769 
2770                     if (!String.IsNullOrEmpty(val))
2771                         ValidateString(val, key, RequestValidationSource.Cookies);
2772                 }
2773             }
2774         }
2775 
ValidatePostedFileCollection(HttpFileCollection col)2776         private void ValidatePostedFileCollection(HttpFileCollection col) {
2777             if (GranularValidationEnabled) {
2778                 // Granular request validation is enabled - validate collection entries only as they're accessed.
2779                 col.EnableGranularValidation((key, value) => ValidateString(value, "filename", RequestValidationSource.Files));
2780             }
2781             else {
2782                 // Granular request validation is disabled - eagerly validate all collection entries.
2783                 for (int i = 0; i < col.Count; i++) {
2784                     string filename = col[i].FileName;
2785                     ValidateString(filename, "filename", RequestValidationSource.Files);
2786                 }
2787             }
2788         }
2789 
ClearReferencesForWebSocketProcessing()2790         internal void ClearReferencesForWebSocketProcessing() {
2791             bool needToRevalidateInputs = ValidateInputWasCalled;
2792 
2793             // everything not marked [DoNotReset] should be eligible for garbage collection
2794             ReflectionUtil.Reset(this);
2795 
2796             if (needToRevalidateInputs) {
2797                 ValidateInput();
2798             }
2799         }
2800 
2801         /*
2802          * Get coordinates of the clicked image send as name.x=&name.y=
2803          * in the form or in the query string
2804          * @param imageFieldName name of the image field
2805          * @return x,y as int[2] or null if not found
2806          */
2807 
2808         /// <devdoc>
2809         ///    <para>
2810         ///       Maps an incoming image field form parameter into appropriate x/y
2811         ///       coordinate values.
2812         ///    </para>
2813         /// </devdoc>
MapImageCoordinates(String imageFieldName)2814         public int[] MapImageCoordinates(String imageFieldName) {
2815             var coords = MapImageCoordinatatesInternal(imageFieldName, HttpVerb, QueryString, Form);
2816             if (coords != null) {
2817                 return new[] { (int)coords[0], (int)coords[1] };
2818             }
2819             return null;
2820         }
2821 
2822         /*
2823          * Get coordinates of the clicked image send as name.x=&name.y=
2824          * in the form or in the query string
2825          * @param imageFieldName name of the image field
2826          * @return x,y as double[2] or null if not found
2827          */
2828 
2829         /// <devdoc>
2830         ///    <para>
2831         ///       Maps an incoming image field form parameter into appropriate x/y
2832         ///       coordinate values.
2833         ///    </para>
2834         /// </devdoc>
MapRawImageCoordinates(String imageFieldName)2835         public double[] MapRawImageCoordinates(String imageFieldName) {
2836             return MapImageCoordinatatesInternal(imageFieldName, HttpVerb, QueryString, Form);
2837         }
2838 
MapImageCoordinatatesInternal(string imageFieldName, HttpVerb verb, NameValueCollection queryString, NameValueCollection form)2839         internal static double[] MapImageCoordinatatesInternal(string imageFieldName, HttpVerb verb, NameValueCollection queryString, NameValueCollection form) {
2840             // Select collection where to look according to verb
2841 
2842             NameValueCollection c = null;
2843 
2844             switch (verb) {
2845                 case HttpVerb.GET:
2846                 case HttpVerb.HEAD:
2847                     c = queryString;
2848                     break;
2849 
2850                 case HttpVerb.POST:
2851                     c = form;
2852                     break;
2853 
2854                 default:
2855                     return null;
2856             }
2857 
2858             // Look for .x and .y values in the collection
2859 
2860             double[] ret = null;
2861 
2862             try {
2863                 string x = c[imageFieldName + ".x"];
2864                 string y = c[imageFieldName + ".y"];
2865 
2866                 double xVal;
2867                 double yVal;
2868 
2869                 if (x != null && y != null && HttpUtility.TryParseCoordinates(x, out xVal) && HttpUtility.TryParseCoordinates(y, out yVal)) {
2870                     ret = new[] { xVal, yVal };
2871                 }
2872             }
2873             catch {
2874                 // eat parsing exceptions
2875             }
2876 
2877             return ret;
2878         }
2879 
2880         /*
2881          * Save contents of request into a file
2882          * @param filename where to save
2883          * @param includeHeaders flag to request inclusion of Http headers
2884          */
2885 
2886         /// <devdoc>
2887         ///    <para>Saves an HTTP request to disk.</para>
2888         /// </devdoc>
SaveAs(String filename, bool includeHeaders)2889         public void SaveAs(String filename, bool includeHeaders) {
2890             // NDPWhidbey 14376
2891             if (!System.IO.Path.IsPathRooted(filename)) {
2892                 HttpRuntimeSection config = RuntimeConfig.GetConfig(_context).HttpRuntime;
2893                 if (config.RequireRootedSaveAsPath) {
2894                     throw new HttpException(SR.GetString(SR.SaveAs_requires_rooted_path, filename));
2895                 }
2896             }
2897 
2898             FileStream f = new FileStream(filename, FileMode.Create);
2899 
2900             try {
2901                 // headers
2902 
2903                 if (includeHeaders) {
2904                     TextWriter w = new StreamWriter(f);
2905 
2906                     w.Write(this.HttpMethod + " " + this.Path);
2907 
2908                     String qs = this.QueryStringText;
2909                     if (!String.IsNullOrEmpty(qs))
2910                         w.Write("?" + qs);
2911 
2912                     if (_wr != null) {
2913                         // real request -- add protocol
2914                         w.Write(" " + _wr.GetHttpVersion() + "\r\n");
2915 
2916                         // headers
2917                         w.Write(CombineAllHeaders(true));
2918                     }
2919                     else {
2920                         // manufactured request
2921                         w.Write("\r\n");
2922                     }
2923 
2924                     w.Write("\r\n");
2925                     w.Flush();
2926                 }
2927 
2928                 // entity body
2929 
2930                 HttpInputStream s = (HttpInputStream)this.InputStream;
2931                 s.WriteTo(f);
2932                 f.Flush();
2933             }
2934             finally {
2935                 f.Close();
2936             }
2937         }
2938 
2939         /*
2940          * Map virtual path to physical path relative to current request
2941          * @param virtualPath virtual path (absolute or relative)
2942          * @return physical path
2943          */
2944 
2945         /// <devdoc>
2946         ///    <para>
2947         ///       Maps the given virtual path to a physical path.
2948         ///    </para>
2949         /// </devdoc>
MapPath(String virtualPath)2950         public String MapPath(String virtualPath) {
2951             return MapPath(VirtualPath.CreateAllowNull(virtualPath));
2952         }
2953 
MapPath(VirtualPath virtualPath)2954         internal String MapPath(VirtualPath virtualPath) {
2955             if (_wr != null) {
2956                 return MapPath(virtualPath, FilePathObject, true/*allowCrossAppMapping*/);
2957             }
2958             else {
2959                 return virtualPath.MapPath();
2960             }
2961         }
2962 
2963 
2964         /// <devdoc>
2965         ///    <para>
2966         ///       Maps the given virtual path to a physical path.
2967         ///    </para>
2968         /// </devdoc>
MapPath(string virtualPath, string baseVirtualDir, bool allowCrossAppMapping)2969         public String MapPath(string virtualPath, string baseVirtualDir, bool allowCrossAppMapping) {
2970             VirtualPath baseVirtualDirObject;
2971             if (String.IsNullOrEmpty(baseVirtualDir)) {
2972                 // If no base is passed in, use the request's base dir (VSWhidbey 539063)
2973                 baseVirtualDirObject = FilePathObject;
2974             }
2975             else {
2976                 // We need to ensure a trailing slash to match v1.x behavior (VSWhidbey 539063)
2977                 baseVirtualDirObject = VirtualPath.CreateTrailingSlash(baseVirtualDir);
2978             }
2979 
2980             return MapPath(VirtualPath.CreateAllowNull(virtualPath),
2981                 baseVirtualDirObject, allowCrossAppMapping);
2982         }
2983 
MapPath(VirtualPath virtualPath, VirtualPath baseVirtualDir, bool allowCrossAppMapping)2984         internal String MapPath(VirtualPath virtualPath, VirtualPath baseVirtualDir, bool allowCrossAppMapping) {
2985             if (_wr == null)
2986                 throw new HttpException(SR.GetString(SR.Cannot_map_path_without_context));
2987 
2988             // treat null as "."
2989 
2990             //
2991             if (virtualPath == null)
2992                 virtualPath = VirtualPath.Create(".");
2993 
2994             VirtualPath originalVirtualPath = virtualPath; // remember for patch-up at the end
2995 
2996             // Combine it with the base if one was passed in
2997             if (baseVirtualDir != null) {
2998                 virtualPath = baseVirtualDir.Combine(virtualPath);
2999             }
3000 
3001             if (!allowCrossAppMapping)
3002                 virtualPath.FailIfNotWithinAppRoot();
3003 
3004             string realPath = virtualPath.MapPathInternal();
3005 
3006             // patch up the result for Everett combatibility (VSWhidbey 319826)
3007             if (virtualPath.VirtualPathString == "/" &&
3008                 originalVirtualPath.VirtualPathString != "/" &&
3009                 !originalVirtualPath.HasTrailingSlash &&
3010                 UrlPath.PathEndsWithExtraSlash(realPath)) {
3011                 realPath = realPath.Substring(0, realPath.Length - 1);
3012             }
3013 
3014             InternalSecurityPermissions.PathDiscovery(realPath).Demand();
3015             return realPath;
3016         }
3017 
InternalRewritePath(VirtualPath newPath, String newQueryString, bool rebaseClientPath)3018         internal void InternalRewritePath(VirtualPath newPath, String newQueryString, bool rebaseClientPath) {
3019             // clear things that depend on path
3020             _pathTranslated = null;
3021             _pathInfo = null;
3022             _filePath = null;
3023             _url = null;
3024             Unvalidated.InvalidateUrl();
3025 
3026             // DevDiv Bug 164390: calling the worker request's RawUrl method here
3027             // to ensure we cache the original request Url in Url Mapping scenarios.
3028             string temp = RawUrl;
3029 
3030             // remember the new path
3031             _path = newPath;
3032 
3033             if (rebaseClientPath) {
3034                 _clientBaseDir = null;
3035                 _clientFilePath = newPath;
3036             }
3037 
3038             // set a flag so we compute things that depend on path by hand
3039             _computePathInfo = true;
3040 
3041             // parse the new query string (might require config)
3042             if (newQueryString != null)
3043                 this.QueryStringText = newQueryString;
3044 
3045             // remember the rewritten url
3046             _rewrittenUrl = _path.VirtualPathString;
3047             string q = QueryStringText;
3048             if (!String.IsNullOrEmpty(q))
3049                 _rewrittenUrl += "?" + q;
3050 
3051             IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
3052             if (iis7WorkerRequest != null) {
3053                 iis7WorkerRequest.RewriteNotifyPipeline(_path.VirtualPathString, newQueryString, rebaseClientPath);
3054             }
3055         }
3056 
InternalRewritePath(VirtualPath newFilePath, VirtualPath newPathInfo, String newQueryString, bool setClientFilePath)3057         internal void InternalRewritePath(VirtualPath newFilePath, VirtualPath newPathInfo,
3058             String newQueryString, bool setClientFilePath) {
3059             // clear things that depend on path
3060             _pathTranslated = (_wr != null) ? newFilePath.MapPathInternal() : null;
3061             _pathInfo = newPathInfo;
3062             _filePath = newFilePath;
3063             _url = null;
3064             Unvalidated.InvalidateUrl();
3065 
3066             // DevDiv Bug 164390: calling the worker request's RawUrl method here
3067             // to ensure we cache the original request Url in Url Mapping scenarios.
3068             string temp = RawUrl;
3069 
3070             if (newPathInfo == null) {
3071                 _path = newFilePath;
3072             }
3073             else {
3074                 // Combine the file path and the pathInfo to get the path.  Note that we can't call
3075                 // newFilePath.Combine here, since the rules are very different here (VSWhidbey 498926, 528055)
3076                 string newFullPathString = newFilePath.VirtualPathStringWhicheverAvailable + "/" + newPathInfo.VirtualPathString;
3077                 _path = VirtualPath.Create(newFullPathString);
3078             }
3079 
3080             if (newQueryString != null)
3081                 this.QueryStringText = newQueryString;
3082 
3083             // remember the rewritten url
3084             _rewrittenUrl = _path.VirtualPathString;
3085             string q = QueryStringText;
3086             if (!String.IsNullOrEmpty(q))
3087                 _rewrittenUrl += "?" + q;
3088 
3089             // no need to calculate any paths
3090             _computePathInfo = false;
3091 
3092             if (setClientFilePath) {
3093                 _clientFilePath = newFilePath;
3094             }
3095 
3096             IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
3097             if (iis7WorkerRequest != null) {
3098                 String newPath = (_path != null && _path.VirtualPathString != null) ? _path.VirtualPathString : String.Empty;
3099                 iis7WorkerRequest.RewriteNotifyPipeline(newPath, newQueryString, setClientFilePath);
3100             }
3101         }
3102 
3103         internal String RewrittenUrl {
3104             get { return _rewrittenUrl; }
3105         }
3106 
FetchServerVariable(string variable)3107         internal string FetchServerVariable(string variable) {
3108             return _wr.GetServerVariable(variable);
3109         }
3110 
3111         // Used by IIS7WorkerRequest.SynchronizeServerVariables to update server variables in the collection
SynchronizeServerVariable(String name, String value)3112         internal void SynchronizeServerVariable(String name, String value) {
3113             if (name == "IS_LOGIN_PAGE") {
3114                 bool skipAuth = (value != null && value != "0") ? true : false;
3115                 _context.SetSkipAuthorizationNoDemand(skipAuth, true /*managedOnly*/);
3116             }
3117             // populate the server variables collection if necessary
3118             HttpServerVarsCollection serverVars = ServerVariables as HttpServerVarsCollection;
3119             if (serverVars != null) {
3120                 serverVars.SynchronizeServerVariable(name, value);
3121             }
3122         }
3123 
3124         // Used by IIS7WorkerRequest.SynchronizeServerVariables to update server variables in the collection
SynchronizeHeader(String name, String value)3125         internal void SynchronizeHeader(String name, String value) {
3126 
3127             // populate the headers collection if necessary
3128             HttpHeaderCollection headers = Headers as HttpHeaderCollection;
3129             if (headers != null) {
3130                 headers.SynchronizeHeader(name, value);
3131             }
3132 
3133             // if a header changes, the server variable also needs to be updated
3134             // populate the server variables collection if necessary
3135             HttpServerVarsCollection serverVars = ServerVariables as HttpServerVarsCollection;
3136             if (serverVars != null) {
3137                 string svName = "HTTP_" + name.ToUpper(CultureInfo.InvariantCulture).Replace('-', '_');
3138                 serverVars.SynchronizeServerVariable(svName, value);
3139             }
3140         }
3141 
3142         public ChannelBinding HttpChannelBinding {
3143             [PermissionSet(SecurityAction.Demand, Unrestricted = true)]
3144             get {
3145                 if (_wr is IIS7WorkerRequest)
3146                     return ((IIS7WorkerRequest)_wr).HttpChannelBindingToken;
3147                 else if (_wr is ISAPIWorkerRequestInProc)
3148                     return ((ISAPIWorkerRequestInProc)_wr).HttpChannelBindingToken;
3149                 throw new PlatformNotSupportedException();
3150             }
3151         }
3152 
3153         // Retrieves the TLS token bindings for the current request.
3154         // TLS token bindings help mitigate the risk of impersonation by an
3155         // attacker in the event an authenticated client's bearer tokens are
3156         // somehow exfiltrated from the client's machine.
3157         // More info: https://datatracker.ietf.org/doc/draft-popov-token-binding/
3158         //
3159         // This API requires that the server be running the IIS integrated mode
3160         // pipeline and that the server be Win10 or later. If these conditions
3161         // do not hold, this API will return null. This API could also return
3162         // null if the client doesn't support the TLS token binding protocol or
3163         // if the server has disabled support for the protocol.
3164         public ITlsTokenBindingInfo TlsTokenBindingInfo {
3165             get {
3166                 if (!_tlsTokenBindingInfoResolved) {
3167                     IIS7WorkerRequest iis7wr = _wr as IIS7WorkerRequest;
3168                     if (iis7wr != null) {
3169                         _tlsTokenBindingInfo = iis7wr.GetTlsTokenBindingInfo(); // could return null
3170                     }
3171                     _tlsTokenBindingInfoResolved = true;
3172                 }
3173 
3174                 return _tlsTokenBindingInfo;
3175             }
3176         }
3177 
3178         // Only supported on IIS7 and later.
3179         // This is a wrapper for the IIS 7.0 IHttpRequest::InsertEntityBody method.
3180         // If you want to provide IIS with a copy of the request entity previously read by ASP.NET,
3181         // call the InsertEntityBody overload that takes no arguments.
3182         [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
InsertEntityBody(byte[] buffer, int offset, int count)3183         public void InsertEntityBody(byte[] buffer, int offset, int count) {
3184             EnsureHasNotTransitionedToWebSocket();
3185 
3186             IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
3187             if (wr == null)
3188                 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
3189             if (buffer == null)
3190                 throw new ArgumentNullException("buffer");
3191             if (offset < 0)
3192                 throw new ArgumentOutOfRangeException("offset");
3193             if (count < 0)
3194                 throw new ArgumentOutOfRangeException("count");
3195             if (buffer.Length - offset < count)
3196                 throw new ArgumentException(SR.GetString(SR.InvalidOffsetOrCount, "offset", "count"));
3197 
3198             wr.InsertEntityBody(buffer, offset, count);
3199             NeedToInsertEntityBody = false;
3200         }
3201 
3202         // Only supported on IIS7 and later.  IIS does not maintain a copy of the request entity
3203         // after it has been read.  For this reason, it is recommended that only the handler
3204         // read the request entity.  This method provides IIS with a copy of the request entity that ASP.NET
3205         // previously read.  For example, this is useful for scenarios where a native handler may need to access the
3206         // request entity after it has been read by ASP.NET.
InsertEntityBody()3207         public void InsertEntityBody() {
3208             IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
3209             if (wr == null)
3210                 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
3211             byte[] buffer = EntityBody;
3212             if (buffer == null)
3213                 return;
3214             wr.InsertEntityBody(buffer, 0, buffer.Length);
3215             NeedToInsertEntityBody = false;
3216         }
3217 
3218         // Determines if the request entity body has been read, and if so, how it was
3219         // read.  Used to avoid the exception thrown by GetBufferlessInputStream,
3220         // GetBufferedInputStream, Form, Files, InputStream, and BinaryRead when the
3221         // entity has already been read by an incompatible method.
3222         public ReadEntityBodyMode ReadEntityBodyMode { get { return _readEntityBodyMode; } }
3223 
3224         // GetBufferlessInputStream allows the caller to read the request entity bytes directly off the wire, either
3225         // synchronously or asynchronously.  The bytes are not buffered, and once read, neither ASP.NET nor IIS have a
3226         // copy of the request entity.  To read the entire entity, call Read or BeginRead/EndRead repeatedly until zero
3227         // bytes are returned.  The stream returned keeps track of the total bytes read, and for non-chunked entity bodies
3228         // it will not read more bytes than indicated by the Content-Length header.  If the client disconnects while the
3229         // entity is being read, the Read and BeginRead/EndRead methods will throw an exception.
3230         //
3231         // Throws HttpException if the entity has already been read and stored via Form, Files, InputStream, or GetBufferedInputStream.
3232         // To avoid this exception, first call Request.ReadEntityBodyMode.
3233         // Throws HttpException from Read/BeginRead/EndRead if the client disconnects while the entity is being read.
3234         [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="Bufferless", Justification = "Name is from the spec")]
3235         [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
3236           Justification = "This method does additional work which causes very big side-effects")]
GetBufferlessInputStream()3237         public Stream GetBufferlessInputStream() {
3238             return GetInputStream(persistEntityBody: false);
3239         }
3240 
3241         // Same as GetBufferlessInputStream, but with the option of disabling system.web/httpRuntime/maxRequestLength.
3242         [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="Bufferless", Justification = "Name is from the spec")]
3243         [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
3244           Justification = "This method does additional work which causes very big side-effects")]
GetBufferlessInputStream(bool disableMaxRequestLength)3245         public Stream GetBufferlessInputStream(bool disableMaxRequestLength) {
3246             return GetInputStream(false, disableMaxRequestLength);
3247         }
3248 
3249         // GetBufferedInputStream is identical to GetBufferlessInputStream except that it also copies the bytes read
3250         // to internal storage used by ASP.NET to populate HttpRequest.Form, HttpRequest.Files, and HttpRequest.InputStream.
3251         //
3252         // Throws HttpException if the entity has already been read by Form, Files, InputStream, or GetBufferlessInputStream.
3253         // To avoid this exception, first call Request.ReadEntityBodyMode.
3254         // Throws HttpException from Read/BeginRead/EndRead if the client disconnects while the entity is being read.
3255         [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="Buffered", Justification = "Name is from the spec")]
3256         [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
3257           Justification = "This method does additional work which causes very big side-effects")]
GetBufferedInputStream()3258         public Stream GetBufferedInputStream() {
3259             return GetInputStream(persistEntityBody: true);
3260         }
3261 
GetInputStream(bool persistEntityBody, bool disableMaxRequestLength = false)3262         private Stream GetInputStream(bool persistEntityBody, bool disableMaxRequestLength = false) {
3263             EnsureHasNotTransitionedToWebSocket();
3264 
3265             ReadEntityBodyMode requestedMode = (persistEntityBody) ? ReadEntityBodyMode.Buffered : ReadEntityBodyMode.Bufferless;
3266             ReadEntityBodyMode currentMode = _readEntityBodyMode;
3267             if (currentMode == ReadEntityBodyMode.None) {
3268                 _readEntityBodyMode = requestedMode;
3269                 _readEntityBodyStream = new HttpBufferlessInputStream(_context, persistEntityBody, disableMaxRequestLength);
3270             }
3271             else if (currentMode == ReadEntityBodyMode.Classic) {
3272                 throw new HttpException(SR.GetString(SR.Incompatible_with_input_stream));
3273             }
3274             else if (currentMode != requestedMode) {
3275                 throw new HttpException((persistEntityBody) ? SR.GetString(SR.Incompatible_with_get_bufferless_input_stream) : SR.GetString(SR.Incompatible_with_get_buffered_input_stream));
3276             }
3277             return _readEntityBodyStream;
3278         }
3279 
3280         /// <summary>
3281         /// Forcibly terminates the underlying TCP connection to the client, causing any outstanding I/O to fail.
3282         /// </summary>
3283         /// <remarks>
3284         /// This method requires that the application be using the IIS integrated mode pipeline.
3285         ///
3286         /// This method is thread-safe. Any thread may call it at any time.
3287         /// </remarks>
Abort()3288         public void Abort() {
3289             IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
3290             if (wr != null) {
3291                 wr.AbortConnection();
3292             }
3293             else {
3294                 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
3295             }
3296         }
3297 
3298         // helper that throws an exception if we have transitioned the current request to a WebSocket request
EnsureHasNotTransitionedToWebSocket()3299         internal void EnsureHasNotTransitionedToWebSocket() {
3300             if (Context != null) {
3301                 Context.EnsureHasNotTransitionedToWebSocket();
3302             }
3303         }
3304 
3305         /// <summary>
3306         /// Retrieves a CancellationToken that is signaled when a request times out.
3307         /// </summary>
3308         /// <remarks>
3309         /// The timeout period can be specified via config (httpRuntime/executionTimeout) or programmatically via
3310         /// Server.ScriptTimeout. The timeout period is measured from the time the request comes in. If using the
3311         /// default timeout of 110 seconds, the TimedOutToken will be tripped no earlier than 110 seconds after we
3312         /// started processing the request. The application developer can change the ScriptTimeout property if he
3313         /// so chooses, and as long as we haven't yet tripped this token we will respect the new timeout value.
3314         ///
3315         /// Currently we only provide 15 second granularity on this token, so using the default timeout period of
3316         /// 110 seconds this means that we'll actually trip the token sometime between 110 - 125 seconds after we
3317         /// started processing the request. We may improve this resolution in the future.
3318         ///
3319         /// Even though this property is thread-safe, there are restrictions on its use. Please see the remarks
3320         /// on HttpResponse.ClientDisconnectToken for caveats and best practices when consuming CancellationToken
3321         /// properties provided by ASP.NET intrinsics.
3322         ///
3323         /// This property is meaningless once WebSockets request processing has started.
3324         /// </remarks>
3325         public CancellationToken TimedOutToken {
3326             get {
3327                 EnsureHasNotTransitionedToWebSocket();
3328 
3329                 HttpContext context = Context;
3330                 return (context != null) ? context.TimedOutToken : default(CancellationToken);
3331             }
3332         }
3333 
3334     }
3335 }
3336