1 // 2 // System.Web.HttpResponse.cs 3 // 4 // 5 // Author: 6 // Miguel de Icaza (miguel@novell.com) 7 // Gonzalo Paniagua Javier (gonzalo@ximian.com) 8 // Marek Habersack <mhabersack@novell.com> 9 // 10 // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com) 11 // 12 // Permission is hereby granted, free of charge, to any person obtaining 13 // a copy of this software and associated documentation files (the 14 // "Software"), to deal in the Software without restriction, including 15 // without limitation the rights to use, copy, modify, merge, publish, 16 // distribute, sublicense, and/or sell copies of the Software, and to 17 // permit persons to whom the Software is furnished to do so, subject to 18 // the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be 21 // included in all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 // 31 32 using System.Text; 33 using System.Web.UI; 34 using System.Collections; 35 using System.Collections.Specialized; 36 using System.IO; 37 using System.Web.Caching; 38 using System.Threading; 39 using System.Web.Util; 40 using System.Web.Configuration; 41 using System.Globalization; 42 using System.Security.Permissions; 43 using System.Web.Hosting; 44 using System.Web.SessionState; 45 46 using System.Web.Routing; 47 48 namespace System.Web 49 { 50 // CAS - no InheritanceDemand here as the class is sealed 51 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] 52 public sealed partial class HttpResponse 53 { 54 internal HttpWorkerRequest WorkerRequest; 55 internal HttpResponseStream output_stream; 56 internal bool buffer = true; 57 58 ArrayList fileDependencies; 59 60 HttpContext context; 61 TextWriter writer; 62 HttpCachePolicy cache_policy; 63 Encoding encoding; 64 HttpCookieCollection cookies; 65 66 int status_code = 200; 67 string status_description = "OK"; 68 69 string content_type = "text/html"; 70 string charset; 71 bool charset_set; 72 CachedRawResponse cached_response; 73 string user_cache_control = "private"; 74 string redirect_location; 75 string version_header; 76 bool version_header_checked; 77 78 // 79 // Negative Content-Length means we auto-compute the size of content-length 80 // can be overwritten with AppendHeader ("Content-Length", value) 81 // 82 long content_length = -1; 83 84 // 85 // The list of the headers that we will send back to the client, except 86 // the headers that we compute here. 87 // 88 89 HttpHeaderCollection headers; 90 bool headers_sent; 91 NameValueCollection cached_headers; 92 93 // 94 // Transfer encoding state 95 // 96 string transfer_encoding; 97 internal bool use_chunked; 98 99 bool closed; 100 bool completed; 101 internal bool suppress_content; 102 103 // 104 // Session State 105 // 106 string app_path_mod; 107 bool is_request_being_redirected; 108 Encoding headerEncoding; 109 HttpResponse()110 internal HttpResponse () 111 { 112 output_stream = new HttpResponseStream (this); 113 writer = new HttpWriter (this); 114 } 115 HttpResponse(TextWriter writer)116 public HttpResponse (TextWriter writer) : this () 117 { 118 119 this.writer = writer; 120 } 121 HttpResponse(HttpWorkerRequest worker_request, HttpContext context)122 internal HttpResponse (HttpWorkerRequest worker_request, HttpContext context) : this () 123 { 124 WorkerRequest = worker_request; 125 this.context = context; 126 127 if (worker_request != null && worker_request.GetHttpVersion () == "HTTP/1.1") { 128 string gi = worker_request.GetServerVariable ("GATEWAY_INTERFACE"); 129 use_chunked = (String.IsNullOrEmpty (gi) || 130 !gi.StartsWith ("cgi", StringComparison.OrdinalIgnoreCase)); 131 } else { 132 use_chunked = false; 133 } 134 writer = new HttpWriter (this); 135 } 136 SetTextWriter(TextWriter writer)137 internal TextWriter SetTextWriter (TextWriter writer) 138 { 139 TextWriter prev = this.writer; 140 this.writer = writer; 141 return prev; 142 } 143 144 internal string VersionHeader { 145 get { 146 if (!version_header_checked && version_header == null) { 147 version_header_checked = true; 148 HttpRuntimeSection config = HttpRuntime.Section; 149 if (config != null && config.EnableVersionHeader) 150 version_header = Environment.Version.ToString (3); 151 } 152 153 return version_header; 154 } 155 } 156 157 internal HttpContext Context { 158 get { return context; } 159 set { context = value; } 160 } 161 162 internal string[] FileDependencies { 163 get { 164 if (fileDependencies == null || fileDependencies.Count == 0) 165 return new string[0] {}; 166 return (string[]) fileDependencies.ToArray (typeof (string)); 167 } 168 } 169 170 ArrayList FileDependenciesArray { 171 get { 172 if (fileDependencies == null) 173 fileDependencies = new ArrayList (); 174 return fileDependencies; 175 } 176 } 177 178 public bool Buffer { 179 get { 180 return buffer; 181 } 182 183 set { 184 buffer = value; 185 } 186 } 187 188 public bool BufferOutput { 189 get { 190 return buffer; 191 } 192 193 set { 194 buffer = value; 195 } 196 } 197 198 // 199 // Use the default from <globalization> section if the client has not set the encoding 200 // 201 public Encoding ContentEncoding { 202 get { 203 if (encoding == null) { 204 if (context != null) { 205 string client_content_type = context.Request.ContentType; 206 string parameter = HttpRequest.GetParameter (client_content_type, "; charset="); 207 if (parameter != null) { 208 try { 209 // Do what the #1 web server does 210 encoding = Encoding.GetEncoding (parameter); 211 } catch { 212 } 213 } 214 } 215 if (encoding == null) 216 encoding = WebEncoding.ResponseEncoding; 217 } 218 return encoding; 219 } 220 221 set { 222 if (value == null) 223 throw new ArgumentException ("ContentEncoding can not be null"); 224 225 encoding = value; 226 HttpWriter http_writer = writer as HttpWriter; 227 if (http_writer != null) 228 http_writer.SetEncoding (encoding); 229 } 230 } 231 232 public string ContentType { 233 get { 234 return content_type; 235 } 236 237 set { 238 content_type = value; 239 } 240 } 241 242 public string Charset { 243 get { 244 if (charset == null) 245 charset = ContentEncoding.WebName; 246 247 return charset; 248 } 249 250 set { 251 charset_set = true; 252 charset = value; 253 } 254 } 255 256 public HttpCookieCollection Cookies { 257 get { 258 if (cookies == null) 259 cookies = new HttpCookieCollection (true, false); 260 return cookies; 261 } 262 } 263 264 public int Expires { 265 get { 266 if (cache_policy == null) 267 return 0; 268 269 return cache_policy.ExpireMinutes (); 270 } 271 272 set { 273 Cache.SetExpires (DateTime.Now + new TimeSpan (0, value, 0)); 274 } 275 } 276 277 public DateTime ExpiresAbsolute { 278 get { 279 return Cache.Expires; 280 } 281 282 set { 283 Cache.SetExpires (value); 284 } 285 } 286 287 public Stream Filter { 288 get { 289 if (WorkerRequest == null) 290 return null; 291 292 return output_stream.Filter; 293 } 294 295 set { 296 output_stream.Filter = value; 297 } 298 } 299 300 public Encoding HeaderEncoding { 301 get { 302 if (headerEncoding == null) { 303 GlobalizationSection gs = WebConfigurationManager.SafeGetSection ("system.web/globalization", typeof (GlobalizationSection)) as GlobalizationSection; 304 305 if (gs == null) 306 headerEncoding = Encoding.UTF8; 307 else { 308 headerEncoding = gs.ResponseHeaderEncoding; 309 if (headerEncoding == Encoding.Unicode) 310 throw new HttpException ("HeaderEncoding must not be Unicode"); 311 } 312 } 313 return headerEncoding; 314 } 315 set { 316 if (headers_sent) 317 throw new HttpException ("headers have already been sent"); 318 if (value == null) 319 throw new ArgumentNullException ("HeaderEncoding"); 320 if (value == Encoding.Unicode) 321 throw new HttpException ("HeaderEncoding must not be Unicode"); 322 headerEncoding = value; 323 } 324 } 325 326 public NameValueCollection Headers { 327 get { 328 if (headers == null) 329 headers = new HttpHeaderCollection (); 330 331 return headers; 332 } 333 } 334 335 336 public bool IsClientConnected { 337 get { 338 if (WorkerRequest == null) 339 return true; // yep that's true 340 341 return WorkerRequest.IsClientConnected (); 342 } 343 } 344 345 public bool IsRequestBeingRedirected { 346 get { return is_request_being_redirected; } 347 } 348 349 public TextWriter Output { 350 get { 351 return writer; 352 } 353 set { writer = value; } 354 } 355 356 public Stream OutputStream { 357 get { 358 return output_stream; 359 } 360 } 361 362 public string RedirectLocation { 363 get { 364 return redirect_location; 365 } 366 367 set { 368 redirect_location = value; 369 } 370 } 371 372 public string Status { 373 get { return String.Concat (status_code.ToString (), " ", StatusDescription); } 374 375 set { 376 int p = value.IndexOf (' '); 377 if (p == -1) 378 throw new HttpException ("Invalid format for the Status property"); 379 380 string s = value.Substring (0, p); 381 382 if (!Int32.TryParse (s, out status_code)) 383 throw new HttpException ("Invalid format for the Status property"); 384 385 status_description = value.Substring (p+1); 386 } 387 } 388 389 // We ignore the two properties on Mono as they are for use with IIS7, but there is 390 // no point in throwing PlatformNotSupportedException. We might find a use for them 391 // some day. 392 public int SubStatusCode { 393 get; 394 set; 395 } 396 397 public bool SuppressFormsAuthenticationRedirect { 398 get; 399 set; 400 } 401 402 public bool TrySkipIisCustomErrors { 403 get; 404 set; 405 } 406 407 public int StatusCode { 408 get { 409 return status_code; 410 } 411 412 set { 413 if (headers_sent) 414 throw new HttpException ("headers have already been sent"); 415 416 status_code = value; 417 status_description = null; 418 } 419 } 420 421 public string StatusDescription { 422 get { 423 if (status_description == null) 424 status_description = HttpWorkerRequest.GetStatusDescription (status_code); 425 426 return status_description; 427 } 428 429 set { 430 if (headers_sent) 431 throw new HttpException ("headers have already been sent"); 432 433 status_description = value; 434 } 435 } 436 437 public bool SuppressContent { 438 get { 439 return suppress_content; 440 } 441 442 set { 443 suppress_content = value; 444 } 445 } 446 447 [MonoTODO ("Not implemented")] AddCacheDependency(params CacheDependency[] dependencies)448 public void AddCacheDependency (params CacheDependency[] dependencies) 449 { 450 throw new NotImplementedException (); 451 } 452 453 [MonoTODO ("Not implemented")] AddCacheItemDependencies(string[] cacheKeys)454 public void AddCacheItemDependencies (string[] cacheKeys) 455 { 456 throw new NotImplementedException (); 457 } 458 459 [MonoTODO("Currently does nothing")] AddCacheItemDependencies(ArrayList cacheKeys)460 public void AddCacheItemDependencies (ArrayList cacheKeys) 461 { 462 // TODO: talk to jackson about the cache 463 } 464 465 [MonoTODO("Currently does nothing")] AddCacheItemDependency(string cacheKey)466 public void AddCacheItemDependency (string cacheKey) 467 { 468 // TODO: talk to jackson about the cache 469 } 470 AddFileDependencies(ArrayList filenames)471 public void AddFileDependencies (ArrayList filenames) 472 { 473 if (filenames == null || filenames.Count == 0) 474 return; 475 FileDependenciesArray.AddRange (filenames); 476 } 477 AddFileDependencies(string[] filenames)478 public void AddFileDependencies (string[] filenames) 479 { 480 if (filenames == null || filenames.Length == 0) 481 return; 482 FileDependenciesArray.AddRange (filenames); 483 } 484 AddFileDependency(string filename)485 public void AddFileDependency (string filename) 486 { 487 if (filename == null || filename == String.Empty) 488 return; 489 FileDependenciesArray.Add (filename); 490 } 491 AddHeader(string name, string value)492 public void AddHeader (string name, string value) 493 { 494 AppendHeader (name, value); 495 } 496 AppendCookie(HttpCookie cookie)497 public void AppendCookie (HttpCookie cookie) 498 { 499 Cookies.Add (cookie); 500 } 501 502 // 503 // AppendHeader: 504 // Special case for Content-Length, Content-Type, Transfer-Encoding and Cache-Control 505 // 506 // AppendHeader(string name, string value)507 public void AppendHeader (string name, string value) 508 { 509 if (headers_sent) 510 throw new HttpException ("Headers have been already sent"); 511 if (String.Compare (name, "content-length", StringComparison.OrdinalIgnoreCase) == 0){ 512 content_length = (long) UInt64.Parse (value); 513 use_chunked = false; 514 return; 515 } 516 517 if (String.Compare (name, "content-type", StringComparison.OrdinalIgnoreCase) == 0){ 518 ContentType = value; 519 return; 520 } 521 522 if (String.Compare (name, "transfer-encoding", StringComparison.OrdinalIgnoreCase) == 0){ 523 transfer_encoding = value; 524 use_chunked = false; 525 return; 526 } 527 528 if (String.Compare (name, "cache-control", StringComparison.OrdinalIgnoreCase) == 0){ 529 user_cache_control = value; 530 return; 531 } 532 533 Headers.Add (name, value); 534 } 535 536 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)] AppendToLog(string param)537 public void AppendToLog (string param) 538 { 539 Console.Write ("System.Web: "); 540 Console.WriteLine (param); 541 } 542 ApplyAppPathModifier(string virtualPath)543 public string ApplyAppPathModifier (string virtualPath) 544 { 545 if (virtualPath == null || context == null) 546 return null; 547 548 if (virtualPath.Length == 0) 549 return context.Request.RootVirtualDir; 550 551 if (UrlUtils.IsRelativeUrl (virtualPath)) { 552 virtualPath = UrlUtils.Combine (context.Request.RootVirtualDir, virtualPath); 553 } else if (UrlUtils.IsRooted (virtualPath)) { 554 virtualPath = UrlUtils.Canonic (virtualPath); 555 } 556 557 bool cookieless = false; 558 SessionStateSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/sessionState") as SessionStateSection; 559 cookieless = SessionStateModule.IsCookieLess (context, config); 560 561 if (!cookieless) 562 return virtualPath; 563 564 if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) { 565 if (UrlUtils.HasSessionId (virtualPath)) 566 virtualPath = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (virtualPath), virtualPath); 567 return UrlUtils.InsertSessionId (app_path_mod, virtualPath); 568 } 569 570 return virtualPath; 571 } 572 BinaryWrite(byte [] buffer)573 public void BinaryWrite (byte [] buffer) 574 { 575 output_stream.Write (buffer, 0, buffer.Length); 576 } 577 BinaryWrite(byte [] buffer, int start, int len)578 internal void BinaryWrite (byte [] buffer, int start, int len) 579 { 580 output_stream.Write (buffer, start, len); 581 } 582 Clear()583 public void Clear () 584 { 585 ClearContent (); 586 } 587 ClearContent()588 public void ClearContent () 589 { 590 output_stream.Clear (); 591 content_length = -1; 592 } 593 ClearHeaders()594 public void ClearHeaders () 595 { 596 if (headers_sent) 597 throw new HttpException ("headers have been already sent"); 598 599 // Reset the special case headers. 600 content_length = -1; 601 content_type = "text/html"; 602 transfer_encoding = null; 603 user_cache_control = "private"; 604 if (cache_policy != null) 605 cache_policy.Cacheability = HttpCacheability.Private; 606 607 if (headers != null) 608 headers.Clear (); 609 } 610 611 internal bool HeadersSent { 612 get { 613 return headers_sent; 614 } 615 } 616 Close()617 public void Close () 618 { 619 if (closed) 620 return; 621 if (WorkerRequest != null) 622 WorkerRequest.CloseConnection (); 623 closed = true; 624 } 625 DisableKernelCache()626 public void DisableKernelCache () 627 { 628 // does nothing in Mono 629 } 630 End()631 public void End () 632 { 633 if (context == null) 634 return; 635 636 if (context.TimeoutPossible) { 637 Thread.CurrentThread.Abort (FlagEnd.Value); 638 } else { 639 // If this is called from an async event, signal the completion 640 // but don't throw. 641 HttpApplication app_instance = context.ApplicationInstance; 642 if (app_instance != null) 643 app_instance.CompleteRequest (); 644 } 645 } 646 647 // Generate: 648 // Content-Length 649 // Content-Type 650 // Transfer-Encoding (chunked) 651 // Cache-Control 652 // X-AspNet-Version AddHeadersNoCache(NameValueCollection write_headers, bool final_flush)653 void AddHeadersNoCache (NameValueCollection write_headers, bool final_flush) 654 { 655 // 656 // Transfer-Encoding 657 // 658 if (use_chunked) 659 write_headers.Add ("Transfer-Encoding", "chunked"); 660 else if (transfer_encoding != null) 661 write_headers.Add ("Transfer-Encoding", transfer_encoding); 662 if (redirect_location != null) 663 write_headers.Add ("Location", redirect_location); 664 665 string vh = VersionHeader; 666 if (vh != null) 667 write_headers.Add ("X-AspNet-Version", vh); 668 669 // 670 // If Content-Length is set. 671 // 672 if (content_length >= 0) { 673 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength), 674 content_length.ToString (Helpers.InvariantCulture)); 675 } else if (BufferOutput) { 676 if (final_flush) { 677 // 678 // If we are buffering and this is the last flush, not a middle-flush, 679 // we know the content-length. 680 // 681 content_length = output_stream.total; 682 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength), 683 content_length.ToString (Helpers.InvariantCulture)); 684 } else { 685 // 686 // We are buffering, and this is a flush in the middle. 687 // If we are not chunked, we need to set "Connection: close". 688 // 689 if (use_chunked){ 690 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close"); 691 } 692 } 693 } else { 694 // 695 // If the content-length is not set, and we are not buffering, we must 696 // close at the end. 697 // 698 if (use_chunked){ 699 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close"); 700 } 701 } 702 703 // 704 // Cache Control, the cache policy takes precedence over the cache_control property. 705 // 706 if (cache_policy != null) 707 cache_policy.SetHeaders (this, headers); 708 else 709 write_headers.Add ("Cache-Control", CacheControl); 710 711 // 712 // Content-Type 713 // 714 if (content_type != null){ 715 string header = content_type; 716 717 if (charset_set || header == "text/plain" || header == "text/html") { 718 if (header.IndexOf ("charset=") == -1 && !string.IsNullOrEmpty (charset)) { 719 header += "; charset=" + charset; 720 } 721 } 722 723 write_headers.Add ("Content-Type", header); 724 } 725 726 if (cookies != null && cookies.Count != 0){ 727 int n = cookies.Count; 728 for (int i = 0; i < n; i++) 729 write_headers.Add ("Set-Cookie", cookies.Get (i).GetCookieHeaderValue ()); 730 } 731 } 732 WriteHeaders(bool final_flush)733 internal void WriteHeaders (bool final_flush) 734 { 735 if (headers_sent) 736 return; 737 738 // 739 // Flush 740 // 741 if (context != null) { 742 HttpApplication app_instance = context.ApplicationInstance; 743 if (app_instance != null) 744 app_instance.TriggerPreSendRequestHeaders (); 745 } 746 747 headers_sent = true; 748 749 if (cached_response != null) 750 cached_response.SetHeaders (headers); 751 752 // If this page is cached use the cached headers 753 // instead of the standard headers 754 NameValueCollection write_headers; 755 if (cached_headers != null) 756 write_headers = cached_headers; 757 else { 758 write_headers = Headers; 759 AddHeadersNoCache (write_headers, final_flush); 760 } 761 762 if (WorkerRequest != null) 763 WorkerRequest.SendStatus (status_code, StatusDescription); 764 765 if (WorkerRequest != null) { 766 string header_name; 767 string[] values; 768 int header_index; 769 770 for (int i = 0; i < write_headers.Count; i++) { 771 header_name = write_headers.GetKey (i); 772 header_index = HttpWorkerRequest.GetKnownResponseHeaderIndex (header_name); 773 values = write_headers.GetValues (i); 774 if (values == null) 775 continue; 776 777 foreach (string val in values) { 778 if (header_index > -1) 779 WorkerRequest.SendKnownResponseHeader (header_index, val); 780 else 781 WorkerRequest.SendUnknownResponseHeader (header_name, val); 782 } 783 } 784 } 785 } 786 DoFilter(bool close)787 internal void DoFilter (bool close) 788 { 789 if (output_stream.HaveFilter && context != null && context.Error == null) 790 output_stream.ApplyFilter (close); 791 } 792 Flush(bool final_flush)793 internal void Flush (bool final_flush) 794 { 795 if (completed) 796 throw new HttpException ("Server cannot flush a completed response"); 797 798 DoFilter (final_flush); 799 if (!headers_sent){ 800 if (final_flush || status_code != 200) 801 use_chunked = false; 802 } 803 804 bool head = ((context != null) && (context.Request.HttpMethod == "HEAD")); 805 if (suppress_content || head) { 806 if (!headers_sent) 807 WriteHeaders (true); 808 output_stream.Clear (); 809 if (WorkerRequest != null) 810 output_stream.Flush (WorkerRequest, true); // ignore final_flush here. 811 completed = true; 812 return; 813 } 814 completed = final_flush; 815 816 if (!headers_sent) 817 WriteHeaders (final_flush); 818 819 if (context != null) { 820 HttpApplication app_instance = context.ApplicationInstance; 821 if (app_instance != null) 822 app_instance.TriggerPreSendRequestContent (); 823 } 824 825 if (IsCached) 826 cached_response.SetData (output_stream.GetData ()); 827 828 if (WorkerRequest != null) 829 output_stream.Flush (WorkerRequest, final_flush); 830 } 831 Flush()832 public void Flush () 833 { 834 Flush (false); 835 } 836 Pics(string value)837 public void Pics (string value) 838 { 839 AppendHeader ("PICS-Label", value); 840 } 841 Redirect(string url, bool endResponse, int code)842 void Redirect (string url, bool endResponse, int code) 843 { 844 if (url == null) 845 throw new ArgumentNullException ("url"); 846 847 if (headers_sent) 848 throw new HttpException ("Headers have already been sent"); 849 850 if (url.IndexOf ('\n') != -1) 851 throw new ArgumentException ("Redirect URI cannot contain newline characters.", "url"); 852 853 is_request_being_redirected = true; 854 ClearHeaders (); 855 ClearContent (); 856 857 StatusCode = code; 858 url = ApplyAppPathModifier (url); 859 860 bool isFullyQualified; 861 if (StrUtils.StartsWith (url, "http:", true) || 862 StrUtils.StartsWith (url, "https:", true) || 863 StrUtils.StartsWith (url, "file:", true) || 864 StrUtils.StartsWith (url, "ftp:", true)) 865 isFullyQualified = true; 866 else 867 isFullyQualified = false; 868 869 if (!isFullyQualified) { 870 HttpRuntimeSection config = HttpRuntime.Section; 871 if (config != null && config.UseFullyQualifiedRedirectUrl) { 872 var ub = new UriBuilder (context.Request.Url); 873 int qpos = url.IndexOf ('?'); 874 if (qpos == -1) { 875 ub.Path = url; 876 ub.Query = null; 877 } else { 878 ub.Path = url.Substring (0, qpos); 879 ub.Query = url.Substring (qpos + 1); 880 } 881 ub.Fragment = null; 882 ub.Password = null; 883 ub.UserName = null; 884 url = ub.Uri.ToString (); 885 } 886 } 887 888 redirect_location = url; 889 890 // Text for browsers that can't handle location header 891 Write ("<html><head><title>Object moved</title></head><body>\r\n"); 892 Write ("<h2>Object moved to <a href=\"" + url + "\">here</a></h2>\r\n"); 893 Write ("</body><html>\r\n"); 894 895 if (endResponse) 896 End (); 897 is_request_being_redirected = false; 898 } 899 Redirect(string url)900 public void Redirect (string url) 901 { 902 Redirect (url, true); 903 } 904 Redirect(string url, bool endResponse)905 public void Redirect (string url, bool endResponse) 906 { 907 Redirect (url, endResponse, 302); 908 } RedirectPermanent(string url)909 public void RedirectPermanent (string url) 910 { 911 RedirectPermanent (url, true); 912 } 913 RedirectPermanent(string url, bool endResponse)914 public void RedirectPermanent (string url, bool endResponse) 915 { 916 Redirect (url, endResponse, 301); 917 } 918 RedirectToRoute(object routeValues)919 public void RedirectToRoute (object routeValues) 920 { 921 RedirectToRoute ("RedirectToRoute", null, new RouteValueDictionary (routeValues), 302, true); 922 } 923 RedirectToRoute(RouteValueDictionary routeValues)924 public void RedirectToRoute (RouteValueDictionary routeValues) 925 { 926 RedirectToRoute ("RedirectToRoute", null, routeValues, 302, true); 927 } 928 RedirectToRoute(string routeName)929 public void RedirectToRoute (string routeName) 930 { 931 RedirectToRoute ("RedirectToRoute", routeName, null, 302, true); 932 } 933 RedirectToRoute(string routeName, object routeValues)934 public void RedirectToRoute (string routeName, object routeValues) 935 { 936 RedirectToRoute ("RedirectToRoute", routeName, new RouteValueDictionary (routeValues), 302, true); 937 } 938 RedirectToRoute(string routeName, RouteValueDictionary routeValues)939 public void RedirectToRoute (string routeName, RouteValueDictionary routeValues) 940 { 941 RedirectToRoute ("RedirectToRoute", routeName, routeValues, 302, true); 942 } 943 RedirectToRoutePermanent(object routeValues)944 public void RedirectToRoutePermanent (object routeValues) 945 { 946 RedirectToRoute ("RedirectToRoutePermanent", null, new RouteValueDictionary (routeValues), 301, false); 947 } 948 RedirectToRoutePermanent(RouteValueDictionary routeValues)949 public void RedirectToRoutePermanent (RouteValueDictionary routeValues) 950 { 951 RedirectToRoute ("RedirectToRoutePermanent", null, routeValues, 301, false); 952 } 953 RedirectToRoutePermanent(string routeName)954 public void RedirectToRoutePermanent (string routeName) 955 { 956 RedirectToRoute ("RedirectToRoutePermanent", routeName, null, 301, false); 957 } 958 RedirectToRoutePermanent(string routeName, object routeValues)959 public void RedirectToRoutePermanent (string routeName, object routeValues) 960 { 961 RedirectToRoute ("RedirectToRoutePermanent", routeName, new RouteValueDictionary (routeValues), 301, false); 962 } 963 RedirectToRoutePermanent(string routeName, RouteValueDictionary routeValues)964 public void RedirectToRoutePermanent (string routeName, RouteValueDictionary routeValues) 965 { 966 RedirectToRoute ("RedirectToRoutePermanent", routeName, routeValues, 301, false); 967 } 968 RedirectToRoute(string callerName, string routeName, RouteValueDictionary routeValues, int redirectCode, bool endResponse)969 void RedirectToRoute (string callerName, string routeName, RouteValueDictionary routeValues, int redirectCode, bool endResponse) 970 { 971 HttpContext ctx = context ?? HttpContext.Current; 972 HttpRequest req = ctx != null ? ctx.Request : null; 973 974 if (req == null) 975 // Let's emulate .NET 976 throw new NullReferenceException (); 977 978 VirtualPathData vpd = RouteTable.Routes.GetVirtualPath (req.RequestContext, routeName, routeValues); 979 string redirectUrl = vpd != null ? vpd.VirtualPath : null; 980 if (String.IsNullOrEmpty (redirectUrl)) 981 throw new InvalidOperationException ("No matching route found for RedirectToRoute"); 982 983 Redirect (redirectUrl, true, redirectCode); 984 } 985 RemoveOutputCacheItem(string path, string providerName)986 public static void RemoveOutputCacheItem (string path, string providerName) 987 { 988 if (path == null) 989 throw new ArgumentNullException ("path"); 990 991 if (path.Length > 0 && path [0] != '/') 992 throw new ArgumentException ("Invalid path for HttpResponse.RemoveOutputCacheItem: '" + path + "'. An absolute virtual path is expected"); 993 994 OutputCache.RemoveFromProvider (path, providerName); 995 } RemoveOutputCacheItem(string path)996 public static void RemoveOutputCacheItem (string path) 997 { 998 if (path == null) 999 throw new ArgumentNullException ("path"); 1000 1001 if (path.Length == 0) 1002 return; 1003 1004 if (path [0] != '/') 1005 throw new ArgumentException ("'" + path + "' is not an absolute virtual path."); 1006 1007 RemoveOutputCacheItem (path, OutputCache.DefaultProviderName); 1008 } 1009 SetCookie(HttpCookie cookie)1010 public void SetCookie (HttpCookie cookie) 1011 { 1012 AppendCookie (cookie); 1013 } 1014 Write(char ch)1015 public void Write (char ch) 1016 { 1017 TextWriter writer = Output; 1018 // Emulating .NET 1019 if (writer == null) 1020 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required."); 1021 writer.Write (ch); 1022 } 1023 Write(object obj)1024 public void Write (object obj) 1025 { 1026 TextWriter writer = Output; 1027 // Emulating .NET 1028 if (writer == null) 1029 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required."); 1030 if (obj == null) 1031 return; 1032 1033 writer.Write (obj.ToString ()); 1034 } 1035 Write(string s)1036 public void Write (string s) 1037 { 1038 TextWriter writer = Output; 1039 // Emulating .NET 1040 if (writer == null) 1041 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required."); 1042 writer.Write (s); 1043 } 1044 Write(char [] buffer, int index, int count)1045 public void Write (char [] buffer, int index, int count) 1046 { 1047 TextWriter writer = Output; 1048 // Emulating .NET 1049 if (writer == null) 1050 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required."); 1051 writer.Write (buffer, index, count); 1052 } 1053 IsFileSystemDirSeparator(char ch)1054 bool IsFileSystemDirSeparator (char ch) 1055 { 1056 return ch == '\\' || ch == '/'; 1057 } 1058 GetNormalizedFileName(string fn)1059 string GetNormalizedFileName (string fn) 1060 { 1061 if (String.IsNullOrEmpty (fn)) 1062 return fn; 1063 1064 // On Linux we don't change \ to / since filenames with \ are valid. We also 1065 // don't remove drive: designator for the same reason. 1066 int len = fn.Length; 1067 if (len >= 3 && fn [1] == ':' && IsFileSystemDirSeparator (fn [2])) 1068 return Path.GetFullPath (fn); // drive-qualified absolute file path 1069 1070 bool startsWithDirSeparator = IsFileSystemDirSeparator (fn [0]); 1071 if (len >= 2 && startsWithDirSeparator && IsFileSystemDirSeparator (fn [1])) 1072 return Path.GetFullPath (fn); // UNC path 1073 1074 if (!startsWithDirSeparator) { 1075 HttpContext ctx = context ?? HttpContext.Current; 1076 HttpRequest req = ctx != null ? ctx.Request : null; 1077 1078 if (req != null) 1079 return req.MapPath (fn); 1080 } 1081 1082 return fn; // Or should we rather throw? 1083 } 1084 WriteFile(FileStream fs, long offset, long size)1085 internal void WriteFile (FileStream fs, long offset, long size) 1086 { 1087 byte [] buffer = new byte [32*1024]; 1088 1089 if (offset != 0) 1090 fs.Position = offset; 1091 1092 long remain = size; 1093 int n; 1094 while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){ 1095 remain -= n; 1096 output_stream.Write (buffer, 0, n); 1097 } 1098 } 1099 WriteFile(string filename)1100 public void WriteFile (string filename) 1101 { 1102 WriteFile (filename, false); 1103 } 1104 WriteFile(string filename, bool readIntoMemory)1105 public void WriteFile (string filename, bool readIntoMemory) 1106 { 1107 if (filename == null) 1108 throw new ArgumentNullException ("filename"); 1109 1110 string fn = GetNormalizedFileName (filename); 1111 if (readIntoMemory){ 1112 using (FileStream fs = File.OpenRead (fn)) 1113 WriteFile (fs, 0, fs.Length); 1114 } else { 1115 FileInfo fi = new FileInfo (fn); 1116 output_stream.WriteFile (fn, 0, fi.Length); 1117 } 1118 if (buffer) 1119 return; 1120 1121 output_stream.ApplyFilter (false); 1122 Flush (); 1123 } 1124 WriteFile(IntPtr fileHandle, long offset, long size)1125 public void WriteFile (IntPtr fileHandle, long offset, long size) 1126 { 1127 if (offset < 0) 1128 throw new ArgumentNullException ("offset can not be negative"); 1129 if (size < 0) 1130 throw new ArgumentNullException ("size can not be negative"); 1131 1132 if (size == 0) 1133 return; 1134 1135 // Note: this .ctor will throw a SecurityException if the caller 1136 // doesn't have the UnmanagedCode permission 1137 using (FileStream fs = new FileStream (fileHandle, FileAccess.Read)) 1138 WriteFile (fs, offset, size); 1139 1140 if (buffer) 1141 return; 1142 output_stream.ApplyFilter (false); 1143 Flush (); 1144 } 1145 WriteFile(string filename, long offset, long size)1146 public void WriteFile (string filename, long offset, long size) 1147 { 1148 if (filename == null) 1149 throw new ArgumentNullException ("filename"); 1150 if (offset < 0) 1151 throw new ArgumentNullException ("offset can not be negative"); 1152 if (size < 0) 1153 throw new ArgumentNullException ("size can not be negative"); 1154 1155 if (size == 0) 1156 return; 1157 1158 FileStream fs = File.OpenRead (filename); 1159 WriteFile (fs, offset, size); 1160 1161 if (buffer) 1162 return; 1163 1164 output_stream.ApplyFilter (false); 1165 Flush (); 1166 } 1167 WriteSubstitution(HttpResponseSubstitutionCallback callback)1168 public void WriteSubstitution (HttpResponseSubstitutionCallback callback) 1169 { 1170 // Emulation of .NET behavior 1171 if (callback == null) 1172 throw new NullReferenceException (); 1173 1174 object target = callback.Target; 1175 if (target != null && target.GetType () == typeof (Control)) 1176 throw new ArgumentException ("callback"); 1177 1178 string s = callback (context); 1179 if (!IsCached) { 1180 Write (s); 1181 return; 1182 } 1183 1184 Cache.Cacheability = HttpCacheability.Server; 1185 Flush (); 1186 if (WorkerRequest == null) 1187 Write (s); // better this than nothing 1188 else { 1189 byte[] bytes = WebEncoding.ResponseEncoding.GetBytes (s); 1190 WorkerRequest.SendResponseFromMemory (bytes, bytes.Length); 1191 } 1192 1193 cached_response.SetData (callback); 1194 } 1195 1196 // 1197 // Like WriteFile, but never buffers, so we manually Flush here 1198 // TransmitFile(string filename)1199 public void TransmitFile (string filename) 1200 { 1201 if (filename == null) 1202 throw new ArgumentNullException ("filename"); 1203 1204 TransmitFile (filename, false); 1205 } 1206 TransmitFile(string filename, bool final_flush)1207 internal void TransmitFile (string filename, bool final_flush) 1208 { 1209 FileInfo fi = new FileInfo (filename); 1210 using (Stream s = fi.OpenRead ()) { } // Just check if we can read. 1211 output_stream.WriteFile (filename, 0, fi.Length); 1212 output_stream.ApplyFilter (final_flush); 1213 Flush (final_flush); 1214 } 1215 TransmitFile(string filename, long offset, long length)1216 public void TransmitFile (string filename, long offset, long length) 1217 { 1218 output_stream.WriteFile (filename, offset, length); 1219 output_stream.ApplyFilter (false); 1220 Flush (false); 1221 } 1222 TransmitFile(VirtualFile vf)1223 internal void TransmitFile (VirtualFile vf) 1224 { 1225 TransmitFile (vf, false); 1226 } 1227 1228 const int bufLen = 65535; TransmitFile(VirtualFile vf, bool final_flush)1229 internal void TransmitFile (VirtualFile vf, bool final_flush) 1230 { 1231 if (vf == null) 1232 throw new ArgumentNullException ("vf"); 1233 1234 if (vf is DefaultVirtualFile) { 1235 TransmitFile (HostingEnvironment.MapPath (vf.VirtualPath), final_flush); 1236 return; 1237 } 1238 1239 byte[] buf = new byte [bufLen]; 1240 using (Stream s = vf.Open ()) { 1241 int readBytes; 1242 while ((readBytes = s.Read (buf, 0, bufLen)) > 0) { 1243 output_stream.Write (buf, 0, readBytes); 1244 output_stream.ApplyFilter (final_flush); 1245 Flush (false); 1246 } 1247 if (final_flush) 1248 Flush (true); 1249 } 1250 } 1251 1252 #region Session state support SetAppPathModifier(string app_modifier)1253 internal void SetAppPathModifier (string app_modifier) 1254 { 1255 app_path_mod = app_modifier; 1256 } 1257 #endregion 1258 1259 #region Cache Support SetCachedHeaders(NameValueCollection headers)1260 internal void SetCachedHeaders (NameValueCollection headers) 1261 { 1262 cached_headers = headers; 1263 1264 } 1265 1266 internal bool IsCached { 1267 get { return cached_response != null; } 1268 set { 1269 if (value) 1270 cached_response = new CachedRawResponse (cache_policy); 1271 else 1272 cached_response = null; 1273 } 1274 } 1275 1276 public HttpCachePolicy Cache { 1277 get { 1278 if (cache_policy == null) 1279 cache_policy = new HttpCachePolicy (); 1280 1281 return cache_policy; 1282 } 1283 } 1284 GetCachedResponse()1285 internal CachedRawResponse GetCachedResponse () 1286 { 1287 if (cached_response != null) { 1288 cached_response.StatusCode = StatusCode; 1289 cached_response.StatusDescription = StatusDescription; 1290 } 1291 1292 return cached_response; 1293 } 1294 1295 // 1296 // This is one of the old ASP compatibility methods, the real cache 1297 // control is in the Cache property, and this is a second class citizen 1298 // 1299 public string CacheControl { 1300 set { 1301 if (value == null || value == "") { 1302 Cache.SetCacheability (HttpCacheability.NoCache); 1303 user_cache_control = null; 1304 } else if (String.Compare (value, "public", true, Helpers.InvariantCulture) == 0) { 1305 Cache.SetCacheability (HttpCacheability.Public); 1306 user_cache_control = "public"; 1307 } else if (String.Compare (value, "private", true, Helpers.InvariantCulture) == 0) { 1308 Cache.SetCacheability (HttpCacheability.Private); 1309 user_cache_control = "private"; 1310 } else if (String.Compare (value, "no-cache", true, Helpers.InvariantCulture) == 0) { 1311 Cache.SetCacheability (HttpCacheability.NoCache); 1312 user_cache_control = "no-cache"; 1313 } else 1314 throw new ArgumentException ("CacheControl property only allows `public', " + 1315 "`private' or no-cache, for different uses, use " + 1316 "Response.AppendHeader"); 1317 } 1318 1319 get { return (user_cache_control != null) ? user_cache_control : "private"; } 1320 } 1321 #endregion 1322 GetOutputByteCount()1323 internal int GetOutputByteCount () 1324 { 1325 return output_stream.GetTotalLength (); 1326 } 1327 ReleaseResources()1328 internal void ReleaseResources () 1329 { 1330 if (output_stream != null) 1331 output_stream.ReleaseResources (true); 1332 if (completed) 1333 return; 1334 1335 Close (); 1336 completed = true; 1337 } 1338 } 1339 1340 static class FlagEnd 1341 { 1342 public static readonly object Value = new object (); 1343 } 1344 } 1345 1346