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