Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Web / HttpResponse.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="HttpResponse.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 /*
8  * Response intrinsic
9  */
10 namespace System.Web {
11
12     using System.Text;
13     using System.Threading;
14     using System.Threading.Tasks;
15     using System.Runtime.Serialization;
16     using System.IO;
17     using System.Collections;
18     using System.Collections.Specialized;
19     using System.Globalization;
20     using System.Web.Util;
21     using System.Web.Hosting;
22     using System.Web.Caching;
23     using System.Web.Configuration;
24     using System.Web.Routing;
25     using System.Web.UI;
26     using System.Configuration;
27     using System.Security.Permissions;
28     using System.Web.Management;
29     using System.Diagnostics.CodeAnalysis;
30
31
32     /// <devdoc>
33     ///    <para>Used in HttpResponse.WriteSubstitution.</para>
34     /// </devdoc>
35     public delegate String HttpResponseSubstitutionCallback(HttpContext context);
36
37
38     /// <devdoc>
39     ///    <para> Enables type-safe server to browser communication. Used to
40     ///       send Http output to a client.</para>
41     /// </devdoc>
42     public sealed class HttpResponse {
43         private HttpWorkerRequest _wr;              // some response have HttpWorkerRequest
44         private HttpContext _context;               // context
45         private HttpWriter _httpWriter;             // and HttpWriter
46         private TextWriter _writer;                 // others just have Writer
47
48         private HttpHeaderCollection _headers;      // response header collection (IIS7+)
49
50         private bool _headersWritten;
51         private bool _completed;    // after final flush
52         private bool _ended;        // after response.end or execute url
53         private bool _endRequiresObservation; // whether there was a pending call to Response.End that requires observation
54         private bool _flushing;
55         private bool _clientDisconnected;
56         private bool _filteringCompleted;
57         private bool _closeConnectionAfterError;
58
59         // simple properties
60
61         private int         _statusCode = 200;
62         private String      _statusDescription;
63         private bool        _bufferOutput = true;
64         private String      _contentType = "text/html";
65         private String      _charSet;
66         private bool        _customCharSet;
67         private bool        _contentLengthSet;
68         private String      _redirectLocation;
69         private bool        _redirectLocationSet;
70         private Encoding    _encoding;
71         private Encoder     _encoder; // cached encoder for the encoding
72         private Encoding    _headerEncoding; // encoding for response headers, default utf-8
73         private bool        _cacheControlHeaderAdded;
74         private HttpCachePolicy _cachePolicy;
75         private ArrayList   _cacheHeaders;
76         private bool        _suppressHeaders;
77         private bool        _suppressContentSet;
78         private bool        _suppressContent;
79         private string      _appPathModifier;
80         private bool        _isRequestBeingRedirected;
81         private bool        _useAdaptiveError;
82         private bool        _handlerHeadersGenerated;
83         private bool        _sendCacheControlHeader;
84
85         // complex properties
86
87         private ArrayList               _customHeaders;
88         private HttpCookieCollection    _cookies;
89         #pragma warning disable 0649
90         private ResponseDependencyList  _fileDependencyList;
91         private ResponseDependencyList  _virtualPathDependencyList;
92         private ResponseDependencyList  _cacheItemDependencyList;
93         #pragma warning restore 0649
94         private CacheDependency[]       _userAddedDependencies;
95         private CacheDependency         _cacheDependencyForResponse;
96         
97         private ErrorFormatter          _overrideErrorFormatter;
98
99         // cache properties
100         int         _expiresInMinutes;
101         bool        _expiresInMinutesSet;
102         DateTime    _expiresAbsolute;
103         bool        _expiresAbsoluteSet;
104         string      _cacheControl;
105
106         private bool        _statusSet;
107         private int         _subStatusCode;
108         private bool        _versionHeaderSent;
109
110         // These flags for content-type are only used in integrated mode.
111         // DevDivBugs 146983: Content-Type should not be sent when the resonse buffers are empty
112         // DevDivBugs 195148: need to send Content-Type when the handler is managed and the response buffers are non-empty 
113         // Dev10 750934: need to send Content-Type when explicitly set by managed caller
114         private bool        _contentTypeSetByManagedCaller;
115         private bool        _contentTypeSetByManagedHandler;
116
117         // chunking
118         bool        _transferEncodingSet;
119         bool        _chunked;
120
121         // OnSendingHeaders pseudo-event
122         private SubscriptionQueue<Action<HttpContext>> _onSendingHeadersSubscriptionQueue = new SubscriptionQueue<Action<HttpContext>>();
123
124         // mobile redirect properties
125         internal static readonly String RedirectQueryStringVariable = "__redir";
126         internal static readonly String RedirectQueryStringValue = "1";
127         internal static readonly String RedirectQueryStringAssignment = RedirectQueryStringVariable + "=" + RedirectQueryStringValue;
128
129         private static readonly String _redirectQueryString = "?" + RedirectQueryStringAssignment;
130         private static readonly String _redirectQueryStringInline = RedirectQueryStringAssignment + "&";
131
132         internal static event EventHandler Redirecting;
133
134         internal HttpContext Context {
135             get { return _context; }
136             set { _context = value; }
137         }
138
139         internal HttpRequest Request {
140             get {
141                 if (_context == null)
142                     return null;
143                 return _context.Request;
144             }
145         }
146
147         /*
148          * Internal package visible constructor to create responses that
149          * have HttpWorkerRequest
150          *
151          * @param wr Worker Request
152          */
153         internal HttpResponse(HttpWorkerRequest wr, HttpContext context) {
154             _wr = wr;
155             _context = context;
156             // HttpWriter is created in InitResponseWriter
157         }
158
159         // Public constructor for responses that go to an arbitrary writer
160         // Initializes a new instance of the <see langword='HttpResponse'/> class.</para>
161         public HttpResponse(TextWriter writer) {
162             _wr = null;
163             _httpWriter = null;
164             _writer = writer;
165         }
166
167         private bool UsingHttpWriter {
168             get {
169                 return (_httpWriter != null && _writer == _httpWriter);
170             }
171         }
172
173         internal void SetAllocatorProvider(IAllocatorProvider allocator) {
174             if (_httpWriter != null) {
175                 _httpWriter.AllocatorProvider = allocator;
176             }
177         }
178
179         /*
180          *  Cleanup code
181          */
182         internal void Dispose() {
183             // recycle buffers
184             if (_httpWriter != null)
185                 _httpWriter.RecycleBuffers();
186             // recycle dependencies
187             if (_cacheDependencyForResponse != null) {
188                 _cacheDependencyForResponse.Dispose();
189                 _cacheDependencyForResponse = null;
190             }
191             if (_userAddedDependencies != null) {
192                 foreach (CacheDependency dep in _userAddedDependencies) {
193                     dep.Dispose();
194                 }
195                 _userAddedDependencies = null;
196             }
197         }
198
199         internal void InitResponseWriter() {
200             if (_httpWriter == null) {
201                 _httpWriter = new HttpWriter(this);
202
203                 _writer = _httpWriter;
204             }
205         }
206
207         //
208         // Private helper methods
209         //
210
211         private void AppendHeader(HttpResponseHeader h) {
212             if (_customHeaders == null)
213                 _customHeaders = new ArrayList();
214             _customHeaders.Add(h);
215             if (_cachePolicy != null && StringUtil.EqualsIgnoreCase("Set-Cookie", h.Name)) {
216                 _cachePolicy.SetHasSetCookieHeader();
217             }
218         }
219
220         public bool HeadersWritten {
221             get { return _headersWritten; }
222             internal set { _headersWritten = value; }
223         }
224
225         internal ArrayList GenerateResponseHeadersIntegrated(bool forCache) {
226             ArrayList headers = new ArrayList();
227             HttpHeaderCollection responseHeaders = Headers as HttpHeaderCollection;
228             int headerId = 0;
229
230             // copy all current response headers
231             foreach(String key in responseHeaders)
232             {
233                 // skip certain headers that the cache does not cache
234                 // this is based on the cache headers saved separately in AppendHeader
235                 // and not generated in GenerateResponseHeaders in ISAPI mode
236                 headerId = HttpWorkerRequest.GetKnownResponseHeaderIndex(key);
237                 if (headerId >= 0 && forCache &&
238                      (headerId == HttpWorkerRequest.HeaderServer ||
239                       headerId == HttpWorkerRequest.HeaderSetCookie ||
240                       headerId == HttpWorkerRequest.HeaderCacheControl ||
241                       headerId == HttpWorkerRequest.HeaderExpires ||
242                       headerId == HttpWorkerRequest.HeaderLastModified ||
243                       headerId == HttpWorkerRequest.HeaderEtag ||
244                       headerId == HttpWorkerRequest.HeaderVary)) {
245                     continue;
246                 }
247
248                 if ( headerId >= 0 ) {
249                     headers.Add(new HttpResponseHeader(headerId, responseHeaders[key]));
250                 }
251                 else {
252                     headers.Add(new HttpResponseHeader(key, responseHeaders[key]));
253                 }
254             }
255
256             return headers;
257         }
258
259         internal void GenerateResponseHeadersForCookies()
260         {
261             if (_cookies == null || (_cookies.Count == 0 && !_cookies.Changed))
262                 return; // no cookies exist
263
264             HttpHeaderCollection headers = Headers as HttpHeaderCollection;
265             HttpResponseHeader cookieHeader = null;
266             HttpCookie cookie = null;
267             bool needToReset = false;
268
269             // Go through all cookies, and check whether any have been added
270             // or changed.  If a cookie was added, we can simply generate a new
271             // set cookie header for it.  If the cookie collection has been
272             // changed (cleared or cookies removed), or an existing cookie was
273             // changed, we have to regenerate all Set-Cookie headers due to an IIS
274             // limitation that prevents us from being able to delete specific
275             // Set-Cookie headers for items that changed.
276             if (!_cookies.Changed)
277             {
278                 for(int c = 0; c < _cookies.Count; c++)
279                 {
280                     cookie = _cookies[c];
281                     if (cookie.Added) {
282                         // if a cookie was added, we generate a Set-Cookie header for it
283                         cookieHeader = cookie.GetSetCookieHeader(_context);
284                         headers.SetHeader(cookieHeader.Name, cookieHeader.Value, false);
285                         cookie.Added = false;
286                         cookie.Changed = false;
287                     }
288                     else if (cookie.Changed) {
289                         // if a cookie has changed, we need to clear all cookie
290                         // headers and re-write them all since we cant delete
291                         // specific existing cookies
292                         needToReset = true;
293                         break;
294                     }
295                 }
296             }
297
298
299             if (_cookies.Changed || needToReset)
300             {
301                 // delete all set cookie headers
302                 headers.Remove("Set-Cookie");
303
304                 // write all the cookies again
305                 for(int c = 0; c < _cookies.Count; c++)
306                 {
307                     // generate a Set-Cookie header for each cookie
308                     cookie = _cookies[c];
309                     cookieHeader = cookie.GetSetCookieHeader(_context);
310                     headers.SetHeader(cookieHeader.Name, cookieHeader.Value, false);
311                     cookie.Added = false;
312                     cookie.Changed = false;
313                 }
314
315                 _cookies.Changed = false;
316             }
317         }
318
319         internal void GenerateResponseHeadersForHandler()
320         {
321             if ( !(_wr is IIS7WorkerRequest) ) {
322                 return;
323             }
324
325             String versionHeader = null;
326
327             // Generate the default headers associated with an ASP.NET handler
328             if (!_headersWritten && !_handlerHeadersGenerated) {
329                 try {
330                     // The "sendCacheControlHeader" is default to true, but a false setting in either
331                     // the <httpRuntime> section (legacy) or the <outputCache> section (current) will disable
332                     // sending of that header.
333                     RuntimeConfig config = RuntimeConfig.GetLKGConfig(_context);
334
335                     HttpRuntimeSection runtimeConfig = config.HttpRuntime;
336                     if (runtimeConfig != null) {
337                         versionHeader = runtimeConfig.VersionHeader;
338                         _sendCacheControlHeader = runtimeConfig.SendCacheControlHeader;
339                     }
340
341                     OutputCacheSection outputCacheConfig = config.OutputCache;
342                     if (outputCacheConfig != null) {
343                         _sendCacheControlHeader &= outputCacheConfig.SendCacheControlHeader;
344                     }
345
346                     // DevDiv #406078: Need programmatic way of disabling "Cache-Control: private" response header.
347                     if (SuppressDefaultCacheControlHeader) {
348                         _sendCacheControlHeader = false;
349                     }
350
351                     // Ensure that cacheability is set to cache-control: private
352                     // if it is not explicitly set
353                     if (_sendCacheControlHeader && !_cacheControlHeaderAdded) {
354                         Headers.Set("Cache-Control", "private");
355                     }
356
357                     // set the version header
358                     if (!String.IsNullOrEmpty(versionHeader)) {
359                         Headers.Set("X-AspNet-Version", versionHeader);
360                     }
361
362                     // Force content-type generation
363                     _contentTypeSetByManagedHandler = true;
364                 }
365                 finally {
366                     _handlerHeadersGenerated = true;
367                 }
368             }
369         }
370
371         internal ArrayList GenerateResponseHeaders(bool forCache) {
372             ArrayList   headers = new ArrayList();
373             bool sendCacheControlHeader = HttpRuntimeSection.DefaultSendCacheControlHeader;
374
375             // ASP.NET version header
376             if (!forCache ) {
377
378                 if (!_versionHeaderSent) {
379                     String versionHeader = null;
380
381                     // The "sendCacheControlHeader" is default to true, but a false setting in either
382                     // the <httpRuntime> section (legacy) or the <outputCache> section (current) will disable
383                     // sending of that header.
384                     RuntimeConfig config = RuntimeConfig.GetLKGConfig(_context);
385
386                     HttpRuntimeSection runtimeConfig = config.HttpRuntime;
387                     if (runtimeConfig != null) {
388                         versionHeader = runtimeConfig.VersionHeader;
389                         sendCacheControlHeader = runtimeConfig.SendCacheControlHeader;
390                     }
391
392                     OutputCacheSection outputCacheConfig = config.OutputCache;
393                     if (outputCacheConfig != null) {
394                         sendCacheControlHeader &= outputCacheConfig.SendCacheControlHeader;
395                     }
396
397                     if (!String.IsNullOrEmpty(versionHeader)) {
398                         headers.Add(new HttpResponseHeader("X-AspNet-Version", versionHeader));
399                     }
400
401                     _versionHeaderSent = true;
402                 }
403             }
404
405             // custom headers
406             if (_customHeaders != null) {
407                 int numCustomHeaders = _customHeaders.Count;
408                 for (int i = 0; i < numCustomHeaders; i++)
409                     headers.Add(_customHeaders[i]);
410             }
411
412             // location of redirect
413             if (_redirectLocation != null) {
414                 headers.Add(new HttpResponseHeader(HttpWorkerRequest.HeaderLocation, _redirectLocation));
415             }
416
417             // don't include headers that the cache changes or omits on a cache hit
418             if (!forCache) {
419                 // cookies
420                 if (_cookies != null) {
421                     int numCookies = _cookies.Count;
422
423                     for (int i = 0; i < numCookies; i++) {
424                         headers.Add(_cookies[i].GetSetCookieHeader(Context));
425                     }
426                 }
427
428                 // cache policy
429                 if (_cachePolicy != null && _cachePolicy.IsModified()) {
430                     _cachePolicy.GetHeaders(headers, this);
431                 }
432                 else {
433                     if (_cacheHeaders != null) {
434                         headers.AddRange(_cacheHeaders);
435                     }
436
437                     /*
438                      * Ensure that cacheability is set to cache-control: private
439                      * if it is not explicitly set.
440                      */
441                     if (!_cacheControlHeaderAdded && sendCacheControlHeader) {
442                         headers.Add(new HttpResponseHeader(HttpWorkerRequest.HeaderCacheControl, "private"));
443                     }
444                 }
445             }
446
447             //
448             // content type
449             //
450             if ( _statusCode != 204 && _contentType != null) {
451                 String contentType = AppendCharSetToContentType( _contentType );
452                 headers.Add(new HttpResponseHeader(HttpWorkerRequest.HeaderContentType, contentType));
453             }
454
455             // done
456             return headers;
457         }
458
459         internal string AppendCharSetToContentType(string contentType)
460         {
461             String newContentType = contentType;
462
463             // charset=xxx logic -- append if
464             //      not there already and
465             //          custom set or response encoding used by http writer to convert bytes to chars
466             if (_customCharSet || (_httpWriter != null && _httpWriter.ResponseEncodingUsed)) {
467                 if (contentType.IndexOf("charset=", StringComparison.Ordinal) < 0) {
468                     String charset = Charset;
469                     if (charset.Length > 0) { // not suppressed
470                         newContentType = contentType + "; charset=" + charset;
471                     }
472                 }
473             }
474
475             return newContentType;
476         }
477
478         internal bool UseAdaptiveError {
479             get {
480                 return _useAdaptiveError;
481             }
482             set {
483                 _useAdaptiveError = value;
484             }
485         }
486
487         private void WriteHeaders() {
488             if (_wr == null)
489                 return;
490
491              // Fire pre-send headers event
492
493             if (_context != null && _context.ApplicationInstance != null) {
494                 _context.ApplicationInstance.RaiseOnPreSendRequestHeaders();
495             }
496
497             // status
498             // VSWhidbey 270635: We need to reset the status code for mobile devices.
499             if (UseAdaptiveError) {
500
501                 // VSWhidbey 288054: We should change the status code for cases
502                 // that cannot be handled by mobile devices
503                 // 4xx for Client Error and 5xx for Server Error in HTTP spec
504                 int statusCode = StatusCode;
505                 if (statusCode >= 400 && statusCode < 600) {
506                     this.StatusCode = 200;
507                 }
508             }
509
510             // generate headers before we touch the WorkerRequest since header generation might fail,
511             // and we don't want to have touched the WR if this happens
512             ArrayList headers = GenerateResponseHeaders(false);
513
514             _wr.SendStatus(this.StatusCode, this.StatusDescription);
515
516             // headers encoding
517
518             // unicode messes up the response badly
519             Debug.Assert(!this.HeaderEncoding.Equals(Encoding.Unicode));
520             _wr.SetHeaderEncoding(this.HeaderEncoding);
521
522             // headers
523             HttpResponseHeader header = null;
524             int n = (headers != null) ? headers.Count : 0;
525             for (int i = 0; i < n; i++)
526             {
527                 header = headers[i] as HttpResponseHeader;
528                 header.Send(_wr);
529             }
530         }
531
532         internal int GetBufferedLength() {
533             // if length is greater than Int32.MaxValue, Convert.ToInt32 will throw.
534             // This is okay until we support large response sizes
535             return (_httpWriter != null) ? Convert.ToInt32(_httpWriter.GetBufferedLength()) : 0;
536         }
537
538         private static byte[] s_chunkSuffix = new byte[2] { (byte)'\r', (byte)'\n'};
539         private static byte[] s_chunkEnd    = new byte[5] { (byte)'0',  (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n'};
540
541         private void Flush(bool finalFlush, bool async = false) {
542             // Already completed or inside Flush?
543             if (_completed || _flushing)
544                 return;
545
546             // Special case for non HTTP Writer
547             if (_httpWriter == null) {
548                 _writer.Flush();
549                 return;
550             }
551
552             // Avoid recursive flushes
553             _flushing = true;
554
555             bool needToClearBuffers = false;
556             try {
557
558                 IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
559                 if (iis7WorkerRequest != null) {
560                     // generate the handler headers if flushing
561                     GenerateResponseHeadersForHandler();
562
563                     // Push buffers across to native side and explicitly flush.
564                     // IIS7 handles the chunking as necessary so we can omit that logic
565                     UpdateNativeResponse(true /*sendHeaders*/);
566                     
567                     if (!async) {
568                         try {
569                             // force a synchronous send
570                             iis7WorkerRequest.ExplicitFlush();
571                         }
572                         finally {
573                             // always set after flush, successful or not
574                             _headersWritten = true;
575                         }
576                     }
577
578                     return;
579                 }
580
581                 long bufferedLength = 0;
582
583                 //
584                 // Headers
585                 //
586
587                 if (!_headersWritten) {
588                     if (!_suppressHeaders && !_clientDisconnected) {
589                         EnsureSessionStateIfNecessary();
590
591                         if (finalFlush) {
592                             bufferedLength = _httpWriter.GetBufferedLength();
593
594                             // suppress content-type for empty responses
595                             if (!_contentLengthSet && bufferedLength == 0 && _httpWriter != null)
596                                 _contentType = null;
597
598                             SuppressCachingCookiesIfNecessary();
599
600                             // generate response headers
601                             WriteHeaders();
602
603                             // recalculate as sending headers might change it (PreSendHeaders)
604                             bufferedLength = _httpWriter.GetBufferedLength();
605
606                             // Calculate content-length if not set explicitely
607                             // WOS #1380818: Content-Length should not be set for response with 304 status (HTTP.SYS doesn't, and HTTP 1.1 spec implies it)
608                             if (!_contentLengthSet && _statusCode != 304)
609                                 _wr.SendCalculatedContentLength(bufferedLength);
610                         }
611                         else {
612                             // Check if need chunking for HTTP/1.1
613                             if (!_contentLengthSet && !_transferEncodingSet && _statusCode == 200) {
614                                 String protocol = _wr.GetHttpVersion();
615
616                                 if (protocol != null && protocol.Equals("HTTP/1.1")) {
617                                     AppendHeader(new HttpResponseHeader(HttpWorkerRequest.HeaderTransferEncoding, "chunked"));
618                                     _chunked = true;
619                                 }
620
621                                 bufferedLength = _httpWriter.GetBufferedLength();
622                             }
623
624                             WriteHeaders();
625                         }
626                     }
627
628                     _headersWritten = true;
629                 }
630                 else {
631                     bufferedLength = _httpWriter.GetBufferedLength();
632                 }
633
634                 //
635                 // Filter and recalculate length if not done already
636                 //
637
638                 if (!_filteringCompleted) {
639                     _httpWriter.Filter(false);
640                     bufferedLength = _httpWriter.GetBufferedLength();
641                 }
642
643                 //
644                 // Content
645                 //
646
647                 // suppress HEAD content unless overriden
648                 if (!_suppressContentSet && Request != null && Request.HttpVerb == HttpVerb.HEAD)
649                     _suppressContent = true;
650
651                 if (_suppressContent || _ended) {
652                     _httpWriter.ClearBuffers();
653                     bufferedLength = 0;
654                 }
655
656                 if (!_clientDisconnected) {
657                     // Fire pre-send request event
658                     if (_context != null && _context.ApplicationInstance != null)
659                         _context.ApplicationInstance.RaiseOnPreSendRequestContent();
660
661                     if (_chunked) {
662                         if (bufferedLength > 0) {
663                             byte[] chunkPrefix = Encoding.ASCII.GetBytes(Convert.ToString(bufferedLength, 16) + "\r\n");
664                             _wr.SendResponseFromMemory(chunkPrefix, chunkPrefix.Length);
665
666                             _httpWriter.Send(_wr);
667
668                             _wr.SendResponseFromMemory(s_chunkSuffix, s_chunkSuffix.Length);
669                         }
670
671                         if (finalFlush)
672                             _wr.SendResponseFromMemory(s_chunkEnd, s_chunkEnd.Length);
673                     }
674                     else {
675                         _httpWriter.Send(_wr);
676                     }
677                     
678                     if (!async) {
679                         needToClearBuffers = !finalFlush;
680                         _wr.FlushResponse(finalFlush);
681                     }
682                     _wr.UpdateResponseCounters(finalFlush, (int)bufferedLength);
683                 }
684             }
685             finally {
686                 _flushing = false;
687
688                 // Remember if completed
689                 if (finalFlush && _headersWritten)
690                     _completed = true;
691
692                 // clear buffers even if FlushResponse throws
693                 if (needToClearBuffers)
694                     _httpWriter.ClearBuffers();
695             }
696         }
697
698         internal void FinalFlushAtTheEndOfRequestProcessing() {
699             FinalFlushAtTheEndOfRequestProcessing(false);
700         }
701
702         internal void FinalFlushAtTheEndOfRequestProcessing(bool needPipelineCompletion) {
703                 Flush(true);
704         }
705
706         // Returns true if the HttpWorkerRequest supports asynchronous flush; otherwise false.
707         public bool SupportsAsyncFlush {
708             get {
709                 return (_wr != null && _wr.SupportsAsyncFlush);
710             }
711         }
712
713         // Sends the currently buffered response to the client.  If the underlying HttpWorkerRequest
714         // supports asynchronous flush and this method is called from an asynchronous module event
715         // or asynchronous handler, then the send will be performed asynchronously.  Otherwise the
716         // implementation resorts to a synchronous flush.  The HttpResponse.SupportsAsyncFlush property 
717         // returns the value of HttpWorkerRequest.SupportsAsyncFlush.  Asynchronous flush is supported
718         // for IIS 6.0 and higher.
719         public IAsyncResult BeginFlush(AsyncCallback callback, Object state) {
720             if (_completed)
721                 throw new HttpException(SR.GetString(SR.Cannot_flush_completed_response));
722
723             // perform async flush if it is supported
724             if (_wr != null && _wr.SupportsAsyncFlush && !_context.IsInCancellablePeriod) {
725                 Flush(false, true);
726                 return _wr.BeginFlush(callback, state);
727             }
728
729             // perform a [....] flush since async is not supported
730             FlushAsyncResult ar = new FlushAsyncResult(callback, state);
731             try {
732                 Flush(false);
733             }
734             catch(Exception e) {
735                 ar.SetError(e);
736             }
737             ar.Complete(0, HResults.S_OK, IntPtr.Zero, synchronous: true);
738             return ar;
739         }
740
741         // Finish an asynchronous flush.
742         public void EndFlush(IAsyncResult asyncResult) {
743             // finish async flush if it is supported
744             if (_wr != null && _wr.SupportsAsyncFlush && !_context.IsInCancellablePeriod) {
745                 // integrated mode doesn't set this until after ExplicitFlush is called,
746                 // but classic mode sets it after WriteHeaders is called
747                 _headersWritten = true;
748                 if (!(_wr is IIS7WorkerRequest)) {
749                     _httpWriter.ClearBuffers();
750                 }
751                 _wr.EndFlush(asyncResult);
752                 return;
753             }
754             
755             // finish [....] flush since async is not supported
756             if (asyncResult == null)
757                 throw new ArgumentNullException("asyncResult");
758             FlushAsyncResult ar = asyncResult as FlushAsyncResult;
759             if (ar == null)
760                 throw new ArgumentException(null, "asyncResult");
761             ar.ReleaseWaitHandleWhenSignaled();
762             if (ar.Error != null) {
763                 ar.Error.Throw();
764             }
765         }
766
767         public Task FlushAsync() {
768             return Task.Factory.FromAsync(BeginFlush, EndFlush, state: null);
769         }
770
771         // WOS 1555777: kernel cache support
772         // If the response can be kernel cached, return the kernel cache key;
773         // otherwise return null.  The kernel cache key is used to invalidate
774         // the entry if a dependency changes or the item is flushed from the
775         // managed cache for any reason.
776         internal String SetupKernelCaching(String originalCacheUrl) {
777             // don't kernel cache if we have a cookie header
778             if (_cookies != null && _cookies.Count != 0) {
779                 _cachePolicy.SetHasSetCookieHeader();
780                 return null;
781             }
782
783             bool enableKernelCacheForVaryByStar = IsKernelCacheEnabledForVaryByStar();
784
785             // check cache policy
786             if (!_cachePolicy.IsKernelCacheable(Request, enableKernelCacheForVaryByStar)) {
787                 return null;
788             }
789
790             // check configuration if the kernel mode cache is enabled
791             HttpRuntimeSection runtimeConfig = RuntimeConfig.GetLKGConfig(_context).HttpRuntime;
792             if (runtimeConfig == null || !runtimeConfig.EnableKernelOutputCache) {
793                 return null;
794             }
795
796             double seconds = (_cachePolicy.UtcGetAbsoluteExpiration() - DateTime.UtcNow).TotalSeconds;
797             if (seconds <= 0) {
798                 return null;
799             }
800
801             int secondsToLive = seconds < Int32.MaxValue ? (int) seconds : Int32.MaxValue;
802             string kernelCacheUrl = _wr.SetupKernelCaching(secondsToLive, originalCacheUrl, enableKernelCacheForVaryByStar);
803
804             if (kernelCacheUrl != null) {
805                 // Tell cache policy not to use max-age as kernel mode cache doesn't update it
806                 _cachePolicy.SetNoMaxAgeInCacheControl();
807             }
808
809             return kernelCacheUrl;
810         }
811
812         /*
813          * Disable kernel caching for this response.  If kernel caching is not supported, this method
814          * returns without performing any action.
815          */
816         public void DisableKernelCache() {
817             if (_wr == null) {
818                 return;
819             }
820
821             _wr.DisableKernelCache();
822         }
823
824         /*
825          * Disable IIS user-mode caching for this response.  If IIS user-mode caching is not supported, this method
826          * returns without performing any action.
827          */
828         public void DisableUserCache() {
829             if (_wr == null) {
830                 return;
831             }
832
833             _wr.DisableUserCache();
834         }
835
836         private bool IsKernelCacheEnabledForVaryByStar()
837         {
838             OutputCacheSection outputCacheConfig = RuntimeConfig.GetAppConfig().OutputCache;
839             return (_cachePolicy.IsVaryByStar && outputCacheConfig.EnableKernelCacheForVaryByStar);
840         }
841
842         internal void FilterOutput() {
843             if(_filteringCompleted) {
844                 return;
845             }
846
847             try {
848                 if (UsingHttpWriter) {
849                     IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
850                     if (iis7WorkerRequest != null) {
851                         _httpWriter.FilterIntegrated(true, iis7WorkerRequest);
852                     }
853                     else {
854                         _httpWriter.Filter(true);
855                     }
856                 }
857             }
858             finally {
859                 _filteringCompleted = true;
860             }
861         }
862
863         /// <devdoc>
864         /// Prevents any other writes to the Response
865         /// </devdoc>
866         internal void IgnoreFurtherWrites() {
867             if (UsingHttpWriter) {
868                 _httpWriter.IgnoreFurtherWrites();
869             }
870         }
871
872         /*
873          * Is the entire response buffered so far
874          */
875         internal bool IsBuffered() {
876             return !_headersWritten && UsingHttpWriter;
877         }
878
879         //  Expose cookie collection to request
880         //    Gets the HttpCookie collection sent by the current request.</para>
881         public HttpCookieCollection Cookies {
882             get {
883                 if (_cookies == null)
884                     _cookies = new HttpCookieCollection(this, false);
885
886                 return _cookies;
887             }
888         }
889
890         // returns TRUE iff there is at least one response cookie marked Shareable = false
891         internal bool ContainsNonShareableCookies() {
892             if (_cookies != null) {
893                 for (int i = 0; i < _cookies.Count; i++) {
894                     if (!_cookies[i].Shareable) {
895                         return true;
896                     }
897                 }
898             }
899             return false;
900         }
901
902         internal HttpCookieCollection GetCookiesNoCreate() {
903             return _cookies;
904         }
905
906         public NameValueCollection Headers {
907             get {
908                 if ( !(_wr is IIS7WorkerRequest) ) {
909                     throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
910                 }
911
912                 if (_headers == null) {
913                     _headers = new HttpHeaderCollection(_wr, this, 16);
914                 }
915
916                 return _headers;
917             }
918         }
919
920         /*
921          * Add dependency on a file to the current response
922          */
923
924         /// <devdoc>
925         ///    <para>Adds dependency on a file to the current response.</para>
926         /// </devdoc>
927         public void AddFileDependency(String filename) {
928             _fileDependencyList.AddDependency(filename, "filename");
929         }
930
931         // Add dependency on a list of files to the current response
932
933         //   Adds dependency on a group of files to the current response.
934         public void AddFileDependencies(ArrayList filenames) {
935             _fileDependencyList.AddDependencies(filenames, "filenames");
936         }
937
938
939         public void AddFileDependencies(string[] filenames) {
940             _fileDependencyList.AddDependencies(filenames, "filenames");
941         }
942
943         internal void AddVirtualPathDependencies(string[] virtualPaths) {
944             _virtualPathDependencyList.AddDependencies(virtualPaths, "virtualPaths", false, Request.Path);
945         }
946
947         internal void AddFileDependencies(string[] filenames, DateTime utcTime) {
948             _fileDependencyList.AddDependencies(filenames, "filenames", false, utcTime);
949         }
950
951         // Add dependency on another cache item to the response.
952         public void AddCacheItemDependency(string cacheKey) {
953             _cacheItemDependencyList.AddDependency(cacheKey, "cacheKey");
954         }
955
956         // Add dependency on a list of cache items to the response.
957         public void AddCacheItemDependencies(ArrayList cacheKeys) {
958             _cacheItemDependencyList.AddDependencies(cacheKeys, "cacheKeys");
959         }
960
961
962         public void AddCacheItemDependencies(string[] cacheKeys) {
963             _cacheItemDependencyList.AddDependencies(cacheKeys, "cacheKeys");
964         }
965
966         // Add dependency on one or more CacheDependency objects to the response
967         public void AddCacheDependency(params CacheDependency[] dependencies) {
968             if (dependencies == null) {
969                 throw new ArgumentNullException("dependencies");
970             }
971             if (dependencies.Length == 0) {
972                 return;
973             }
974             if (_cacheDependencyForResponse != null) {
975                 throw new InvalidOperationException(SR.GetString(SR.Invalid_operation_cache_dependency));
976             }
977             if (_userAddedDependencies == null) {
978                 // copy array argument contents so they can't be changed beneath us
979                 _userAddedDependencies = (CacheDependency[]) dependencies.Clone();
980             }
981             else {
982                 CacheDependency[] deps = new CacheDependency[_userAddedDependencies.Length + dependencies.Length];
983                 int i = 0;
984                 for (i = 0; i < _userAddedDependencies.Length; i++) {
985                     deps[i] = _userAddedDependencies[i];
986                 }
987                 for (int j = 0; j < dependencies.Length; j++) {
988                     deps[i + j] = dependencies[j];
989                 }
990                 _userAddedDependencies = deps;
991             }
992             Cache.SetDependencies(true);
993         }
994
995         public static void RemoveOutputCacheItem(string path) {
996             RemoveOutputCacheItem(path, null);
997         }
998
999         public static void RemoveOutputCacheItem(string path, string providerName) {
1000             if (path == null)
1001                 throw new ArgumentNullException("path");
1002             if (StringUtil.StringStartsWith(path, "\\\\") || path.IndexOf(':') >= 0 || !UrlPath.IsRooted(path))
1003                 throw new ArgumentException(SR.GetString(SR.Invalid_path_for_remove, path));
1004
1005             string key = OutputCacheModule.CreateOutputCachedItemKey(
1006                     path, HttpVerb.GET, null, null);
1007
1008             if (providerName == null) {
1009                 OutputCache.Remove(key, (HttpContext)null);
1010             }
1011             else {
1012                 OutputCache.RemoveFromProvider(key, providerName);
1013             }
1014
1015             key = OutputCacheModule.CreateOutputCachedItemKey(
1016                     path, HttpVerb.POST, null, null);
1017             
1018             if (providerName == null) {
1019                 OutputCache.Remove(key, (HttpContext)null);
1020             }
1021             else { 
1022                 OutputCache.RemoveFromProvider(key, providerName);
1023             }
1024         }
1025
1026         // Check if there are file dependencies.
1027         internal bool HasFileDependencies() {
1028             return _fileDependencyList.HasDependencies();
1029         }
1030
1031         // Check if there are item dependencies.
1032         internal bool HasCacheItemDependencies() {
1033             return _cacheItemDependencyList.HasDependencies();
1034         }
1035
1036         internal CacheDependency CreateCacheDependencyForResponse() {
1037             if (_cacheDependencyForResponse == null) {
1038                 CacheDependency dependency;
1039                 
1040                 // N.B. - add file dependencies last so that we hit the file changes monitor
1041                 // just once.
1042                 dependency = _cacheItemDependencyList.CreateCacheDependency(CacheDependencyType.CacheItems, null);
1043                 dependency = _fileDependencyList.CreateCacheDependency(CacheDependencyType.Files, dependency);
1044                 dependency = _virtualPathDependencyList.CreateCacheDependency(CacheDependencyType.VirtualPaths, dependency);
1045                 
1046                 // N.B. we add in the aggregate dependency here, and return it,
1047                 // so this function should only be called once, because the resulting
1048                 // dependency can only be added to the cache once
1049                 if (_userAddedDependencies != null) {
1050                     AggregateCacheDependency agg = new AggregateCacheDependency();
1051                     agg.Add(_userAddedDependencies);
1052                     if (dependency != null) {
1053                         agg.Add(dependency);
1054                     }
1055                     // clear it because we added them to the dependencies for the response
1056                     _userAddedDependencies = null;
1057                     _cacheDependencyForResponse = agg;
1058                 }
1059                 else {
1060                     _cacheDependencyForResponse = dependency;
1061                 }
1062             }
1063             return _cacheDependencyForResponse;
1064         }
1065
1066         // Get response headers and content as HttpRawResponse
1067         internal HttpRawResponse GetSnapshot() {
1068             int statusCode = 200;
1069             string statusDescription = null;
1070             ArrayList headers = null;
1071             ArrayList buffers = null;
1072             bool hasSubstBlocks = false;
1073
1074             if (!IsBuffered())
1075                 throw new HttpException(SR.GetString(SR.Cannot_get_snapshot_if_not_buffered));
1076
1077             IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
1078
1079             // data
1080             if (!_suppressContent) {
1081                 if (iis7WorkerRequest != null) {
1082                     buffers = _httpWriter.GetIntegratedSnapshot(out hasSubstBlocks, iis7WorkerRequest);
1083                 }
1084                 else {
1085                     buffers = _httpWriter.GetSnapshot(out hasSubstBlocks);
1086                 }
1087             }
1088
1089             // headers (after data as the data has side effects (like charset, see ASURT 113202))
1090             if (!_suppressHeaders) {
1091                 statusCode = _statusCode;
1092                 statusDescription = _statusDescription;
1093                 // In integrated pipeline, we need to use the current response headers
1094                 // from the response (these may have been generated by other handlers, etc)
1095                 // instead of the ASP.NET cached headers
1096                 if (iis7WorkerRequest != null) {
1097                     headers = GenerateResponseHeadersIntegrated(true);
1098                 }
1099                 else {
1100                     headers = GenerateResponseHeaders(true);
1101                 }
1102             }
1103             return new HttpRawResponse(statusCode, statusDescription, headers, buffers, hasSubstBlocks);
1104         }
1105
1106         // Send saved response snapshot as the entire response
1107         internal void UseSnapshot(HttpRawResponse rawResponse, bool sendBody) {
1108             if (_headersWritten)
1109                 throw new HttpException(SR.GetString(SR.Cannot_use_snapshot_after_headers_sent));
1110
1111             if (_httpWriter == null)
1112                 throw new HttpException(SR.GetString(SR.Cannot_use_snapshot_for_TextWriter));
1113
1114             ClearAll();
1115
1116             // restore status
1117             StatusCode = rawResponse.StatusCode;
1118             StatusDescription = rawResponse.StatusDescription;
1119
1120             // restore headers
1121             ArrayList headers = rawResponse.Headers;
1122             int n = (headers != null) ? headers.Count : 0;
1123             for (int i = 0; i < n; i++) {
1124                 HttpResponseHeader h = (HttpResponseHeader)(headers[i]);
1125                 this.AppendHeader(h.Name, h.Value);
1126             }
1127
1128             // restore content
1129             _httpWriter.UseSnapshot(rawResponse.Buffers);
1130
1131             _suppressContent = !sendBody;
1132         }
1133
1134         internal void CloseConnectionAfterError() {
1135             _closeConnectionAfterError = true;
1136         }
1137
1138         private void WriteErrorMessage(Exception e, bool dontShowSensitiveErrors) {
1139             ErrorFormatter errorFormatter = null;
1140             CultureInfo uiculture = null, savedUiculture = null;
1141             bool needToRestoreUiculture = false;
1142
1143             if (_context.DynamicUICulture != null) {
1144                 // if the user set the culture dynamically use it
1145                 uiculture =  _context.DynamicUICulture;
1146             }
1147             else  {
1148                 // get the UI culture under which the error text must be created (use LKG to avoid errors while reporting error)
1149                 GlobalizationSection globConfig = RuntimeConfig.GetLKGConfig(_context).Globalization;
1150                 if ((globConfig != null) && (!String.IsNullOrEmpty(globConfig.UICulture))) {
1151                     try {
1152                         uiculture = HttpServerUtility.CreateReadOnlyCultureInfo(globConfig.UICulture);
1153                     }
1154                     catch {
1155                     }
1156                 }
1157             }
1158
1159             //  In Integrated mode, generate the necessary response headers for the error
1160             GenerateResponseHeadersForHandler();
1161
1162             // set the UI culture
1163             if (uiculture != null) {
1164                 savedUiculture = Thread.CurrentThread.CurrentUICulture;
1165                 Thread.CurrentThread.CurrentUICulture = uiculture;
1166                 needToRestoreUiculture = true;
1167             }
1168
1169             try {
1170                 try {
1171                     // Try to get an error formatter
1172                     errorFormatter = GetErrorFormatter(e);
1173 #if DBG
1174                     Debug.Trace("internal", "Error stack for " + Request.Path, e);
1175 #endif
1176                     if (dontShowSensitiveErrors && !errorFormatter.CanBeShownToAllUsers)
1177                         errorFormatter = new GenericApplicationErrorFormatter(Request.IsLocal);
1178
1179                     Debug.Trace("internal", "errorFormatter's type = " +  errorFormatter.GetType());
1180
1181                     if (ErrorFormatter.RequiresAdaptiveErrorReporting(Context)) {
1182                         _writer.Write(errorFormatter.GetAdaptiveErrorMessage(Context, dontShowSensitiveErrors));
1183                     }
1184                     else {
1185                         _writer.Write(errorFormatter.GetHtmlErrorMessage(dontShowSensitiveErrors));
1186
1187                         // Write a stack dump in an HTML comment for debugging purposes
1188                         // Only show it for Asp permission medium or higher (ASURT 126373)
1189                         if (!dontShowSensitiveErrors &&
1190                             HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
1191                             _writer.Write("<!-- \r\n");
1192                             WriteExceptionStack(e);
1193                             _writer.Write("-->");
1194                         }
1195                          if (!dontShowSensitiveErrors && !Request.IsLocal ) {
1196                              _writer.Write("<!-- \r\n");
1197                              _writer.Write(SR.GetString(SR.Information_Disclosure_Warning));
1198                              _writer.Write("-->");
1199                          }
1200                     }
1201
1202                     if (_closeConnectionAfterError) {
1203                         Flush();
1204                         Close();
1205                     }
1206                 }
1207                 finally {
1208                     // restore ui culture
1209                     if (needToRestoreUiculture)
1210                         Thread.CurrentThread.CurrentUICulture = savedUiculture;
1211                 }
1212             }
1213             catch { // Protect against exception filters
1214                 throw;
1215             }
1216         }
1217
1218         internal void SetOverrideErrorFormatter(ErrorFormatter errorFormatter) {
1219             _overrideErrorFormatter = errorFormatter;
1220         }
1221
1222         internal ErrorFormatter GetErrorFormatter(Exception e) {
1223             ErrorFormatter  errorFormatter = null;
1224
1225             if (_overrideErrorFormatter != null) {
1226                 return _overrideErrorFormatter;
1227             }
1228
1229             // Try to get an error formatter
1230             errorFormatter = HttpException.GetErrorFormatter(e);
1231
1232             if (errorFormatter == null) {
1233                 ConfigurationException ce = e as ConfigurationException;
1234                 if (ce != null && !String.IsNullOrEmpty(ce.Filename))
1235                     errorFormatter = new ConfigErrorFormatter(ce);
1236             }
1237
1238             // If we couldn't get one, create one here
1239             if (errorFormatter == null) {
1240                 // If it's a 404, use a special error page, otherwise, use a more
1241                 // generic one.
1242                 if (_statusCode == 404)
1243                     errorFormatter = new PageNotFoundErrorFormatter(Request.Path);
1244                 else if (_statusCode == 403)
1245                     errorFormatter = new PageForbiddenErrorFormatter(Request.Path);
1246                 else {
1247                     if (e is System.Security.SecurityException)
1248                         errorFormatter = new SecurityErrorFormatter(e);
1249                     else
1250                         errorFormatter = new UnhandledErrorFormatter(e);
1251                 }
1252             }
1253
1254             // Show config source only on local request for security reasons
1255             // Config file snippet may unintentionally reveal sensitive information (not related to the error)
1256             ConfigErrorFormatter configErrorFormatter = errorFormatter as ConfigErrorFormatter;
1257             if (configErrorFormatter != null) {
1258                 configErrorFormatter.AllowSourceCode = Request.IsLocal;
1259             }
1260
1261             return errorFormatter;
1262         }
1263
1264         private void WriteOneExceptionStack(Exception e) {
1265             Exception subExcep = e.InnerException;
1266             if (subExcep != null)
1267                 WriteOneExceptionStack(subExcep);
1268
1269             string title = "[" + e.GetType().Name + "]";
1270             if (e.Message != null && e.Message.Length > 0)
1271                 title += ": " + HttpUtility.HtmlEncode(e.Message);
1272
1273             _writer.WriteLine(title);
1274             if (e.StackTrace != null)
1275                 _writer.WriteLine(e.StackTrace);
1276         }
1277
1278         private void WriteExceptionStack(Exception e) {
1279             ConfigurationErrorsException errors = e as ConfigurationErrorsException;
1280             if (errors == null) {
1281                 WriteOneExceptionStack(e);
1282             }
1283             else {
1284                 // Write the original exception to get the first error with
1285                 // a full stack trace
1286                 WriteOneExceptionStack(e);
1287
1288                 // Write additional errors, which will contain truncated stacks
1289                 ICollection col = errors.Errors;
1290                 if (col.Count > 1) {
1291                     bool firstSkipped = false;
1292                     foreach (ConfigurationException ce in col) {
1293                         if (!firstSkipped) {
1294                             firstSkipped = true;
1295                             continue;
1296                         }
1297
1298                         _writer.WriteLine("---");
1299                         WriteOneExceptionStack(ce);
1300                     }
1301                 }
1302             }
1303         }
1304
1305         internal void ReportRuntimeError(Exception e, bool canThrow, bool localExecute) {
1306             CustomErrorsSection customErrorsSetting = null;
1307             bool useCustomErrors = false;
1308             int code = -1;
1309
1310             if (_completed)
1311                 return;
1312
1313             // always try to disable IIS custom errors when we send an error
1314             if (_wr != null) {
1315                 _wr.TrySkipIisCustomErrors = true;
1316             }
1317
1318             if (!localExecute) {
1319                 code = HttpException.GetHttpCodeForException(e);
1320
1321                 // Don't raise event for 404.  See VSWhidbey 124147.
1322                 if (code != 404) {
1323                     WebBaseEvent.RaiseRuntimeError(e, this);
1324                 }
1325
1326                 // This cannot use the HttpContext.IsCustomErrorEnabled property, since it must call
1327                 // GetSettings() with the canThrow parameter.
1328                 customErrorsSetting = CustomErrorsSection.GetSettings(_context, canThrow);
1329                 if (customErrorsSetting != null)
1330                     useCustomErrors = customErrorsSetting.CustomErrorsEnabled(Request);
1331                 else
1332                     useCustomErrors = true;
1333             }
1334
1335             if (!_headersWritten) {
1336                 // nothing sent yet - entire response
1337
1338                 if (code == -1) {
1339                     code = HttpException.GetHttpCodeForException(e);
1340                 }
1341
1342                 // change 401 to 500 in case the config is not to impersonate
1343                 if (code == 401 && !_context.IsClientImpersonationConfigured)
1344                     code = 500;
1345
1346                 if (_context.TraceIsEnabled)
1347                     _context.Trace.StatusCode = code;
1348
1349                 if (!localExecute && useCustomErrors) {
1350                     String redirect = (customErrorsSetting != null) ? customErrorsSetting.GetRedirectString(code) : null;
1351
1352                     RedirectToErrorPageStatus redirectStatus = RedirectToErrorPage(redirect, customErrorsSetting.RedirectMode);
1353                     switch (redirectStatus) {
1354                         case RedirectToErrorPageStatus.Success:
1355                             // success - nothing to do
1356                             break;
1357
1358                         case RedirectToErrorPageStatus.NotAttempted:
1359                             // if no redirect display generic error
1360                             ClearAll();
1361                             StatusCode = code;
1362                             WriteErrorMessage(e, dontShowSensitiveErrors: true);
1363                             break;
1364
1365                         default:
1366                             // DevDiv #70492 - If we tried to display the custom error page but failed in doing so, we should display
1367                             // a generic error message instead of trying to display the original error. We have a compat switch on
1368                             // the <customErrors> element to control this behavior.
1369
1370                             if (customErrorsSetting.AllowNestedErrors) {
1371                                 // The user has set the compat switch to use the original (pre-bug fix) behavior.
1372                                 goto case RedirectToErrorPageStatus.NotAttempted;
1373                             }
1374
1375                             ClearAll();
1376                             StatusCode = 500;
1377                             HttpException dummyException = new HttpException();
1378                             dummyException.SetFormatter(new CustomErrorFailedErrorFormatter());
1379                             WriteErrorMessage(dummyException, dontShowSensitiveErrors: true);
1380                             break;
1381                     }
1382                 }
1383                 else {
1384                     ClearAll();
1385                     StatusCode = code;
1386                     WriteErrorMessage(e, dontShowSensitiveErrors: false);
1387                 }
1388             }
1389             else {
1390                 Clear();
1391
1392                 if (_contentType != null && _contentType.Equals("text/html")) {
1393                     // in the middle of Html - break Html
1394                     Write("\r\n\r\n</pre></table></table></table></table></table>");
1395                     Write("</font></font></font></font></font>");
1396                     Write("</i></i></i></i></i></b></b></b></b></b></u></u></u></u></u>");
1397                     Write("<p>&nbsp;</p><hr>\r\n\r\n");
1398                 }
1399
1400                 WriteErrorMessage(e, useCustomErrors);
1401             }
1402         }
1403
1404         internal void SynchronizeStatus(int statusCode, int subStatusCode, string description) {
1405             _statusCode = statusCode;
1406             _subStatusCode = subStatusCode;
1407             _statusDescription = description;
1408         }
1409
1410
1411         internal void SynchronizeHeader(int knownHeaderIndex, string name, string value) {
1412             HttpHeaderCollection headers = Headers as HttpHeaderCollection;
1413             headers.SynchronizeHeader(name, value);
1414
1415             // unknown headers have an index < 0
1416             if (knownHeaderIndex < 0) {
1417                 return;
1418             }
1419
1420             bool fHeadersWritten = HeadersWritten;
1421             HeadersWritten = false; // Turn off the warning for "Headers have been written and can not be set"
1422             try {
1423                 switch (knownHeaderIndex) {
1424                 case HttpWorkerRequest.HeaderCacheControl:
1425                     _cacheControlHeaderAdded = true;
1426                     break;
1427                 case HttpWorkerRequest.HeaderContentType:
1428                     _contentType = value;
1429                     break;
1430                 case HttpWorkerRequest.HeaderLocation:
1431                     _redirectLocation = value;
1432                     _redirectLocationSet = false;
1433                     break;
1434                 case HttpWorkerRequest.HeaderSetCookie:
1435                     // If the header is Set-Cookie, update the corresponding
1436                     // cookie in the cookies collection
1437                     if (value != null) {
1438                         HttpCookie cookie = HttpRequest.CreateCookieFromString(value);
1439                         // do not write this cookie back to IIS
1440                         cookie.FromHeader = true;
1441                         Cookies.Set(cookie);
1442                         cookie.Changed = false;
1443                         cookie.Added = false;
1444                     }
1445                     break;
1446                 }
1447             } finally {
1448                 HeadersWritten = fHeadersWritten;
1449             }
1450         }
1451
1452         internal void SyncStatusIntegrated() {
1453             Debug.Assert(_wr is IIS7WorkerRequest, "_wr is IIS7WorkerRequest");
1454              if (!_headersWritten && _statusSet) {
1455                  // For integrated pipeline, synchronize the status immediately so that the FREB log
1456                  // correctly indicates the module and notification that changed the status.
1457                  _wr.SendStatus(_statusCode, _subStatusCode, this.StatusDescription);
1458                  _statusSet = false;
1459              }
1460         }
1461
1462         // Public properties
1463
1464         // Http status code
1465         //    Gets or sets the HTTP status code of output returned to client.
1466         public int StatusCode {
1467             get {
1468                 return _statusCode;
1469             }
1470
1471             set {
1472                 if (_headersWritten)
1473                     throw new HttpException(SR.GetString(SR.Cannot_set_status_after_headers_sent));
1474
1475                 if (_statusCode != value) {
1476                     _statusCode = value;
1477                     _subStatusCode = 0;
1478                     _statusDescription = null;
1479                     _statusSet = true;
1480                 }
1481             }
1482         }
1483
1484         // the IIS sub status code
1485         // since this doesn't get emitted in the protocol
1486         // we won't send it through the worker request interface
1487         // directly
1488         public int SubStatusCode {
1489             get {
1490                 if ( !(_wr is IIS7WorkerRequest) ) {
1491                     throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
1492                 }
1493
1494                 return _subStatusCode;
1495             }
1496             set {
1497                 if ( !(_wr is IIS7WorkerRequest) ) {
1498                     throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
1499                 }
1500
1501                 if (_headersWritten) {
1502                     throw new HttpException(SR.GetString(SR.Cannot_set_status_after_headers_sent));
1503                 }
1504
1505                 _subStatusCode = value;
1506                 _statusSet = true;
1507             }
1508         }
1509
1510         // Allows setting both the status and the substatus individually. If not in IIS7 integrated mode,
1511         // the substatus code is ignored so as not to throw an exception.
1512         internal void SetStatusCode(int statusCode, int subStatus = -1) {
1513             StatusCode = statusCode;
1514             if (subStatus >= 0 && _wr is IIS7WorkerRequest) {
1515                 SubStatusCode = subStatus;
1516             }
1517         }
1518
1519         /*
1520          * Http status description string
1521          */
1522
1523         // Http status description string
1524         //    Gets or sets the HTTP status string of output returned to the client.
1525         public String StatusDescription {
1526             get {
1527                 if (_statusDescription == null)
1528                     _statusDescription = HttpWorkerRequest.GetStatusDescription(_statusCode);
1529
1530                 return _statusDescription;
1531             }
1532
1533             set {
1534                 if (_headersWritten)
1535                     throw new HttpException(SR.GetString(SR.Cannot_set_status_after_headers_sent));
1536
1537                 if (value != null && value.Length > 512)  // ASURT 124743
1538                     throw new ArgumentOutOfRangeException("value");
1539                 _statusDescription = value;
1540                 _statusSet = true;
1541             }
1542         }
1543
1544         public bool TrySkipIisCustomErrors {
1545             get {
1546                 if (_wr != null) {
1547                     return _wr.TrySkipIisCustomErrors;
1548                 }
1549                 return false;
1550             }
1551             set {
1552                 if (_wr != null) {
1553                     _wr.TrySkipIisCustomErrors = value;
1554                 }
1555             }
1556         }
1557
1558         /// <summary>
1559         /// By default, the FormsAuthenticationModule hooks EndRequest and converts HTTP 401 status codes to
1560         /// HTTP 302, redirecting to the login page. This isn't appropriate for certain classes of errors,
1561         /// e.g. where authentication succeeded but authorization failed, or where the current request is
1562         /// an AJAX or web service request. This property provides a way to suppress the redirect behavior
1563         /// and send the original status code to the client.
1564         /// </summary>
1565         public bool SuppressFormsAuthenticationRedirect {
1566             get;
1567             set;
1568         }
1569
1570         /// <summary>
1571         /// By default, ASP.NET sends a "Cache-Control: private" response header unless an explicit cache
1572         /// policy has been specified for this response. This property allows suppressing this default
1573         /// response header on a per-request basis. It can still be suppressed for the entire application
1574         /// by setting the appropriate value in &lt;httpRuntime&gt; or &lt;outputCache&gt;. See
1575         /// http://msdn.microsoft.com/en-us/library/system.web.configuration.httpruntimesection.sendcachecontrolheader.aspx
1576         /// for more information on those config elements.
1577         /// </summary>
1578         /// <remarks>
1579         /// Developers should use caution when suppressing the default Cache-Control header, as proxies
1580         /// and other intermediaries may treat responses without this header as cacheable by default.
1581         /// This could lead to the inadvertent caching of sensitive information.
1582         /// See RFC 2616, Sec. 13.4, for more information.
1583         /// </remarks>
1584         public bool SuppressDefaultCacheControlHeader {
1585             get;
1586             set;
1587         }
1588
1589         // Flag indicating to buffer the output
1590         //    Gets or sets a value indicating whether HTTP output is buffered.
1591         public bool BufferOutput {
1592             get {
1593                 return _bufferOutput;
1594             }
1595
1596             set {
1597                 if (_bufferOutput != value) {
1598                     _bufferOutput = value;
1599
1600                     if (_httpWriter != null)
1601                         _httpWriter.UpdateResponseBuffering();
1602                 }
1603             }
1604         }
1605
1606         // Gets the Content-Encoding HTTP response header.
1607         internal String GetHttpHeaderContentEncoding() {
1608             string coding = null;
1609             if (_wr is IIS7WorkerRequest) {
1610                 if (_headers != null) {
1611                     coding = _headers["Content-Encoding"];
1612                 }
1613             }
1614             else if (_customHeaders != null) {
1615                 int numCustomHeaders = _customHeaders.Count;
1616                 for (int i = 0; i < numCustomHeaders; i++) {
1617                     HttpResponseHeader h = (HttpResponseHeader)_customHeaders[i];
1618                     if (h.Name == "Content-Encoding") {
1619                         coding = h.Value;
1620                         break;
1621                     }
1622                 }
1623             }
1624             return coding;
1625         }
1626
1627         /*
1628          * Content-type
1629          */
1630
1631         /// <devdoc>
1632         ///    <para>Gets or sets the
1633         ///       HTTP MIME type of output.</para>
1634         /// </devdoc>
1635         public String ContentType {
1636             get {
1637                 return _contentType;
1638             }
1639
1640             set {
1641                 if (_headersWritten) {
1642                     // Don't throw if the new content type is the same as the current one
1643                     if (_contentType == value)
1644                         return;
1645
1646                     throw new HttpException(SR.GetString(SR.Cannot_set_content_type_after_headers_sent));
1647                 }
1648
1649                 _contentTypeSetByManagedCaller = true;
1650                 _contentType = value;
1651             }
1652         }
1653
1654
1655         //    Gets or sets the HTTP charset of output.
1656         public String Charset {
1657             get {
1658                 if (_charSet == null)
1659                     _charSet = ContentEncoding.WebName;
1660
1661                 return _charSet;
1662             }
1663
1664             set {
1665                 if (_headersWritten)
1666                     throw new HttpException(SR.GetString(SR.Cannot_set_content_type_after_headers_sent));
1667
1668                 if (value != null)
1669                     _charSet = value;
1670                 else
1671                     _charSet = String.Empty;  // to differentiate between not set (default) and empty chatset
1672
1673                 _customCharSet = true;
1674             }
1675         }
1676
1677         // Content encoding for conversion
1678         //   Gets or sets the HTTP character set of output.
1679         public Encoding ContentEncoding {
1680             get {
1681                 if (_encoding == null) {
1682                     // use LKG config because Response.ContentEncoding is need to display [config] error
1683                     GlobalizationSection globConfig = RuntimeConfig.GetLKGConfig(_context).Globalization;
1684                     if (globConfig != null)
1685                         _encoding = globConfig.ResponseEncoding;
1686
1687                     if (_encoding == null)
1688                         _encoding = Encoding.Default;
1689                 }
1690
1691                 return _encoding;
1692             }
1693
1694             set {
1695                 if (value == null)
1696                     throw new ArgumentNullException("value");
1697
1698                 if (_encoding == null || !_encoding.Equals(value)) {
1699                     _encoding = value;
1700                     _encoder = null;   // flush cached encoder
1701
1702                     if (_httpWriter != null)
1703                         _httpWriter.UpdateResponseEncoding();
1704                 }
1705             }
1706         }
1707
1708
1709         public Encoding HeaderEncoding {
1710             get {
1711                 if (_headerEncoding == null) {
1712                     // use LKG config because Response.ContentEncoding is need to display [config] error
1713                     GlobalizationSection globConfig = RuntimeConfig.GetLKGConfig(_context).Globalization;
1714                     if (globConfig != null)
1715                         _headerEncoding = globConfig.ResponseHeaderEncoding;
1716
1717                     // default to UTF-8 (also for Unicode as headers cannot be double byte encoded)
1718                     if (_headerEncoding == null || _headerEncoding.Equals(Encoding.Unicode))
1719                         _headerEncoding = Encoding.UTF8;
1720                 }
1721
1722                 return _headerEncoding;
1723             }
1724
1725             set {
1726                 if (value == null)
1727                     throw new ArgumentNullException("value");
1728
1729                 if (value.Equals(Encoding.Unicode)) {
1730                     throw new HttpException(SR.GetString(SR.Invalid_header_encoding, value.WebName));
1731                 }
1732
1733                 if (_headerEncoding == null || !_headerEncoding.Equals(value)) {
1734                     if (_headersWritten)
1735                         throw new HttpException(SR.GetString(SR.Cannot_set_header_encoding_after_headers_sent));
1736
1737                     _headerEncoding = value;
1738                 }
1739             }
1740         }
1741
1742         // Encoder cached for the current encoding
1743         internal Encoder ContentEncoder {
1744             get {
1745                 if (_encoder == null) {
1746                     Encoding e = ContentEncoding;
1747                     _encoder = e.GetEncoder();
1748
1749                     // enable best fit mapping accoding to config
1750                     // (doesn't apply to utf-8 which is the default, thus optimization)
1751
1752                     if (!e.Equals(Encoding.UTF8)) {
1753                         bool enableBestFit = false;
1754
1755                         GlobalizationSection globConfig = RuntimeConfig.GetLKGConfig(_context).Globalization;
1756                         if (globConfig != null) {
1757                             enableBestFit = globConfig.EnableBestFitResponseEncoding;
1758                         }
1759
1760                         if (!enableBestFit) {
1761                             // setting 'fallback' disables best fit mapping
1762                             _encoder.Fallback = new EncoderReplacementFallback();
1763                         }
1764                     }
1765                 }
1766                 return _encoder;
1767             }
1768         }
1769
1770         // Cache policy
1771         //    Returns the caching semantics of the Web page (expiration time, privacy, vary clauses).
1772         public HttpCachePolicy Cache {
1773             get {
1774                 if (_cachePolicy == null) {
1775                     _cachePolicy = new HttpCachePolicy();
1776                 }
1777
1778                 return _cachePolicy;
1779             }
1780         }
1781
1782         // Return whether or not we have cache policy. We don't want to create it in
1783         // situations where we don't modify it.
1784         internal bool HasCachePolicy {
1785             get {
1786                 return _cachePolicy != null;
1787             }
1788         }
1789
1790         // Client connected flag
1791         //   Gets a value indicating whether the client is still connected to the server.
1792         public bool IsClientConnected {
1793             get {
1794                 if (_clientDisconnected)
1795                     return false;
1796
1797                 if (_wr != null && !_wr.IsClientConnected()) {
1798                     _clientDisconnected = true;
1799                     return false;
1800                 }
1801
1802                 return true;
1803             }
1804         }
1805
1806         /// <summary>
1807         /// Returns a CancellationToken that is tripped when the client disconnects. This can be used
1808         /// to listen for async disconnect notifications.
1809         /// </summary>
1810         /// <remarks>
1811         /// This method requires that the application be hosted on IIS 7.5 or higher and that the
1812         /// application pool be running the integrated mode pipeline.
1813         /// 
1814         /// Consumers should be aware of some restrictions when consuming this CancellationToken.
1815         /// Failure to heed these warnings can lead to race conditions, deadlocks, or other
1816         /// undefined behavior.
1817         /// 
1818         /// - This API is thread-safe. However, ASP.NET will dispose of the token object at the
1819         ///   end of the request. Consumers should exercise caution and ensure that they're not
1820         ///   calling into this API outside the bounds of a single request. This is similar to
1821         ///   the contract with BCL Task-returning methods which take a CancellationToken as a
1822         ///   parameter: the callee should not touch the CancellationToken after the returned
1823         ///   Task transitions to a terminal state.
1824         /// 
1825         /// - DO NOT wait on the CancellationToken.WaitHandle, as this defeats the purpose of an
1826         ///   async notification and can cause deadlocks.
1827         /// 
1828         /// - DO NOT call the CancellationToken.Register overloads which invoke the callback on
1829         ///   the original SynchronizationContext.
1830         /// 
1831         /// - DO NOT consume HttpContext or other non-thread-safe ASP.NET intrinsic objects from
1832         ///   within the callback provided to Register. Remember: the callback may be running
1833         ///   concurrently with other ASP.NET or application code.
1834         /// 
1835         /// - DO keep the callback methods short-running and non-blocking. Make every effort to
1836         ///   avoid throwing exceptions from within the callback methods.
1837         /// 
1838         /// - We do not guarantee that we will ever transition the token to a canceled state.
1839         ///   For example, if the request finishes without the client having disconnected, we
1840         ///   will dispose of this token as mentioned earlier without having first canceled it.
1841         /// </remarks>
1842         public CancellationToken ClientDisconnectedToken {
1843             get {
1844                 IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
1845                 CancellationToken cancellationToken;
1846                 if (wr != null && wr.TryGetClientDisconnectedCancellationToken(out cancellationToken)) {
1847                     return cancellationToken;
1848                 }
1849                 else {
1850                     throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_75_Integrated));
1851                 }
1852             }
1853         }
1854
1855         public bool IsRequestBeingRedirected {
1856             get {
1857                 return _isRequestBeingRedirected;
1858             }
1859             internal set {
1860                 _isRequestBeingRedirected = value;
1861             }
1862         }
1863
1864
1865         /// <devdoc>
1866         ///    <para>Gets or Sets a redirection string (value of location resposne header) for redirect response.</para>
1867         /// </devdoc>
1868         public String RedirectLocation {
1869             get { return _redirectLocation; }
1870             set {
1871                 if (_headersWritten)
1872                     throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent));
1873
1874                 _redirectLocation = value;
1875                 _redirectLocationSet = true;
1876             }
1877         }
1878
1879         /*
1880          * Disconnect client
1881          */
1882
1883         /// <devdoc>
1884         ///    <para>Closes the socket connection to a client.</para>
1885         /// </devdoc>
1886         public void Close() {
1887             if (!_clientDisconnected && !_completed && _wr != null) {
1888                 _wr.CloseConnection();
1889                 _clientDisconnected = true;
1890             }
1891         }
1892
1893         // TextWriter object
1894         //    Enables custom output to the outgoing Http content body.
1895         public TextWriter Output {
1896             get { return _writer;}
1897             set { _writer = value; }
1898         }
1899
1900         internal TextWriter SwitchWriter(TextWriter writer) {
1901             TextWriter oldWriter = _writer;
1902             _writer = writer;
1903             return oldWriter;
1904         }
1905
1906         // Output stream
1907         //       Enables binary output to the outgoing Http content body.
1908         public Stream OutputStream {
1909             get {
1910                 if (!UsingHttpWriter)
1911                     throw new HttpException(SR.GetString(SR.OutputStream_NotAvail));
1912
1913                 return _httpWriter.OutputStream;
1914             }
1915         }
1916
1917         // ASP classic compat
1918         //    Writes a string of binary characters to the HTTP output stream.
1919         public void BinaryWrite(byte[] buffer) {
1920             OutputStream.Write(buffer, 0, buffer.Length);
1921         }
1922
1923
1924         //  Appends a PICS (Platform for Internet Content Selection) label HTTP header to the output stream.
1925         public void Pics(String value) {
1926             AppendHeader("PICS-Label", value);
1927         }
1928
1929         // Filtering stream
1930         //       Specifies a wrapping filter object to modify HTTP entity body before transmission.
1931         public Stream Filter {
1932             get {
1933                 if (UsingHttpWriter)
1934                     return _httpWriter.GetCurrentFilter();
1935                 else
1936                     return null;
1937             }
1938
1939             set {
1940                 if (UsingHttpWriter) {
1941                     _httpWriter.InstallFilter(value);
1942
1943                     IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
1944                     if (iis7WorkerRequest != null) {
1945                         iis7WorkerRequest.ResponseFilterInstalled();
1946                     }
1947                 }
1948                 else
1949                     throw new HttpException(SR.GetString(SR.Filtering_not_allowed));
1950             }
1951
1952         }
1953
1954         // Flag to suppress writing of content
1955         //    Gets or sets a value indicating that HTTP content will not be sent to client.
1956         public bool SuppressContent {
1957             get {
1958                 return _suppressContent;
1959             }
1960
1961             set {
1962                 _suppressContent = value;
1963                 _suppressContentSet = true;
1964             }
1965         }
1966
1967         //
1968         // Public methods
1969         //
1970
1971         /*
1972           * Add Http custom header
1973           *
1974           * @param name header name
1975           * @param value header value
1976           */
1977
1978         /// <devdoc>
1979         ///    <para>Adds an HTTP
1980         ///       header to the output stream.</para>
1981         /// </devdoc>
1982         public void AppendHeader(String name, String value) {
1983             bool isCacheHeader = false;
1984
1985             if (_headersWritten)
1986                 throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent));
1987
1988             // some headers are stored separately or require special action
1989             int knownHeaderIndex = HttpWorkerRequest.GetKnownResponseHeaderIndex(name);
1990
1991             switch (knownHeaderIndex) {
1992                 case HttpWorkerRequest.HeaderContentType:
1993                     ContentType = value;
1994                     return; // don't keep as custom header
1995
1996                 case HttpWorkerRequest.HeaderContentLength:
1997                     _contentLengthSet = true;
1998                     break;
1999
2000                 case HttpWorkerRequest.HeaderLocation:
2001                     RedirectLocation = value;
2002                     return; // don't keep as custom header
2003
2004                 case HttpWorkerRequest.HeaderTransferEncoding:
2005                     _transferEncodingSet = true;
2006                     break;
2007
2008                 case HttpWorkerRequest.HeaderCacheControl:
2009                     _cacheControlHeaderAdded = true;
2010                     goto case HttpWorkerRequest.HeaderExpires;
2011                 case HttpWorkerRequest.HeaderExpires:
2012                 case HttpWorkerRequest.HeaderLastModified:
2013                 case HttpWorkerRequest.HeaderEtag:
2014                 case HttpWorkerRequest.HeaderVary:
2015                     isCacheHeader = true;
2016                     break;
2017             }
2018
2019             // In integrated mode, write the headers directly
2020             if (_wr is IIS7WorkerRequest) {
2021                 Headers.Add(name, value);
2022             }
2023             else {
2024                 if (isCacheHeader)
2025                 {
2026                     // don't keep as custom header
2027                     if (_cacheHeaders == null) {
2028                         _cacheHeaders = new ArrayList();
2029                     }
2030
2031                     _cacheHeaders.Add(new HttpResponseHeader(knownHeaderIndex, value));
2032                     return;
2033                 }
2034                 else {
2035                     HttpResponseHeader h;
2036                     if (knownHeaderIndex >= 0)
2037                         h = new HttpResponseHeader(knownHeaderIndex, value);
2038                     else
2039                         h = new HttpResponseHeader(name, value);
2040
2041                     AppendHeader(h);
2042                 }
2043             }
2044         }
2045
2046
2047         /// <internalonly/>
2048         /// <devdoc>
2049         ///    <para>
2050         ///       Adds an HTTP
2051         ///       cookie to the output stream.
2052         ///    </para>
2053         /// </devdoc>
2054         public void AppendCookie(HttpCookie cookie) {
2055             if (_headersWritten)
2056                 throw new HttpException(SR.GetString(SR.Cannot_append_cookie_after_headers_sent));
2057
2058             Cookies.AddCookie(cookie, true);
2059             OnCookieAdd(cookie);
2060         }
2061
2062
2063         /// <internalonly/>
2064         /// <devdoc>
2065         /// </devdoc>
2066         public void SetCookie(HttpCookie cookie) {
2067             if (_headersWritten)
2068                 throw new HttpException(SR.GetString(SR.Cannot_append_cookie_after_headers_sent));
2069
2070             Cookies.AddCookie(cookie, false);
2071             OnCookieCollectionChange();
2072         }
2073
2074         internal void BeforeCookieCollectionChange() {
2075             if (_headersWritten)
2076                 throw new HttpException(SR.GetString(SR.Cannot_modify_cookies_after_headers_sent));
2077         }
2078
2079         internal void OnCookieAdd(HttpCookie cookie) {
2080             // add to request's cookies as well
2081             Request.AddResponseCookie(cookie);
2082         }
2083
2084         internal void OnCookieCollectionChange() {
2085             // synchronize with request cookie collection
2086             Request.ResetCookies();
2087         }
2088
2089         // Clear response headers
2090         //    Clears all headers from the buffer stream.
2091         public void ClearHeaders() {
2092             if (_headersWritten)
2093                 throw new HttpException(SR.GetString(SR.Cannot_clear_headers_after_headers_sent));
2094
2095             StatusCode = 200;
2096             _subStatusCode = 0;
2097             _statusDescription = null;
2098
2099             _contentType = "text/html";
2100             _contentTypeSetByManagedCaller = false;
2101             _charSet = null;
2102             _customCharSet = false;
2103             _contentLengthSet = false;
2104
2105             _redirectLocation = null;
2106             _redirectLocationSet = false;
2107             _isRequestBeingRedirected = false;
2108
2109             _customHeaders = null;
2110
2111             if (_headers != null) {
2112                 _headers.ClearInternal();
2113             }
2114
2115             _transferEncodingSet = false;
2116             _chunked = false;
2117
2118             if (_cookies != null) {
2119                 _cookies.Reset();
2120                 Request.ResetCookies();
2121             }
2122
2123             if (_cachePolicy != null) {
2124                 _cachePolicy.Reset();
2125             }
2126
2127             _cacheControlHeaderAdded = false;
2128             _cacheHeaders = null;
2129
2130             _suppressHeaders = false;
2131             _suppressContent = false;
2132             _suppressContentSet = false;
2133
2134             _expiresInMinutes = 0;
2135             _expiresInMinutesSet = false;
2136             _expiresAbsolute = DateTime.MinValue;
2137             _expiresAbsoluteSet = false;
2138             _cacheControl = null;
2139
2140             IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
2141             if (iis7WorkerRequest != null) {
2142                 // clear the native response as well
2143                 ClearNativeResponse(false, true, iis7WorkerRequest);
2144                 // DevDiv 162749:
2145                 // We need to regenerate Cache-Control: private only when the handler is managed and
2146                 // configuration has <outputCache sendCacheControlHeader="true" /> in system.web
2147                 // caching section.
2148                 if (_handlerHeadersGenerated && _sendCacheControlHeader) {
2149                     Headers.Set("Cache-Control", "private");
2150                 }
2151                 _handlerHeadersGenerated = false;
2152             }
2153         }
2154
2155
2156         /// <devdoc>
2157         ///    <para>Clears all content output from the buffer stream.</para>
2158         /// </devdoc>
2159         public void ClearContent() {
2160             Clear();
2161         }
2162
2163         /*
2164          * Clear response buffer and headers. (For ASP compat doesn't clear headers)
2165          */
2166
2167         /// <devdoc>
2168         ///    <para>Clears all headers and content output from the buffer stream.</para>
2169         /// </devdoc>
2170         public void Clear() {
2171             if (UsingHttpWriter)
2172                 _httpWriter.ClearBuffers();
2173
2174             IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
2175             if (iis7WorkerRequest != null) {
2176                 // clear the native response buffers too
2177                 ClearNativeResponse(true, false, iis7WorkerRequest);
2178             }
2179
2180
2181         }
2182
2183         /*
2184          * Clear response buffer and headers. Internal. Used to be 'Clear'.
2185          */
2186         internal void ClearAll() {
2187             if (!_headersWritten)
2188                 ClearHeaders();
2189             Clear();
2190         }
2191
2192         /*
2193          * Flush response currently buffered
2194          */
2195
2196         /// <devdoc>
2197         ///    <para>Sends all currently buffered output to the client.</para>
2198         /// </devdoc>
2199         public void Flush() {
2200             if (_completed)
2201                 throw new HttpException(SR.GetString(SR.Cannot_flush_completed_response));
2202
2203             Flush(false);
2204         }
2205
2206         // Registers a callback that the ASP.NET runtime will invoke immediately before
2207         // response headers are sent for this request. This differs from the IHttpModule-
2208         // level pipeline event in that this is a per-request subscription rather than
2209         // a per-application subscription. The intent is that the callback may modify
2210         // the response status code or may set a response cookie or header. Other usage
2211         // notes and caveats:
2212         //
2213         // - This API is available only in the IIS integrated mode pipeline and only
2214         //   if response headers haven't yet been sent for this request.
2215         // - The ASP.NET runtime does not guarantee anything about the thread that the
2216         //   callback is invoked on. For example, the callback may be invoked synchronously
2217         //   on a background thread if a background flush is being performed.
2218         //   HttpContext.Current is not guaranteed to be available on such a thread.
2219         // - The callback must not call any API that manipulates the response entity body
2220         //   or that results in a flush. For example, the callback must not call
2221         //   Response.Redirect, as that method may manipulate the response entity body.
2222         // - The callback must contain only short-running synchronous code. Trying to kick
2223         //   off an asynchronous operation or wait on such an operation could result in
2224         //   a deadlock.
2225         // - The callback must not throw, otherwise behavior is undefined.
2226         [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = @"The normal event pattern doesn't work between HttpResponse and HttpResponseBase since the signatures differ.")]
2227         public ISubscriptionToken AddOnSendingHeaders(Action<HttpContext> callback) {
2228             if (callback == null) {
2229                 throw new ArgumentNullException("callback");
2230             }
2231
2232             if (!(_wr is IIS7WorkerRequest)) {
2233                 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
2234             }
2235
2236             if (HeadersWritten) {
2237                 throw new HttpException(SR.GetString(SR.Cannot_call_method_after_headers_sent_generic));
2238             }
2239
2240             return _onSendingHeadersSubscriptionQueue.Enqueue(callback);
2241         }
2242
2243         /*
2244          * Append string to the log record
2245          *
2246          * @param param string to append to the log record
2247          */
2248
2249         /// <devdoc>
2250         ///    <para>Adds custom log information to the IIS log file.</para>
2251         /// </devdoc>
2252         [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Medium)]
2253         public void AppendToLog(String param) {
2254             // only makes sense for IIS
2255             if (_wr is System.Web.Hosting.ISAPIWorkerRequest)
2256                 ((System.Web.Hosting.ISAPIWorkerRequest)_wr).AppendLogParameter(param);
2257             else if (_wr is System.Web.Hosting.IIS7WorkerRequest)
2258                 _context.Request.AppendToLogQueryString(param);
2259         }
2260
2261
2262         /// <devdoc>
2263         ///    <para>Redirects a client to a new URL.</para>
2264         /// </devdoc>
2265         public void Redirect(String url) {
2266             Redirect(url, true, false);
2267         }
2268
2269         /// <devdoc>
2270         ///    <para>Redirects a client to a new URL.</para>
2271         /// </devdoc>
2272         public void Redirect(String url, bool endResponse) {
2273             Redirect(url, endResponse, false);
2274         }
2275
2276         public void RedirectToRoute(object routeValues) {
2277             RedirectToRoute(new RouteValueDictionary(routeValues));
2278         }
2279
2280         public void RedirectToRoute(string routeName) {
2281             RedirectToRoute(routeName, (RouteValueDictionary)null, false);
2282         }
2283
2284         public void RedirectToRoute(RouteValueDictionary routeValues) {
2285             RedirectToRoute(null /* routeName */, routeValues, false);
2286         }
2287
2288         public void RedirectToRoute(string routeName, object routeValues) {
2289             RedirectToRoute(routeName, new RouteValueDictionary(routeValues), false);
2290         }
2291
2292         public void RedirectToRoute(string routeName, RouteValueDictionary routeValues) {
2293             RedirectToRoute(routeName, routeValues, false);
2294         }
2295
2296         private void RedirectToRoute(string routeName, RouteValueDictionary routeValues, bool permanent) {
2297             string destinationUrl = null;
2298             VirtualPathData data = RouteTable.Routes.GetVirtualPath(Request.RequestContext, routeName, routeValues);
2299             if (data != null) {
2300                 destinationUrl = data.VirtualPath;
2301             }
2302
2303             if (String.IsNullOrEmpty(destinationUrl)) {
2304                 throw new InvalidOperationException(SR.GetString(SR.No_Route_Found_For_Redirect));
2305             }
2306
2307             Redirect(destinationUrl, false /* endResponse */, permanent);
2308         }
2309
2310         public void RedirectToRoutePermanent(object routeValues) {
2311             RedirectToRoutePermanent(new RouteValueDictionary(routeValues));
2312         }
2313
2314         public void RedirectToRoutePermanent(string routeName) {
2315             RedirectToRoute(routeName, (RouteValueDictionary)null, true);
2316         }
2317
2318         public void RedirectToRoutePermanent(RouteValueDictionary routeValues) {
2319             RedirectToRoute(null /* routeName */, routeValues, true);
2320         }
2321
2322         public void RedirectToRoutePermanent(string routeName, object routeValues) {
2323             RedirectToRoute(routeName, new RouteValueDictionary(routeValues), true);
2324         }
2325
2326         public void RedirectToRoutePermanent(string routeName, RouteValueDictionary routeValues) {
2327             RedirectToRoute(routeName, routeValues, true);
2328         }
2329
2330
2331         /// <devdoc>
2332         ///    <para>Redirects a client to a new URL with a 301.</para>
2333         /// </devdoc>
2334         [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
2335             Justification="Warning was suppressed for consistency with existing similar Redirect API")]
2336         public void RedirectPermanent(String url) {
2337             Redirect(url, true, true);
2338         }
2339
2340         /// <devdoc>
2341         ///    <para>Redirects a client to a new URL with a 301.</para>
2342         /// </devdoc>
2343         [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
2344             Justification = "Warning was suppressed for consistency with existing similar Redirect API")]
2345         public void RedirectPermanent(String url, bool endResponse) {
2346             Redirect(url, endResponse, true);
2347         }
2348
2349         internal void Redirect(String url, bool endResponse, bool permanent) {
2350 #if DBG
2351             string originalUrl = url;
2352 #endif
2353             if (url == null)
2354                 throw new ArgumentNullException("url");
2355
2356             if (url.IndexOf('\n') >= 0)
2357                 throw new ArgumentException(SR.GetString(SR.Cannot_redirect_to_newline));
2358
2359             if (_headersWritten)
2360                 throw new HttpException(SR.GetString(SR.Cannot_redirect_after_headers_sent));
2361
2362             Page page = _context.Handler as Page;
2363             if ((page != null) && page.IsCallback) {
2364                 throw new ApplicationException(SR.GetString(SR.Redirect_not_allowed_in_callback));
2365             }
2366
2367             url = ApplyRedirectQueryStringIfRequired(url);
2368
2369             url = ApplyAppPathModifier(url);
2370
2371             url = ConvertToFullyQualifiedRedirectUrlIfRequired(url);
2372
2373             url = UrlEncodeRedirect(url);
2374
2375             Clear();
2376
2377             // If it's a Page and SmartNavigation is on, return a short script
2378             // to perform the redirect instead of returning a 302 (bugs ASURT 82331/86782)
2379 #pragma warning disable 0618    // To avoid SmartNavigation deprecation warning
2380             if (page != null && page.IsPostBack && page.SmartNavigation && (Request["__smartNavPostBack"] == "true")) {
2381 #pragma warning restore 0618
2382                 Write("<BODY><ASP_SMARTNAV_RDIR url=\"");
2383                 Write(HttpUtility.HtmlEncode(url));
2384                 Write("\"></ASP_SMARTNAV_RDIR>");
2385
2386                 Write("</BODY>");
2387             }
2388             else {
2389                 this.StatusCode = permanent ? 301 : 302;
2390                 RedirectLocation = url;
2391                 // DevDivBugs 158137: 302 Redirect vulnerable to XSS
2392                 // A ---- of protocol identifiers. We don't want to UrlEncode
2393                 // URLs matching these schemes in order to not break the
2394                 // physical Object Moved to link.
2395                 if (UriUtil.IsSafeScheme(url)) {
2396                     url = HttpUtility.HtmlAttributeEncode(url);
2397                 }
2398                 else {
2399                     url = HttpUtility.HtmlAttributeEncode(HttpUtility.UrlEncode(url));
2400                 }
2401                 Write("<html><head><title>Object moved</title></head><body>\r\n");
2402                 Write("<h2>Object moved to <a href=\"" + url + "\">here</a>.</h2>\r\n");
2403                 Write("</body></html>\r\n");
2404             }
2405
2406             _isRequestBeingRedirected = true;
2407
2408 #if DBG
2409             Debug.Trace("ClientUrl", "*** Redirect (" + originalUrl + ") --> " + RedirectLocation + " ***");
2410 #endif
2411
2412             var redirectingHandler = Redirecting;
2413             if (redirectingHandler != null) {
2414                 redirectingHandler(this, EventArgs.Empty);
2415             }
2416
2417             if (endResponse)
2418                 End();
2419         }
2420
2421         internal string ApplyRedirectQueryStringIfRequired(string url) {
2422             if (Request == null || (string)Request.Browser["requiresPostRedirectionHandling"] != "true")
2423                 return url;
2424
2425             Page page = _context.Handler as Page;
2426             if (page != null && !page.IsPostBack)
2427                 return url;
2428
2429             //do not add __redir=1 if it already exists
2430             int i = url.IndexOf(RedirectQueryStringAssignment, StringComparison.Ordinal);
2431             if(i == -1) {
2432                 i = url.IndexOf('?');
2433                 if (i >= 0) {
2434                     url = url.Insert(i + 1, _redirectQueryStringInline);
2435                 }
2436                 else {
2437                     url = String.Concat(url, _redirectQueryString);
2438                 }
2439             }
2440             return url;
2441         }
2442
2443         //
2444         // Redirect to error page appending ?aspxerrorpath if no query string in the url.
2445         // Fails to redirect if request is already for error page.
2446         // Suppresses all errors.
2447         // See comments on RedirectToErrorPageStatus type for meaning of return values.
2448         //
2449         internal RedirectToErrorPageStatus RedirectToErrorPage(String url, CustomErrorsRedirectMode redirectMode) {
2450             const String qsErrorMark = "aspxerrorpath";
2451
2452             try {
2453                 if (String.IsNullOrEmpty(url))
2454                     return RedirectToErrorPageStatus.NotAttempted;   // nowhere to redirect
2455
2456                 if (_headersWritten)
2457                     return RedirectToErrorPageStatus.NotAttempted;
2458
2459                 if (Request.QueryString[qsErrorMark] != null)
2460                     return RedirectToErrorPageStatus.Failed;   // already in error redirect
2461
2462                 if (redirectMode == CustomErrorsRedirectMode.ResponseRewrite) {
2463                     Context.Server.Execute(url);
2464                 }
2465                 else {
2466                     // append query string
2467                     if (url.IndexOf('?') < 0)
2468                         url = url + "?" + qsErrorMark + "=" + HttpEncoderUtility.UrlEncodeSpaces(Request.Path);
2469
2470                     // redirect without response.end
2471                     Redirect(url, false /*endResponse*/);
2472                 }
2473             }
2474             catch {
2475                 return RedirectToErrorPageStatus.Failed;
2476             }
2477
2478             return RedirectToErrorPageStatus.Success;
2479         }
2480
2481         // Represents the result of calling RedirectToErrorPage
2482         internal enum RedirectToErrorPageStatus {
2483             NotAttempted, // Redirect or rewrite was not attempted, possibly because no redirect URL was specified
2484             Success, // Redirect or rewrite was attempted and succeeded
2485             Failed // Redirect or rewrite was attempted and failed, possibly due to the error page throwing
2486         }
2487
2488         // Implementation of the DefaultHttpHandler for IIS6+
2489         internal bool CanExecuteUrlForEntireResponse {
2490             get {
2491                 // if anything is sent, too late
2492                 if (_headersWritten) {
2493                     return false;
2494                 }
2495
2496                 // must have the right kind of worker request
2497                 if (_wr == null || !_wr.SupportsExecuteUrl) {
2498                     return false;
2499                 }
2500
2501                 // must not be capturing output to custom writer
2502                 if (!UsingHttpWriter) {
2503                     return false;
2504                 }
2505
2506                 // there is some cached output not yet sent
2507                 if (_httpWriter.GetBufferedLength() != 0) {
2508                     return false;
2509                 }
2510
2511                 // can't use execute url with filter installed
2512                 if (_httpWriter.FilterInstalled) {
2513                     return false;
2514                 }
2515
2516                 if (_cachePolicy != null && _cachePolicy.IsModified()) {
2517                     return false;
2518                 }
2519
2520                 return true;
2521             }
2522         }
2523
2524         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This is a safe critical method.")]
2525         internal IAsyncResult BeginExecuteUrlForEntireResponse(
2526                                     String pathOverride, NameValueCollection requestHeaders,
2527                                     AsyncCallback cb, Object state) {
2528             Debug.Assert(CanExecuteUrlForEntireResponse);
2529
2530             // prepare user information
2531             String userName, userAuthType;
2532             if (_context != null && _context.User != null) {
2533                 userName     = _context.User.Identity.Name;
2534                 userAuthType = _context.User.Identity.AuthenticationType;
2535             }
2536             else {
2537                 userName = String.Empty;
2538                 userAuthType = String.Empty;
2539             }
2540
2541             // get the path
2542             String path = Request.RewrittenUrl; // null is ok
2543
2544             if (pathOverride != null) {
2545                 path = pathOverride;
2546             }
2547
2548             // get the headers
2549             String headers = null;
2550
2551             if (requestHeaders != null) {
2552                 int numHeaders = requestHeaders.Count;
2553
2554                 if (numHeaders > 0) {
2555                     StringBuilder sb = new StringBuilder();
2556
2557                     for (int i = 0; i < numHeaders; i++) {
2558                         sb.Append(requestHeaders.GetKey(i));
2559                         sb.Append(": ");
2560                         sb.Append(requestHeaders.Get(i));
2561                         sb.Append("\r\n");
2562                     }
2563
2564                     headers = sb.ToString();
2565                 }
2566             }
2567
2568             byte[] entity = null;
2569             if (_context != null && _context.Request != null) {
2570                 entity = _context.Request.EntityBody;
2571             }
2572
2573             Debug.Trace("ExecuteUrl", "HttpResponse.BeginExecuteUrlForEntireResponse:" +
2574                 " path=" + path + " headers=" + headers +
2575                 " userName=" + userName + " authType=" + userAuthType);
2576
2577             // call worker request to start async execute url for this request
2578             IAsyncResult ar = _wr.BeginExecuteUrl(
2579                     path,
2580                     null, // this method
2581                     headers,
2582                     true, // let execute url send headers
2583                     true, // add user info
2584                     _wr.GetUserToken(),
2585                     userName,
2586                     userAuthType,
2587                     entity,
2588                     cb,
2589                     state);
2590
2591             // suppress further sends from ASP.NET
2592             // (only if succeeded starting async operation - not is 'finally' block)
2593             _headersWritten = true;
2594             _ended = true;
2595
2596             return ar;
2597         }
2598
2599         internal void EndExecuteUrlForEntireResponse(IAsyncResult result) {
2600             Debug.Trace("ExecuteUrl", "HttpResponse.EndExecuteUrlForEntireResponse");
2601             _wr.EndExecuteUrl(result);
2602         }
2603
2604         // Methods to write from file
2605
2606         //    Writes values to an HTTP output content stream.
2607         public void Write(String s) {
2608             _writer.Write(s);
2609         }
2610
2611         // Writes values to an HTTP output content stream.
2612         public void Write(Object obj) {
2613             _writer.Write(obj);
2614         }
2615
2616
2617         /// <devdoc>
2618         ///    <para>Writes values to an HTTP output content stream.</para>
2619         /// </devdoc>
2620         public void Write(char ch) {
2621             _writer.Write(ch);
2622         }
2623
2624
2625         /// <devdoc>
2626         ///    <para>Writes values to an HTTP output content stream.</para>
2627         /// </devdoc>
2628         public void Write(char[] buffer, int index, int count) {
2629             _writer.Write(buffer, index, count);
2630         }
2631
2632
2633         /// <devdoc>
2634         ///    <para>Writes a substition block to the response.</para>
2635         /// </devdoc>
2636         public void WriteSubstitution(HttpResponseSubstitutionCallback callback) {
2637             // cannot be instance method on a control
2638             if (callback.Target != null && callback.Target is Control) {
2639                 throw new ArgumentException(SR.GetString(SR.Invalid_substitution_callback), "callback");
2640             }
2641
2642             if (UsingHttpWriter) {
2643                 // HttpWriter can take substitution blocks
2644                 _httpWriter.WriteSubstBlock(callback, _wr as IIS7WorkerRequest);
2645             }
2646             else {
2647                 // text writer -- write as string
2648                 _writer.Write(callback(_context));
2649             }
2650
2651             // set the cache policy: reduce cachability from public to server
2652             if (_cachePolicy != null && _cachePolicy.GetCacheability() == HttpCacheability.Public)
2653                 _cachePolicy.SetCacheability(HttpCacheability.Server);
2654         }
2655
2656         /*
2657          * Helper method to write from file stream
2658          *
2659          * Handles only TextWriter case. For real requests
2660          * HttpWorkerRequest can take files
2661          */
2662         private void WriteStreamAsText(Stream f, long offset, long size) {
2663             if (size < 0)
2664                 size = f.Length - offset;
2665
2666             if (size > 0) {
2667                 if (offset > 0)
2668                     f.Seek(offset, SeekOrigin.Begin);
2669
2670                 byte[] fileBytes = new byte[(int)size];
2671                 int bytesRead = f.Read(fileBytes, 0, (int)size);
2672                 _writer.Write(Encoding.Default.GetChars(fileBytes, 0, bytesRead));
2673             }
2674         }
2675
2676         // support for VirtualPathProvider
2677         internal void WriteVirtualFile(VirtualFile vf) {
2678             Debug.Trace("WriteVirtualFile", vf.Name);
2679
2680             using (Stream s = vf.Open()) {
2681                 if (UsingHttpWriter) {
2682                     long size = s.Length;
2683
2684                     if (size > 0) {
2685                         // write as memory block
2686                         byte[] fileBytes = new byte[(int)size];
2687                         int bytesRead = s.Read(fileBytes, 0, (int) size);
2688                         _httpWriter.WriteBytes(fileBytes, 0, bytesRead);
2689                     }
2690                 }
2691                 else {
2692                     // Write file contents
2693                     WriteStreamAsText(s, 0, -1);
2694                 }
2695             }
2696         }
2697
2698         // Helper method to get absolute physical filename from the argument to WriteFile
2699         private String GetNormalizedFilename(String fn) {
2700             // If it's not a physical path, call MapPath on it
2701             if (!UrlPath.IsAbsolutePhysicalPath(fn)) {
2702                 if (Request != null)
2703                     fn = Request.MapPath(fn); // relative to current request
2704                 else
2705                     fn = HostingEnvironment.MapPath(fn);
2706             }
2707
2708             return fn;
2709         }
2710
2711         // Write file
2712         ///  Writes a named file directly to an HTTP content output stream.
2713         public void WriteFile(String filename) {
2714             if (filename == null) {
2715                 throw new ArgumentNullException("filename");
2716             }
2717
2718             WriteFile(filename, false);
2719         }
2720
2721         /*
2722          * Write file
2723          *
2724          * @param filename file to write
2725          * @readIntoMemory flag to read contents into memory immediately
2726          */
2727
2728         /// <devdoc>
2729         ///    <para> Reads a file into a memory block.</para>
2730         /// </devdoc>
2731         public void WriteFile(String filename, bool readIntoMemory) {
2732             if (filename == null) {
2733                 throw new ArgumentNullException("filename");
2734             }
2735
2736             filename = GetNormalizedFilename(filename);
2737
2738             FileStream f = null;
2739
2740             try {
2741                 f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
2742
2743                 if (UsingHttpWriter) {
2744                     long size = f.Length;
2745
2746                     if (size > 0) {
2747                         if (readIntoMemory) {
2748                             // write as memory block
2749                             byte[] fileBytes = new byte[(int)size];
2750                             int bytesRead = f.Read(fileBytes, 0, (int) size);
2751                             _httpWriter.WriteBytes(fileBytes, 0, bytesRead);
2752                         }
2753                         else {
2754                             // write as file block
2755                             f.Close(); // close before writing
2756                             f = null;
2757                             _httpWriter.WriteFile(filename, 0, size);
2758                         }
2759                     }
2760                 }
2761                 else {
2762                     // Write file contents
2763                     WriteStreamAsText(f, 0, -1);
2764                 }
2765             }
2766             finally {
2767                 if (f != null)
2768                     f.Close();
2769             }
2770         }
2771
2772
2773         public void TransmitFile(string filename) {
2774             TransmitFile(filename, 0, -1);
2775         }
2776         public void TransmitFile(string filename, long offset, long length) {
2777             if (filename == null) {
2778                 throw new ArgumentNullException("filename");
2779             }
2780             if (offset < 0)
2781                 throw new ArgumentException(SR.GetString(SR.Invalid_range), "offset");
2782             if (length < -1)
2783                 throw new ArgumentException(SR.GetString(SR.Invalid_range), "length");
2784
2785             filename = GetNormalizedFilename(filename);
2786
2787             long size;
2788             using (FileStream f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
2789                 size = f.Length;
2790                 // length of -1 means send rest of file
2791                 if (length == -1) {
2792                     length =  size - offset;
2793                 }
2794                 if (size < offset) {
2795                     throw new ArgumentException(SR.GetString(SR.Invalid_range), "offset");
2796                 }
2797                 else if ((size - offset) < length) {
2798                     throw new ArgumentException(SR.GetString(SR.Invalid_range), "length");
2799                 }
2800                 if (!UsingHttpWriter) {
2801                     WriteStreamAsText(f, offset, length);
2802                     return;
2803                 }
2804             }
2805
2806             if (length > 0) {
2807                 bool supportsLongTransmitFile = (_wr != null && _wr.SupportsLongTransmitFile);
2808
2809                 _httpWriter.TransmitFile(filename, offset, length,
2810                    _context.IsClientImpersonationConfigured || HttpRuntime.IsOnUNCShareInternal, supportsLongTransmitFile);
2811             }
2812         }
2813
2814
2815         private void ValidateFileRange(String filename, long offset, long length) {
2816             FileStream f = null;
2817
2818             try {
2819                 f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
2820
2821                 long fileSize = f.Length;
2822
2823                 if (length == -1)
2824                     length = fileSize - offset;
2825
2826                 if (offset < 0 || length > fileSize - offset)
2827                     throw new HttpException(SR.GetString(SR.Invalid_range));
2828             }
2829             finally {
2830                 if (f != null)
2831                     f.Close();
2832             }
2833         }
2834
2835         /*
2836          * Write file
2837          *
2838          * @param filename file to write
2839          * @param offset file offset to start writing
2840          * @param size number of bytes to write
2841          */
2842
2843         /// <devdoc>
2844         ///    <para>Writes a file directly to an HTTP content output stream.</para>
2845         /// </devdoc>
2846         public void WriteFile(String filename, long offset, long size) {
2847             if (filename == null) {
2848                 throw new ArgumentNullException("filename");
2849             }
2850
2851             if (size == 0)
2852                 return;
2853
2854             filename = GetNormalizedFilename(filename);
2855
2856             ValidateFileRange(filename, offset, size);
2857
2858             if (UsingHttpWriter) {
2859                 // HttpWriter can take files -- don't open here (but Demand permission)
2860                 InternalSecurityPermissions.FileReadAccess(filename).Demand();
2861                 _httpWriter.WriteFile(filename, offset, size);
2862             }
2863             else {
2864                 FileStream f = null;
2865
2866                 try {
2867                     f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
2868                     WriteStreamAsText(f, offset, size);
2869                 }
2870                 finally {
2871                     if (f != null)
2872                         f.Close();
2873                 }
2874             }
2875         }
2876
2877         /*
2878          * Write file
2879          *
2880          * @param handle file to write
2881          * @param offset file offset to start writing
2882          * @param size number of bytes to write
2883          */
2884
2885         /// <devdoc>
2886         ///    <para>Writes a file directly to an HTTP content output stream.</para>
2887         /// </devdoc>
2888         [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]
2889         public void WriteFile(IntPtr fileHandle, long offset, long size) {
2890             if (size <= 0)
2891                 return;
2892
2893             FileStream f = null;
2894
2895             try {
2896                 f = new FileStream(new Microsoft.Win32.SafeHandles.SafeFileHandle(fileHandle,false), FileAccess.Read);
2897
2898                 if (UsingHttpWriter) {
2899                     long fileSize = f.Length;
2900
2901                     if (size == -1)
2902                         size = fileSize - offset;
2903
2904                     if (offset < 0 || size > fileSize - offset)
2905                         throw new HttpException(SR.GetString(SR.Invalid_range));
2906
2907                     if (offset > 0)
2908                         f.Seek(offset, SeekOrigin.Begin);
2909
2910                     // write as memory block
2911                     byte[] fileBytes = new byte[(int)size];
2912                     int bytesRead = f.Read(fileBytes, 0, (int)size);
2913                     _httpWriter.WriteBytes(fileBytes, 0, bytesRead);
2914                 }
2915                 else {
2916                     WriteStreamAsText(f, offset, size);
2917                 }
2918             }
2919             finally {
2920                 if (f != null)
2921                     f.Close();
2922             }
2923         }
2924
2925         /// <devdoc>
2926         ///    <para>Allows HTTP/2 Server Push</para>
2927         /// </devdoc>
2928         public void PushPromise(string path) {
2929             // 
2930
2931
2932             PushPromise(path, method: "GET", headers: null);
2933         }
2934
2935         /// <devdoc>
2936         ///    <para>Allows HTTP/2 Server Push</para>
2937         /// </devdoc>
2938         public void PushPromise(string path, string method, NameValueCollection headers) {
2939             // PushPromise is non-deterministic and application shouldn't have logic that depends on it. 
2940             // It's only purpose is performance advantage in some cases.
2941             // There are many conditions (protocol and implementation) that may cause to 
2942             // ignore the push requests completely.
2943             // The expectation is based on fire-and-forget 
2944
2945             if (path == null) {
2946                 throw new ArgumentNullException("path");
2947             }
2948
2949             if (method == null) {
2950                 throw new ArgumentNullException("method");
2951             }
2952
2953             // Extract an optional query string
2954             string queryString = string.Empty;
2955             int i = path.IndexOf('?');
2956
2957             if (i >= 0) {
2958                 if (i < path.Length - 1) {
2959                     queryString = path.Substring(i + 1);
2960                 }
2961
2962                 // Remove the query string portion from the path
2963                 path = path.Substring(0, i);
2964             }
2965
2966
2967             // Only virtual path is allowed:
2968             // "/path"   - origin relative
2969             // "~/path"  - app relative
2970             // "path"    - request relative
2971             // "../path" - reduced 
2972             if (string.IsNullOrEmpty(path) || !UrlPath.IsValidVirtualPathWithoutProtocol(path)) {
2973                 throw new ArgumentException(SR.GetString(SR.Invalid_path_for_push_promise, path));
2974             }
2975
2976             VirtualPath virtualPath = Request.FilePathObject.Combine(VirtualPath.Create(path));
2977
2978             try {
2979                 if (!HttpRuntime.UseIntegratedPipeline) {
2980                     throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
2981                 }
2982
2983                 // Do push promise
2984                 IIS7WorkerRequest wr = (IIS7WorkerRequest) _wr;
2985                 wr.PushPromise(virtualPath.VirtualPathString, queryString, method, headers);
2986             }
2987             catch (PlatformNotSupportedException e) {
2988                 // Ignore errors if push promise is not supported
2989                 if (Context.TraceIsEnabled) {
2990                     Context.Trace.Write("aspx", "Push promise is not supported", e);
2991                 }
2992             }
2993         }
2994
2995         //
2996         // Deprecated ASP compatibility methods and properties
2997         //
2998
2999
3000         /// <devdoc>
3001         ///    <para>
3002         ///       Same as StatusDescription. Provided only for ASP compatibility.
3003         ///    </para>
3004         /// </devdoc>
3005         public string Status {
3006             get {
3007                 return this.StatusCode.ToString(NumberFormatInfo.InvariantInfo) + " " + this.StatusDescription;
3008             }
3009
3010             set {
3011                 int code = 200;
3012                 String descr = "OK";
3013
3014                 try {
3015                     int i = value.IndexOf(' ');
3016                     code = Int32.Parse(value.Substring(0, i), CultureInfo.InvariantCulture);
3017                     descr = value.Substring(i+1);
3018                 }
3019                 catch {
3020                     throw new HttpException(SR.GetString(SR.Invalid_status_string));
3021                 }
3022
3023                 this.StatusCode = code;
3024                 this.StatusDescription = descr;
3025             }
3026         }
3027
3028
3029         /// <devdoc>
3030         ///    <para>
3031         ///       Same as BufferOutput. Provided only for ASP compatibility.
3032         ///    </para>
3033         /// </devdoc>
3034         public bool Buffer {
3035             get { return this.BufferOutput;}
3036             set { this.BufferOutput = value;}
3037         }
3038
3039
3040         /// <devdoc>
3041         ///    <para>Same as Appendheader. Provided only for ASP compatibility.</para>
3042         /// </devdoc>
3043         public void AddHeader(String name, String value) {
3044             AppendHeader(name, value);
3045         }
3046
3047         /*
3048          * Cancelles handler processing of the current request
3049          * throws special [non-]exception uncatchable by the user code
3050          * to tell application to stop module execution.
3051          */
3052
3053         /// <devdoc>
3054         ///    <para>Sends all currently buffered output to the client then closes the
3055         ///       socket connection.</para>
3056         /// </devdoc>
3057         public void End() {
3058             if (_context.IsInCancellablePeriod) {
3059                 AbortCurrentThread();
3060             }
3061             else {
3062                 // when cannot abort execution, flush and supress further output
3063                 _endRequiresObservation = true;
3064
3065                 if (!_flushing) { // ignore Reponse.End while flushing (in OnPreSendHeaders)
3066                     Flush();
3067                     _ended = true;
3068
3069                     if (_context.ApplicationInstance != null) {
3070                         _context.ApplicationInstance.CompleteRequest();
3071                     }
3072                 }
3073             }
3074         }
3075
3076         // Aborts the current thread if Response.End was called and not yet observed.
3077         internal void ObserveResponseEndCalled() {
3078             if (_endRequiresObservation) {
3079                 _endRequiresObservation = false;
3080                 AbortCurrentThread();
3081             }
3082         }
3083
3084         [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "Known issue, but required for proper operation of ASP.NET.")]
3085         [SecurityPermission(SecurityAction.Assert, ControlThread = true)]
3086         private static void AbortCurrentThread() {
3087             Thread.CurrentThread.Abort(new HttpApplication.CancelModuleException(false));
3088         }
3089
3090         /*
3091          * ASP compatible caching properties
3092          */
3093
3094
3095         /// <devdoc>
3096         ///    <para>
3097         ///       Gets or sets the time, in minutes, until cached
3098         ///       information will be removed from the cache. Provided for ASP compatiblility. Use
3099         ///       the <see cref='System.Web.HttpResponse.Cache'/>
3100         ///       Property instead.
3101         ///    </para>
3102         /// </devdoc>
3103         public int Expires {
3104             get {
3105                 return _expiresInMinutes;
3106             }
3107             set {
3108                 if (!_expiresInMinutesSet || value < _expiresInMinutes) {
3109                     _expiresInMinutes = value;
3110                     Cache.SetExpires(_context.Timestamp + new TimeSpan(0, _expiresInMinutes, 0));
3111                 }
3112             }
3113         }
3114
3115
3116         /// <devdoc>
3117         ///    <para>
3118         ///       Gets or sets the absolute time that cached information
3119         ///       will be removed from the cache. Provided for ASP compatiblility. Use the <see cref='System.Web.HttpResponse.Cache'/>
3120         ///       property instead.
3121         ///    </para>
3122         /// </devdoc>
3123         public DateTime ExpiresAbsolute {
3124             get {
3125                 return _expiresAbsolute;
3126             }
3127             set {
3128                 if (!_expiresAbsoluteSet || value < _expiresAbsolute) {
3129                     _expiresAbsolute = value;
3130                     Cache.SetExpires(_expiresAbsolute);
3131                 }
3132             }
3133         }
3134
3135
3136         /// <devdoc>
3137         ///    <para>
3138         ///       Provided for ASP compatiblility. Use the <see cref='System.Web.HttpResponse.Cache'/>
3139         ///       property instead.
3140         ///    </para>
3141         /// </devdoc>
3142         public string CacheControl {
3143             get {
3144                 if (_cacheControl == null) {
3145                     // the default
3146                     return "private";
3147                 }
3148
3149                 return _cacheControl;
3150             }
3151             set {
3152                 if (String.IsNullOrEmpty(value)) {
3153                     _cacheControl = null;
3154                     Cache.SetCacheability(HttpCacheability.NoCache);
3155                 }
3156                 else if (StringUtil.EqualsIgnoreCase(value, "private")) {
3157                     _cacheControl = value;
3158                     Cache.SetCacheability(HttpCacheability.Private);
3159                 }
3160                 else if (StringUtil.EqualsIgnoreCase(value, "public")) {
3161                     _cacheControl = value;
3162                     Cache.SetCacheability(HttpCacheability.Public);
3163                 }
3164                 else if (StringUtil.EqualsIgnoreCase(value, "no-cache")) {
3165                     _cacheControl = value;
3166                     Cache.SetCacheability(HttpCacheability.NoCache);
3167                 }
3168                 else {
3169                     throw new ArgumentException(SR.GetString(SR.Invalid_value_for_CacheControl, value));
3170                 }
3171             }
3172         }
3173
3174         internal void SetAppPathModifier(string appPathModifier) {
3175             if (appPathModifier != null && (
3176                 appPathModifier.Length == 0 ||
3177                 appPathModifier[0] == '/' ||
3178                 appPathModifier[appPathModifier.Length - 1] == '/')) {
3179
3180                 throw new ArgumentException(SR.GetString(SR.InvalidArgumentValue, "appPathModifier"));
3181             }
3182
3183             _appPathModifier = appPathModifier;
3184
3185             Debug.Trace("ClientUrl", "*** SetAppPathModifier (" + appPathModifier + ") ***");
3186         }
3187
3188
3189         public string ApplyAppPathModifier(string virtualPath) {
3190 #if DBG
3191             string originalUrl = virtualPath;
3192 #endif
3193             object ch = _context.CookielessHelper; // This ensures that the cookieless-helper is initialized and applies the AppPathModifier
3194             if (virtualPath == null)
3195                 return null;
3196
3197             if (UrlPath.IsRelativeUrl(virtualPath)) {
3198                 // DevDiv 173208: RewritePath returns an HTTP 500 error code when requested with certain user agents
3199                 // We should use ClientBaseDir instead of FilePathObject.
3200                 virtualPath = UrlPath.Combine(Request.ClientBaseDir.VirtualPathString, virtualPath);
3201             }
3202             else {
3203                 // ignore paths with http://server/... or //
3204                 if (!UrlPath.IsRooted(virtualPath) || virtualPath.StartsWith("//", StringComparison.Ordinal)) {
3205                     return virtualPath;
3206                 }
3207
3208                 virtualPath = UrlPath.Reduce(virtualPath);
3209             }
3210
3211             if (_appPathModifier == null || virtualPath.IndexOf(_appPathModifier, StringComparison.Ordinal) >= 0) {
3212 #if DBG
3213                 Debug.Trace("ClientUrl", "*** ApplyAppPathModifier (" + originalUrl + ") --> " + virtualPath + " ***");
3214 #endif
3215                 return virtualPath;
3216             }
3217             
3218             string appPath = HttpRuntime.AppDomainAppVirtualPathString;
3219
3220             int compareLength = appPath.Length;
3221             bool isVirtualPathShort = (virtualPath.Length == appPath.Length - 1);
3222             if (isVirtualPathShort) {
3223                 compareLength--;
3224             }
3225
3226             // String.Compare will throw exception if there aren't compareLength characters
3227             if (virtualPath.Length < compareLength) {
3228                 return virtualPath;
3229             }
3230
3231             if (!StringUtil.EqualsIgnoreCase(virtualPath, 0, appPath, 0, compareLength)) {
3232                 return virtualPath;
3233             }
3234
3235             if (isVirtualPathShort) {
3236                 virtualPath += "/";
3237             }
3238
3239             Debug.Assert(virtualPath.Length >= appPath.Length);
3240             if (virtualPath.Length == appPath.Length) {
3241                 virtualPath = virtualPath.Substring(0, appPath.Length) + _appPathModifier + "/";
3242             }
3243             else {
3244                 virtualPath =
3245                     virtualPath.Substring(0, appPath.Length) +
3246                     _appPathModifier +
3247                     "/" +
3248                     virtualPath.Substring(appPath.Length);
3249             }
3250 #if DBG
3251             Debug.Trace("ClientUrl", "*** ApplyAppPathModifier (" + originalUrl + ") --> " + virtualPath + " ***");
3252 #endif
3253
3254             return virtualPath;
3255         }
3256
3257         internal String RemoveAppPathModifier(string virtualPath) {
3258             if (String.IsNullOrEmpty(_appPathModifier))
3259                 return virtualPath;
3260
3261             int pos = virtualPath.IndexOf(_appPathModifier, StringComparison.Ordinal);
3262
3263             if (pos <= 0 || virtualPath[pos-1] != '/')
3264                 return virtualPath;
3265
3266             return virtualPath.Substring(0, pos-1) + virtualPath.Substring(pos + _appPathModifier.Length);
3267         }
3268
3269         internal bool UsePathModifier {
3270             get {
3271                 return !String.IsNullOrEmpty(_appPathModifier);
3272             }
3273         }
3274
3275         private String ConvertToFullyQualifiedRedirectUrlIfRequired(String url) {
3276             HttpRuntimeSection runtimeConfig = RuntimeConfig.GetConfig(_context).HttpRuntime;
3277             if (    runtimeConfig.UseFullyQualifiedRedirectUrl ||
3278                     (Request != null && (string)Request.Browser["requiresFullyQualifiedRedirectUrl"] == "true")) {
3279                 return (new Uri(Request.Url, url)).AbsoluteUri ;
3280             }
3281             else {
3282                 return url;
3283             }
3284         }
3285
3286         private String UrlEncodeIDNSafe(String url) {
3287             // Bug 86594: Should not encode the domain part of the url. For example,
3288             // http://Ãœbersite/Ãœberpage.aspx should only encode the 2nd Ãœ.
3289             // To accomplish this we must separate the scheme+host+port portion of the url from the path portion,
3290             // encode the path portion, then reconstruct the url.
3291             Debug.Assert(!url.Contains("?"), "Querystring should have been stripped off.");
3292
3293             string schemeAndAuthority;
3294             string path;
3295             string queryAndFragment;
3296             bool isValidUrl = UriUtil.TrySplitUriForPathEncode(url, out schemeAndAuthority, out path, out queryAndFragment, checkScheme: true);
3297
3298             if (isValidUrl) {
3299                 // only encode the path portion
3300                 return schemeAndAuthority + HttpEncoderUtility.UrlEncodeSpaces(HttpUtility.UrlEncodeNonAscii(path, Encoding.UTF8)) + queryAndFragment;
3301             }
3302             else {
3303                 // encode the entire URL
3304                 return HttpEncoderUtility.UrlEncodeSpaces(HttpUtility.UrlEncodeNonAscii(url, Encoding.UTF8));
3305             }
3306         }
3307
3308         private String UrlEncodeRedirect(String url) {
3309             // convert all non-ASCII chars before ? to %XX using UTF-8 and
3310             // after ? using Response.ContentEncoding
3311
3312             int iqs = url.IndexOf('?');
3313
3314             if (iqs >= 0) {
3315                 Encoding qsEncoding = (Request != null) ? Request.ContentEncoding : ContentEncoding;
3316                 url = UrlEncodeIDNSafe(url.Substring(0, iqs)) + HttpUtility.UrlEncodeNonAscii(url.Substring(iqs), qsEncoding);
3317             }
3318             else {
3319                 url = UrlEncodeIDNSafe(url);
3320             }
3321
3322             return url;
3323         }
3324
3325         internal void UpdateNativeResponse(bool sendHeaders)
3326         {
3327             IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
3328
3329             if (null == iis7WorkerRequest) {
3330                 return;
3331             }
3332
3333             // WOS 1841024 - Don't set _suppressContent to true for HEAD requests.  IIS needs the content
3334             // in order to correctly set the Content-Length header.
3335             // WOS 1634512 - need to clear buffers if _ended == true
3336             // WOS 1850019 - Breaking Change: ASP.NET v2.0: Content-Length is not correct for pages that call HttpResponse.SuppressContent
3337             if ((_suppressContent && Request != null && Request.HttpVerb != HttpVerb.HEAD) || _ended)
3338                 Clear();
3339
3340             bool needPush = false;
3341             // NOTE: This also sets the response encoding on the HttpWriter
3342             long bufferedLength = _httpWriter.GetBufferedLength();
3343
3344             //
3345             // Set headers and status
3346             //
3347             if (!_headersWritten)
3348             {
3349                 //
3350                 // Set status
3351                 //
3352                 // VSWhidbey 270635: We need to reset the status code for mobile devices.
3353                 if (UseAdaptiveError) {
3354
3355                     // VSWhidbey 288054: We should change the status code for cases
3356                     // that cannot be handled by mobile devices
3357                     // 4xx for Client Error and 5xx for Server Error in HTTP spec
3358                     int statusCode = StatusCode;
3359                     if (statusCode >= 400 && statusCode < 600) {
3360                         this.StatusCode = 200;
3361                     }
3362                 }
3363
3364                 // DevDiv #782830: Provide a hook where the application can change the response status code
3365                 // or response headers.
3366                 if (sendHeaders && !_onSendingHeadersSubscriptionQueue.IsEmpty) {
3367                     _onSendingHeadersSubscriptionQueue.FireAndComplete(cb => cb(Context));
3368                 }
3369
3370                 if (_statusSet) {
3371                     _wr.SendStatus(this.StatusCode, this.SubStatusCode, this.StatusDescription);
3372                     _statusSet = false;
3373                 }
3374
3375                 //
3376                 //  Set headers
3377                 //
3378                 if (!_suppressHeaders && !_clientDisconnected)
3379                 {
3380                     if (sendHeaders) {
3381                         EnsureSessionStateIfNecessary();
3382                     }
3383
3384                     // If redirect location set, write it through to IIS as a header
3385                     if (_redirectLocation != null && _redirectLocationSet) {
3386                         HttpHeaderCollection headers = Headers as HttpHeaderCollection;
3387                         headers.Set("Location", _redirectLocation);
3388                         _redirectLocationSet = false;
3389                     }
3390
3391                     // Check if there is buffered response
3392                     bool responseBuffered = bufferedLength > 0 || iis7WorkerRequest.IsResponseBuffered();
3393
3394                     //
3395                     // Generate Content-Type
3396                     //
3397                     if (_contentType != null                                              // Valid Content-Type
3398                         && (_contentTypeSetByManagedCaller                                // Explicitly set by managed caller 
3399                             || (_contentTypeSetByManagedHandler && responseBuffered))) {  // Implicitly set by managed handler and response is non-empty
3400                         HttpHeaderCollection headers = Headers as HttpHeaderCollection;
3401                         String contentType = AppendCharSetToContentType(_contentType);
3402                         headers.Set("Content-Type", contentType);
3403                     }
3404
3405                     //
3406                     // If cookies have been added/changed, set the corresponding headers
3407                     //
3408                     GenerateResponseHeadersForCookies();
3409
3410                     // Not calling WriteHeaders headers in Integrated mode.
3411                     // Instead, most headers are generated when the handler runs,
3412                     // or on demand as necessary.
3413                     // The only exception are the cache policy headers.
3414                     if (sendHeaders) {
3415
3416                         SuppressCachingCookiesIfNecessary();
3417
3418                         if (_cachePolicy != null) {
3419                             if (_cachePolicy.IsModified()) {
3420                                 ArrayList cacheHeaders = new ArrayList();
3421                                 _cachePolicy.GetHeaders(cacheHeaders, this);
3422                                 HttpHeaderCollection headers = Headers as HttpHeaderCollection;
3423                                 foreach (HttpResponseHeader header in cacheHeaders) {
3424                                     // set and override the header
3425                                     headers.Set(header.Name, header.Value);
3426                                 }
3427                             }
3428                         }
3429
3430                         needPush = true;
3431                     }
3432                 }
3433             }
3434
3435             if (_flushing && !_filteringCompleted) {
3436                 _httpWriter.FilterIntegrated(false, iis7WorkerRequest);
3437                 bufferedLength = _httpWriter.GetBufferedLength();
3438             }
3439
3440             if (!_clientDisconnected && (bufferedLength > 0 || needPush)) {
3441
3442                 if (bufferedLength == 0 ) {
3443                     if (_httpWriter.IgnoringFurtherWrites) {
3444                         return;
3445                     }
3446                 }
3447
3448                 // push HttpWriter buffers to worker request
3449                 _httpWriter.Send(_wr);
3450                 // push buffers through into native
3451                 iis7WorkerRequest.PushResponseToNative();
3452                 // dispose them (since they're copied or
3453                 // owned by native request)
3454                 _httpWriter.DisposeIntegratedBuffers();
3455             }
3456         }
3457
3458         private void ClearNativeResponse(bool clearEntity, bool clearHeaders, IIS7WorkerRequest wr) {
3459             wr.ClearResponse(clearEntity, clearHeaders);
3460             if (clearEntity) {
3461                 _httpWriter.ClearSubstitutionBlocks();
3462             }
3463         }
3464
3465         private void SuppressCachingCookiesIfNecessary() {
3466             // MSRC 11855 (DevDiv 297240 / 362405)
3467             // We should suppress caching cookies if non-shareable cookies are
3468             // present in the response. Since these cookies can cary sensitive information, 
3469             // we should set Cache-Control: no-cache=set-cookie if there is such cookie
3470             // This prevents all well-behaved caches (both intermediary proxies and any local caches
3471             // on the client) from storing this sensitive information.
3472             // 
3473             // Additionally, we should not set this header during an SSL request, as certain versions
3474             // of IE don't handle it properly and simply refuse to render the page. More info:
3475             // http://blogs.msdn.com/b/ieinternals/archive/2009/10/02/internet-explorer-cannot-download-over-https-when-no-cache.aspx
3476             //
3477             // Finally, we don't need to set 'no-cache' if the response is not publicly cacheable,
3478             // as ASP.NET won't cache the response (due to the cookies) and proxies won't cache
3479             // the response (due to Cache-Control: private).
3480             // If _cachePolicy isn't set, then Cache.GetCacheability() will contruct a default one (which causes Cache-Control: private)
3481             if (!Request.IsSecureConnection && ContainsNonShareableCookies() && Cache.GetCacheability() == HttpCacheability.Public) {
3482                 Cache.SetCacheability(HttpCacheability.NoCache, "Set-Cookie");
3483             }
3484
3485             // if there are any cookies, do not kernel cache the response
3486             if (_cachePolicy != null && _cookies != null && _cookies.Count != 0) {
3487                 _cachePolicy.SetHasSetCookieHeader();
3488                 // In integrated mode, the cookies will eventually be sent to IIS via IIS7WorkerRequest.SetUnknownResponseHeader,
3489                 // where we will disable both HTTP.SYS kernel cache and IIS user mode cache (DevDiv 113142 & 255268). In classic
3490                 // mode, the cookies will be sent to IIS via ISAPIWorkerRequest.SendUnknownResponseHeader and 
3491                 // ISAPIWorkerRequest.SendKnownResponseHeader (DevDiv 113142), where we also disables the kernel cache. So the 
3492                 // call of DisableKernelCache below is not really needed.
3493                 DisableKernelCache();
3494             }
3495         }
3496
3497         private void EnsureSessionStateIfNecessary() {
3498             // Ensure the session state is in complete state before sending the response headers
3499             // Due to optimization and delay initialization sometimes we create and store the session state id in ReleaseSessionState.
3500             // But it's too late in case of Flush. Session state id must be written (if used) before sending the headers.
3501             if (AppSettings.EnsureSessionStateLockedOnFlush) {
3502                 _context.EnsureSessionStateIfNecessary();
3503             }
3504         }
3505     }
3506
3507     internal enum CacheDependencyType {
3508         Files,
3509         CacheItems,
3510         VirtualPaths
3511     }
3512
3513     struct ResponseDependencyList {
3514         private ArrayList   _dependencies;
3515         private string[]    _dependencyArray;
3516         private DateTime    _oldestDependency;
3517         private string      _requestVirtualPath;
3518
3519         internal void AddDependency(string item, string argname) {
3520             if (item == null) {
3521                 throw new ArgumentNullException(argname);
3522             }
3523
3524             _dependencyArray = null;
3525
3526             if (_dependencies == null) {
3527                 _dependencies = new ArrayList(1);
3528             }
3529
3530             DateTime utcNow = DateTime.UtcNow;
3531
3532             _dependencies.Add(new ResponseDependencyInfo(
3533                     new string[] {item}, utcNow));
3534
3535             // _oldestDependency is initialized to MinValue and indicates that it must always be set
3536             if (_oldestDependency == DateTime.MinValue || utcNow < _oldestDependency)
3537                 _oldestDependency = utcNow;
3538         }
3539
3540         internal void AddDependencies(ArrayList items, string argname) {
3541             if (items == null) {
3542                 throw new ArgumentNullException(argname);
3543             }
3544
3545             string[] a = (string[]) items.ToArray(typeof(string));
3546             AddDependencies(a, argname, false);
3547         }
3548
3549         internal void AddDependencies(string[] items, string argname) {
3550             AddDependencies(items, argname, true);
3551         }
3552
3553         internal void AddDependencies(string[] items, string argname, bool cloneArray) {
3554             AddDependencies(items, argname, cloneArray, DateTime.UtcNow);
3555         }
3556
3557         internal void AddDependencies(string[] items, string argname, bool cloneArray, string requestVirtualPath) {
3558             if (requestVirtualPath == null)
3559                 throw new ArgumentNullException("requestVirtualPath");
3560
3561             _requestVirtualPath = requestVirtualPath;
3562             AddDependencies(items, argname, cloneArray, DateTime.UtcNow);
3563         }
3564
3565         internal void AddDependencies(string[] items, string argname, bool cloneArray, DateTime utcDepTime) {
3566             if (items == null) {
3567                 throw new ArgumentNullException(argname);
3568             }
3569
3570             string [] itemsLocal;
3571
3572             if (cloneArray) {
3573                 itemsLocal = (string[]) items.Clone();
3574             }
3575             else {
3576                 itemsLocal = items;
3577             }
3578
3579             foreach (string item in itemsLocal) {
3580                 if (String.IsNullOrEmpty(item)) {
3581                     throw new ArgumentNullException(argname);
3582                 }
3583             }
3584
3585             _dependencyArray = null;
3586
3587             if (_dependencies == null) {
3588                 _dependencies = new ArrayList(1);
3589             }
3590
3591             _dependencies.Add(new ResponseDependencyInfo(itemsLocal, utcDepTime));
3592
3593             // _oldestDependency is initialized to MinValue and indicates that it must always be set
3594             if (_oldestDependency == DateTime.MinValue || utcDepTime < _oldestDependency)
3595                 _oldestDependency = utcDepTime;
3596         }
3597
3598         internal bool HasDependencies() {
3599             if (_dependencyArray == null && _dependencies == null)
3600                 return false;
3601
3602             return true;
3603         }
3604
3605         internal string[] GetDependencies() {
3606             if (_dependencyArray == null && _dependencies != null) {
3607                 int size = 0;
3608                 foreach (ResponseDependencyInfo info in _dependencies) {
3609                     size += info.items.Length;
3610                 }
3611
3612                 _dependencyArray = new string[size];
3613
3614                 int index = 0;
3615                 foreach (ResponseDependencyInfo info in _dependencies) {
3616                     int length = info.items.Length;
3617                     Array.Copy(info.items, 0, _dependencyArray, index, length);
3618                     index += length;
3619                 }
3620             }
3621
3622             return _dependencyArray;
3623         }
3624
3625         // The caller of this method must dispose the cache dependencies
3626         internal CacheDependency CreateCacheDependency(CacheDependencyType dependencyType, CacheDependency dependency) {
3627             if (_dependencies != null) {
3628                 if (dependencyType == CacheDependencyType.Files
3629                     || dependencyType == CacheDependencyType.CacheItems) {
3630                     foreach (ResponseDependencyInfo info in _dependencies) {
3631                         CacheDependency dependencyOld = dependency;
3632                         try {
3633                             if (dependencyType == CacheDependencyType.Files) {
3634                                 dependency = new CacheDependency(0, info.items, null, dependencyOld, info.utcDate);
3635                             }
3636                             else {
3637                                 // We create a "public" CacheDepdency here, since the keys are for public items.
3638                                 dependency = new CacheDependency(null, info.items, dependencyOld,
3639                                                                  DateTimeUtil.ConvertToLocalTime(info.utcDate));
3640                             }
3641                         }
3642                         finally {
3643                             if (dependencyOld != null) {
3644                                 dependencyOld.Dispose();
3645                             }
3646                         }
3647                     }
3648                 }
3649                 else {
3650                     CacheDependency virtualDependency = null;
3651                     VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
3652                     if (vpp != null && _requestVirtualPath != null) {
3653                         virtualDependency = vpp.GetCacheDependency(_requestVirtualPath, GetDependencies(), _oldestDependency);
3654                     }
3655                     if (virtualDependency != null) {
3656                         AggregateCacheDependency tempDep = new AggregateCacheDependency();
3657                         tempDep.Add(virtualDependency);
3658                         if (dependency != null) {
3659                             tempDep.Add(dependency);
3660                         }
3661                         dependency = tempDep;
3662                     }
3663                 }
3664             }
3665
3666             return dependency;
3667         }
3668     }
3669
3670     internal class ResponseDependencyInfo {
3671         internal readonly string[]    items;
3672         internal readonly DateTime    utcDate;
3673
3674         internal ResponseDependencyInfo(string[] items, DateTime utcDate) {
3675             this.items = items;
3676             this.utcDate = utcDate;
3677         }
3678     }
3679 }
3680