1 //------------------------------------------------------------------------------
2 // <copyright file="HttpResponse.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
10 namespace System.Web {
13 using System.Threading;
14 using System.Threading.Tasks;
15 using System.Runtime.Serialization;
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;
26 using System.Configuration;
27 using System.Security.Permissions;
28 using System.Web.Management;
29 using System.Diagnostics.CodeAnalysis;
33 /// <para>Used in HttpResponse.WriteSubstitution.</para>
35 public delegate String HttpResponseSubstitutionCallback(HttpContext context);
39 /// <para> Enables type-safe server to browser communication. Used to
40 /// send Http output to a client.</para>
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
48 private HttpHeaderCollection _headers; // response header collection (IIS7+)
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;
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;
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;
97 private ErrorFormatter _overrideErrorFormatter;
100 int _expiresInMinutes;
101 bool _expiresInMinutesSet;
102 DateTime _expiresAbsolute;
103 bool _expiresAbsoluteSet;
104 string _cacheControl;
106 private bool _statusSet;
107 private int _subStatusCode;
108 private bool _versionHeaderSent;
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;
118 bool _transferEncodingSet;
121 // OnSendingHeaders pseudo-event
122 private SubscriptionQueue<Action<HttpContext>> _onSendingHeadersSubscriptionQueue = new SubscriptionQueue<Action<HttpContext>>();
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;
129 private static readonly String _redirectQueryString = "?" + RedirectQueryStringAssignment;
130 private static readonly String _redirectQueryStringInline = RedirectQueryStringAssignment + "&";
132 internal static event EventHandler Redirecting;
134 internal HttpContext Context {
135 get { return _context; }
136 set { _context = value; }
139 internal HttpRequest Request {
141 if (_context == null)
143 return _context.Request;
148 * Internal package visible constructor to create responses that
149 * have HttpWorkerRequest
151 * @param wr Worker Request
153 internal HttpResponse(HttpWorkerRequest wr, HttpContext context) {
156 // HttpWriter is created in InitResponseWriter
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) {
167 private bool UsingHttpWriter {
169 return (_httpWriter != null && _writer == _httpWriter);
173 internal void SetAllocatorProvider(IAllocatorProvider allocator) {
174 if (_httpWriter != null) {
175 _httpWriter.AllocatorProvider = allocator;
182 internal void Dispose() {
184 if (_httpWriter != null)
185 _httpWriter.RecycleBuffers();
186 // recycle dependencies
187 if (_cacheDependencyForResponse != null) {
188 _cacheDependencyForResponse.Dispose();
189 _cacheDependencyForResponse = null;
191 if (_userAddedDependencies != null) {
192 foreach (CacheDependency dep in _userAddedDependencies) {
195 _userAddedDependencies = null;
199 internal void InitResponseWriter() {
200 if (_httpWriter == null) {
201 _httpWriter = new HttpWriter(this);
203 _writer = _httpWriter;
208 // Private helper methods
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();
220 public bool HeadersWritten {
221 get { return _headersWritten; }
222 internal set { _headersWritten = value; }
225 internal ArrayList GenerateResponseHeadersIntegrated(bool forCache) {
226 ArrayList headers = new ArrayList();
227 HttpHeaderCollection responseHeaders = Headers as HttpHeaderCollection;
230 // copy all current response headers
231 foreach(String key in responseHeaders)
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)) {
248 if ( headerId >= 0 ) {
249 headers.Add(new HttpResponseHeader(headerId, responseHeaders[key]));
252 headers.Add(new HttpResponseHeader(key, responseHeaders[key]));
259 internal void GenerateResponseHeadersForCookies()
261 if (_cookies == null || (_cookies.Count == 0 && !_cookies.Changed))
262 return; // no cookies exist
264 HttpHeaderCollection headers = Headers as HttpHeaderCollection;
265 HttpResponseHeader cookieHeader = null;
266 HttpCookie cookie = null;
267 bool needToReset = false;
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)
278 for(int c = 0; c < _cookies.Count; c++)
280 cookie = _cookies[c];
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;
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
299 if (_cookies.Changed || needToReset)
301 // delete all set cookie headers
302 headers.Remove("Set-Cookie");
304 // write all the cookies again
305 for(int c = 0; c < _cookies.Count; c++)
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;
315 _cookies.Changed = false;
319 internal void GenerateResponseHeadersForHandler()
321 if ( !(_wr is IIS7WorkerRequest) ) {
325 String versionHeader = null;
327 // Generate the default headers associated with an ASP.NET handler
328 if (!_headersWritten && !_handlerHeadersGenerated) {
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);
335 HttpRuntimeSection runtimeConfig = config.HttpRuntime;
336 if (runtimeConfig != null) {
337 versionHeader = runtimeConfig.VersionHeader;
338 _sendCacheControlHeader = runtimeConfig.SendCacheControlHeader;
341 OutputCacheSection outputCacheConfig = config.OutputCache;
342 if (outputCacheConfig != null) {
343 _sendCacheControlHeader &= outputCacheConfig.SendCacheControlHeader;
346 // DevDiv #406078: Need programmatic way of disabling "Cache-Control: private" response header.
347 if (SuppressDefaultCacheControlHeader) {
348 _sendCacheControlHeader = false;
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");
357 // set the version header
358 if (!String.IsNullOrEmpty(versionHeader)) {
359 Headers.Set("X-AspNet-Version", versionHeader);
362 // Force content-type generation
363 _contentTypeSetByManagedHandler = true;
366 _handlerHeadersGenerated = true;
371 internal ArrayList GenerateResponseHeaders(bool forCache) {
372 ArrayList headers = new ArrayList();
373 bool sendCacheControlHeader = HttpRuntimeSection.DefaultSendCacheControlHeader;
375 // ASP.NET version header
378 if (!_versionHeaderSent) {
379 String versionHeader = null;
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);
386 HttpRuntimeSection runtimeConfig = config.HttpRuntime;
387 if (runtimeConfig != null) {
388 versionHeader = runtimeConfig.VersionHeader;
389 sendCacheControlHeader = runtimeConfig.SendCacheControlHeader;
392 OutputCacheSection outputCacheConfig = config.OutputCache;
393 if (outputCacheConfig != null) {
394 sendCacheControlHeader &= outputCacheConfig.SendCacheControlHeader;
397 if (!String.IsNullOrEmpty(versionHeader)) {
398 headers.Add(new HttpResponseHeader("X-AspNet-Version", versionHeader));
401 _versionHeaderSent = true;
406 if (_customHeaders != null) {
407 int numCustomHeaders = _customHeaders.Count;
408 for (int i = 0; i < numCustomHeaders; i++)
409 headers.Add(_customHeaders[i]);
412 // location of redirect
413 if (_redirectLocation != null) {
414 headers.Add(new HttpResponseHeader(HttpWorkerRequest.HeaderLocation, _redirectLocation));
417 // don't include headers that the cache changes or omits on a cache hit
420 if (_cookies != null) {
421 int numCookies = _cookies.Count;
423 for (int i = 0; i < numCookies; i++) {
424 headers.Add(_cookies[i].GetSetCookieHeader(Context));
429 if (_cachePolicy != null && _cachePolicy.IsModified()) {
430 _cachePolicy.GetHeaders(headers, this);
433 if (_cacheHeaders != null) {
434 headers.AddRange(_cacheHeaders);
438 * Ensure that cacheability is set to cache-control: private
439 * if it is not explicitly set.
441 if (!_cacheControlHeaderAdded && sendCacheControlHeader) {
442 headers.Add(new HttpResponseHeader(HttpWorkerRequest.HeaderCacheControl, "private"));
450 if ( _statusCode != 204 && _contentType != null) {
451 String contentType = AppendCharSetToContentType( _contentType );
452 headers.Add(new HttpResponseHeader(HttpWorkerRequest.HeaderContentType, contentType));
459 internal string AppendCharSetToContentType(string contentType)
461 String newContentType = contentType;
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;
475 return newContentType;
478 internal bool UseAdaptiveError {
480 return _useAdaptiveError;
483 _useAdaptiveError = value;
487 private void WriteHeaders() {
491 // Fire pre-send headers event
493 if (_context != null && _context.ApplicationInstance != null) {
494 _context.ApplicationInstance.RaiseOnPreSendRequestHeaders();
498 // VSWhidbey 270635: We need to reset the status code for mobile devices.
499 if (UseAdaptiveError) {
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;
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);
514 _wr.SendStatus(this.StatusCode, this.StatusDescription);
518 // unicode messes up the response badly
519 Debug.Assert(!this.HeaderEncoding.Equals(Encoding.Unicode));
520 _wr.SetHeaderEncoding(this.HeaderEncoding);
523 HttpResponseHeader header = null;
524 int n = (headers != null) ? headers.Count : 0;
525 for (int i = 0; i < n; i++)
527 header = headers[i] as HttpResponseHeader;
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;
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'};
541 private void Flush(bool finalFlush, bool async = false) {
542 // Already completed or inside Flush?
543 if (_completed || _flushing)
546 // Special case for non HTTP Writer
547 if (_httpWriter == null) {
552 // Avoid recursive flushes
555 bool needToClearBuffers = false;
558 IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
559 if (iis7WorkerRequest != null) {
560 // generate the handler headers if flushing
561 GenerateResponseHeadersForHandler();
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*/);
569 // force a synchronous send
570 iis7WorkerRequest.ExplicitFlush();
573 // always set after flush, successful or not
574 _headersWritten = true;
581 long bufferedLength = 0;
587 if (!_headersWritten) {
588 if (!_suppressHeaders && !_clientDisconnected) {
589 EnsureSessionStateIfNecessary();
592 bufferedLength = _httpWriter.GetBufferedLength();
594 // suppress content-type for empty responses
595 if (!_contentLengthSet && bufferedLength == 0 && _httpWriter != null)
598 SuppressCachingCookiesIfNecessary();
600 // generate response headers
603 // recalculate as sending headers might change it (PreSendHeaders)
604 bufferedLength = _httpWriter.GetBufferedLength();
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);
612 // Check if need chunking for HTTP/1.1
613 if (!_contentLengthSet && !_transferEncodingSet && _statusCode == 200) {
614 String protocol = _wr.GetHttpVersion();
616 if (protocol != null && protocol.Equals("HTTP/1.1")) {
617 AppendHeader(new HttpResponseHeader(HttpWorkerRequest.HeaderTransferEncoding, "chunked"));
621 bufferedLength = _httpWriter.GetBufferedLength();
628 _headersWritten = true;
631 bufferedLength = _httpWriter.GetBufferedLength();
635 // Filter and recalculate length if not done already
638 if (!_filteringCompleted) {
639 _httpWriter.Filter(false);
640 bufferedLength = _httpWriter.GetBufferedLength();
647 // suppress HEAD content unless overriden
648 if (!_suppressContentSet && Request != null && Request.HttpVerb == HttpVerb.HEAD)
649 _suppressContent = true;
651 if (_suppressContent || _ended) {
652 _httpWriter.ClearBuffers();
656 if (!_clientDisconnected) {
657 // Fire pre-send request event
658 if (_context != null && _context.ApplicationInstance != null)
659 _context.ApplicationInstance.RaiseOnPreSendRequestContent();
662 if (bufferedLength > 0) {
663 byte[] chunkPrefix = Encoding.ASCII.GetBytes(Convert.ToString(bufferedLength, 16) + "\r\n");
664 _wr.SendResponseFromMemory(chunkPrefix, chunkPrefix.Length);
666 _httpWriter.Send(_wr);
668 _wr.SendResponseFromMemory(s_chunkSuffix, s_chunkSuffix.Length);
672 _wr.SendResponseFromMemory(s_chunkEnd, s_chunkEnd.Length);
675 _httpWriter.Send(_wr);
679 needToClearBuffers = !finalFlush;
680 _wr.FlushResponse(finalFlush);
682 _wr.UpdateResponseCounters(finalFlush, (int)bufferedLength);
688 // Remember if completed
689 if (finalFlush && _headersWritten)
692 // clear buffers even if FlushResponse throws
693 if (needToClearBuffers)
694 _httpWriter.ClearBuffers();
698 internal void FinalFlushAtTheEndOfRequestProcessing() {
699 FinalFlushAtTheEndOfRequestProcessing(false);
702 internal void FinalFlushAtTheEndOfRequestProcessing(bool needPipelineCompletion) {
706 // Returns true if the HttpWorkerRequest supports asynchronous flush; otherwise false.
707 public bool SupportsAsyncFlush {
709 return (_wr != null && _wr.SupportsAsyncFlush);
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) {
721 throw new HttpException(SR.GetString(SR.Cannot_flush_completed_response));
723 // perform async flush if it is supported
724 if (_wr != null && _wr.SupportsAsyncFlush && !_context.IsInCancellablePeriod) {
726 return _wr.BeginFlush(callback, state);
729 // perform a [....] flush since async is not supported
730 FlushAsyncResult ar = new FlushAsyncResult(callback, state);
737 ar.Complete(0, HResults.S_OK, IntPtr.Zero, synchronous: true);
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();
751 _wr.EndFlush(asyncResult);
755 // finish [....] flush since async is not supported
756 if (asyncResult == null)
757 throw new ArgumentNullException("asyncResult");
758 FlushAsyncResult ar = asyncResult as FlushAsyncResult;
760 throw new ArgumentException(null, "asyncResult");
761 ar.ReleaseWaitHandleWhenSignaled();
762 if (ar.Error != null) {
767 public Task FlushAsync() {
768 return Task.Factory.FromAsync(BeginFlush, EndFlush, state: null);
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();
783 bool enableKernelCacheForVaryByStar = IsKernelCacheEnabledForVaryByStar();
785 // check cache policy
786 if (!_cachePolicy.IsKernelCacheable(Request, enableKernelCacheForVaryByStar)) {
790 // check configuration if the kernel mode cache is enabled
791 HttpRuntimeSection runtimeConfig = RuntimeConfig.GetLKGConfig(_context).HttpRuntime;
792 if (runtimeConfig == null || !runtimeConfig.EnableKernelOutputCache) {
796 double seconds = (_cachePolicy.UtcGetAbsoluteExpiration() - DateTime.UtcNow).TotalSeconds;
801 int secondsToLive = seconds < Int32.MaxValue ? (int) seconds : Int32.MaxValue;
802 string kernelCacheUrl = _wr.SetupKernelCaching(secondsToLive, originalCacheUrl, enableKernelCacheForVaryByStar);
804 if (kernelCacheUrl != null) {
805 // Tell cache policy not to use max-age as kernel mode cache doesn't update it
806 _cachePolicy.SetNoMaxAgeInCacheControl();
809 return kernelCacheUrl;
813 * Disable kernel caching for this response. If kernel caching is not supported, this method
814 * returns without performing any action.
816 public void DisableKernelCache() {
821 _wr.DisableKernelCache();
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.
828 public void DisableUserCache() {
833 _wr.DisableUserCache();
836 private bool IsKernelCacheEnabledForVaryByStar()
838 OutputCacheSection outputCacheConfig = RuntimeConfig.GetAppConfig().OutputCache;
839 return (_cachePolicy.IsVaryByStar && outputCacheConfig.EnableKernelCacheForVaryByStar);
842 internal void FilterOutput() {
843 if(_filteringCompleted) {
848 if (UsingHttpWriter) {
849 IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
850 if (iis7WorkerRequest != null) {
851 _httpWriter.FilterIntegrated(true, iis7WorkerRequest);
854 _httpWriter.Filter(true);
859 _filteringCompleted = true;
864 /// Prevents any other writes to the Response
866 internal void IgnoreFurtherWrites() {
867 if (UsingHttpWriter) {
868 _httpWriter.IgnoreFurtherWrites();
873 * Is the entire response buffered so far
875 internal bool IsBuffered() {
876 return !_headersWritten && UsingHttpWriter;
879 // Expose cookie collection to request
880 // Gets the HttpCookie collection sent by the current request.</para>
881 public HttpCookieCollection Cookies {
883 if (_cookies == null)
884 _cookies = new HttpCookieCollection(this, false);
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) {
902 internal HttpCookieCollection GetCookiesNoCreate() {
906 public NameValueCollection Headers {
908 if ( !(_wr is IIS7WorkerRequest) ) {
909 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
912 if (_headers == null) {
913 _headers = new HttpHeaderCollection(_wr, this, 16);
921 * Add dependency on a file to the current response
925 /// <para>Adds dependency on a file to the current response.</para>
927 public void AddFileDependency(String filename) {
928 _fileDependencyList.AddDependency(filename, "filename");
931 // Add dependency on a list of files to the current response
933 // Adds dependency on a group of files to the current response.
934 public void AddFileDependencies(ArrayList filenames) {
935 _fileDependencyList.AddDependencies(filenames, "filenames");
939 public void AddFileDependencies(string[] filenames) {
940 _fileDependencyList.AddDependencies(filenames, "filenames");
943 internal void AddVirtualPathDependencies(string[] virtualPaths) {
944 _virtualPathDependencyList.AddDependencies(virtualPaths, "virtualPaths", false, Request.Path);
947 internal void AddFileDependencies(string[] filenames, DateTime utcTime) {
948 _fileDependencyList.AddDependencies(filenames, "filenames", false, utcTime);
951 // Add dependency on another cache item to the response.
952 public void AddCacheItemDependency(string cacheKey) {
953 _cacheItemDependencyList.AddDependency(cacheKey, "cacheKey");
956 // Add dependency on a list of cache items to the response.
957 public void AddCacheItemDependencies(ArrayList cacheKeys) {
958 _cacheItemDependencyList.AddDependencies(cacheKeys, "cacheKeys");
962 public void AddCacheItemDependencies(string[] cacheKeys) {
963 _cacheItemDependencyList.AddDependencies(cacheKeys, "cacheKeys");
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");
971 if (dependencies.Length == 0) {
974 if (_cacheDependencyForResponse != null) {
975 throw new InvalidOperationException(SR.GetString(SR.Invalid_operation_cache_dependency));
977 if (_userAddedDependencies == null) {
978 // copy array argument contents so they can't be changed beneath us
979 _userAddedDependencies = (CacheDependency[]) dependencies.Clone();
982 CacheDependency[] deps = new CacheDependency[_userAddedDependencies.Length + dependencies.Length];
984 for (i = 0; i < _userAddedDependencies.Length; i++) {
985 deps[i] = _userAddedDependencies[i];
987 for (int j = 0; j < dependencies.Length; j++) {
988 deps[i + j] = dependencies[j];
990 _userAddedDependencies = deps;
992 Cache.SetDependencies(true);
995 public static void RemoveOutputCacheItem(string path) {
996 RemoveOutputCacheItem(path, null);
999 public static void RemoveOutputCacheItem(string path, string providerName) {
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));
1005 string key = OutputCacheModule.CreateOutputCachedItemKey(
1006 path, HttpVerb.GET, null, null);
1008 if (providerName == null) {
1009 OutputCache.Remove(key, (HttpContext)null);
1012 OutputCache.RemoveFromProvider(key, providerName);
1015 key = OutputCacheModule.CreateOutputCachedItemKey(
1016 path, HttpVerb.POST, null, null);
1018 if (providerName == null) {
1019 OutputCache.Remove(key, (HttpContext)null);
1022 OutputCache.RemoveFromProvider(key, providerName);
1026 // Check if there are file dependencies.
1027 internal bool HasFileDependencies() {
1028 return _fileDependencyList.HasDependencies();
1031 // Check if there are item dependencies.
1032 internal bool HasCacheItemDependencies() {
1033 return _cacheItemDependencyList.HasDependencies();
1036 internal CacheDependency CreateCacheDependencyForResponse() {
1037 if (_cacheDependencyForResponse == null) {
1038 CacheDependency dependency;
1040 // N.B. - add file dependencies last so that we hit the file changes monitor
1042 dependency = _cacheItemDependencyList.CreateCacheDependency(CacheDependencyType.CacheItems, null);
1043 dependency = _fileDependencyList.CreateCacheDependency(CacheDependencyType.Files, dependency);
1044 dependency = _virtualPathDependencyList.CreateCacheDependency(CacheDependencyType.VirtualPaths, dependency);
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);
1055 // clear it because we added them to the dependencies for the response
1056 _userAddedDependencies = null;
1057 _cacheDependencyForResponse = agg;
1060 _cacheDependencyForResponse = dependency;
1063 return _cacheDependencyForResponse;
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;
1075 throw new HttpException(SR.GetString(SR.Cannot_get_snapshot_if_not_buffered));
1077 IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
1080 if (!_suppressContent) {
1081 if (iis7WorkerRequest != null) {
1082 buffers = _httpWriter.GetIntegratedSnapshot(out hasSubstBlocks, iis7WorkerRequest);
1085 buffers = _httpWriter.GetSnapshot(out hasSubstBlocks);
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);
1100 headers = GenerateResponseHeaders(true);
1103 return new HttpRawResponse(statusCode, statusDescription, headers, buffers, hasSubstBlocks);
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));
1111 if (_httpWriter == null)
1112 throw new HttpException(SR.GetString(SR.Cannot_use_snapshot_for_TextWriter));
1117 StatusCode = rawResponse.StatusCode;
1118 StatusDescription = rawResponse.StatusDescription;
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);
1129 _httpWriter.UseSnapshot(rawResponse.Buffers);
1131 _suppressContent = !sendBody;
1134 internal void CloseConnectionAfterError() {
1135 _closeConnectionAfterError = true;
1138 private void WriteErrorMessage(Exception e, bool dontShowSensitiveErrors) {
1139 ErrorFormatter errorFormatter = null;
1140 CultureInfo uiculture = null, savedUiculture = null;
1141 bool needToRestoreUiculture = false;
1143 if (_context.DynamicUICulture != null) {
1144 // if the user set the culture dynamically use it
1145 uiculture = _context.DynamicUICulture;
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))) {
1152 uiculture = HttpServerUtility.CreateReadOnlyCultureInfo(globConfig.UICulture);
1159 // In Integrated mode, generate the necessary response headers for the error
1160 GenerateResponseHeadersForHandler();
1162 // set the UI culture
1163 if (uiculture != null) {
1164 savedUiculture = Thread.CurrentThread.CurrentUICulture;
1165 Thread.CurrentThread.CurrentUICulture = uiculture;
1166 needToRestoreUiculture = true;
1171 // Try to get an error formatter
1172 errorFormatter = GetErrorFormatter(e);
1174 Debug.Trace("internal", "Error stack for " + Request.Path, e);
1176 if (dontShowSensitiveErrors && !errorFormatter.CanBeShownToAllUsers)
1177 errorFormatter = new GenericApplicationErrorFormatter(Request.IsLocal);
1179 Debug.Trace("internal", "errorFormatter's type = " + errorFormatter.GetType());
1181 if (ErrorFormatter.RequiresAdaptiveErrorReporting(Context)) {
1182 _writer.Write(errorFormatter.GetAdaptiveErrorMessage(Context, dontShowSensitiveErrors));
1185 _writer.Write(errorFormatter.GetHtmlErrorMessage(dontShowSensitiveErrors));
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("-->");
1195 if (!dontShowSensitiveErrors && !Request.IsLocal ) {
1196 _writer.Write("<!-- \r\n");
1197 _writer.Write(SR.GetString(SR.Information_Disclosure_Warning));
1198 _writer.Write("-->");
1202 if (_closeConnectionAfterError) {
1208 // restore ui culture
1209 if (needToRestoreUiculture)
1210 Thread.CurrentThread.CurrentUICulture = savedUiculture;
1213 catch { // Protect against exception filters
1218 internal void SetOverrideErrorFormatter(ErrorFormatter errorFormatter) {
1219 _overrideErrorFormatter = errorFormatter;
1222 internal ErrorFormatter GetErrorFormatter(Exception e) {
1223 ErrorFormatter errorFormatter = null;
1225 if (_overrideErrorFormatter != null) {
1226 return _overrideErrorFormatter;
1229 // Try to get an error formatter
1230 errorFormatter = HttpException.GetErrorFormatter(e);
1232 if (errorFormatter == null) {
1233 ConfigurationException ce = e as ConfigurationException;
1234 if (ce != null && !String.IsNullOrEmpty(ce.Filename))
1235 errorFormatter = new ConfigErrorFormatter(ce);
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
1242 if (_statusCode == 404)
1243 errorFormatter = new PageNotFoundErrorFormatter(Request.Path);
1244 else if (_statusCode == 403)
1245 errorFormatter = new PageForbiddenErrorFormatter(Request.Path);
1247 if (e is System.Security.SecurityException)
1248 errorFormatter = new SecurityErrorFormatter(e);
1250 errorFormatter = new UnhandledErrorFormatter(e);
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;
1261 return errorFormatter;
1264 private void WriteOneExceptionStack(Exception e) {
1265 Exception subExcep = e.InnerException;
1266 if (subExcep != null)
1267 WriteOneExceptionStack(subExcep);
1269 string title = "[" + e.GetType().Name + "]";
1270 if (e.Message != null && e.Message.Length > 0)
1271 title += ": " + HttpUtility.HtmlEncode(e.Message);
1273 _writer.WriteLine(title);
1274 if (e.StackTrace != null)
1275 _writer.WriteLine(e.StackTrace);
1278 private void WriteExceptionStack(Exception e) {
1279 ConfigurationErrorsException errors = e as ConfigurationErrorsException;
1280 if (errors == null) {
1281 WriteOneExceptionStack(e);
1284 // Write the original exception to get the first error with
1285 // a full stack trace
1286 WriteOneExceptionStack(e);
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;
1298 _writer.WriteLine("---");
1299 WriteOneExceptionStack(ce);
1305 internal void ReportRuntimeError(Exception e, bool canThrow, bool localExecute) {
1306 CustomErrorsSection customErrorsSetting = null;
1307 bool useCustomErrors = false;
1313 // always try to disable IIS custom errors when we send an error
1315 _wr.TrySkipIisCustomErrors = true;
1318 if (!localExecute) {
1319 code = HttpException.GetHttpCodeForException(e);
1321 // Don't raise event for 404. See VSWhidbey 124147.
1323 WebBaseEvent.RaiseRuntimeError(e, this);
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);
1332 useCustomErrors = true;
1335 if (!_headersWritten) {
1336 // nothing sent yet - entire response
1339 code = HttpException.GetHttpCodeForException(e);
1342 // change 401 to 500 in case the config is not to impersonate
1343 if (code == 401 && !_context.IsClientImpersonationConfigured)
1346 if (_context.TraceIsEnabled)
1347 _context.Trace.StatusCode = code;
1349 if (!localExecute && useCustomErrors) {
1350 String redirect = (customErrorsSetting != null) ? customErrorsSetting.GetRedirectString(code) : null;
1352 RedirectToErrorPageStatus redirectStatus = RedirectToErrorPage(redirect, customErrorsSetting.RedirectMode);
1353 switch (redirectStatus) {
1354 case RedirectToErrorPageStatus.Success:
1355 // success - nothing to do
1358 case RedirectToErrorPageStatus.NotAttempted:
1359 // if no redirect display generic error
1362 WriteErrorMessage(e, dontShowSensitiveErrors: true);
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.
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;
1377 HttpException dummyException = new HttpException();
1378 dummyException.SetFormatter(new CustomErrorFailedErrorFormatter());
1379 WriteErrorMessage(dummyException, dontShowSensitiveErrors: true);
1386 WriteErrorMessage(e, dontShowSensitiveErrors: false);
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> </p><hr>\r\n\r\n");
1400 WriteErrorMessage(e, useCustomErrors);
1404 internal void SynchronizeStatus(int statusCode, int subStatusCode, string description) {
1405 _statusCode = statusCode;
1406 _subStatusCode = subStatusCode;
1407 _statusDescription = description;
1411 internal void SynchronizeHeader(int knownHeaderIndex, string name, string value) {
1412 HttpHeaderCollection headers = Headers as HttpHeaderCollection;
1413 headers.SynchronizeHeader(name, value);
1415 // unknown headers have an index < 0
1416 if (knownHeaderIndex < 0) {
1420 bool fHeadersWritten = HeadersWritten;
1421 HeadersWritten = false; // Turn off the warning for "Headers have been written and can not be set"
1423 switch (knownHeaderIndex) {
1424 case HttpWorkerRequest.HeaderCacheControl:
1425 _cacheControlHeaderAdded = true;
1427 case HttpWorkerRequest.HeaderContentType:
1428 _contentType = value;
1430 case HttpWorkerRequest.HeaderLocation:
1431 _redirectLocation = value;
1432 _redirectLocationSet = false;
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;
1448 HeadersWritten = fHeadersWritten;
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);
1462 // Public properties
1465 // Gets or sets the HTTP status code of output returned to client.
1466 public int StatusCode {
1472 if (_headersWritten)
1473 throw new HttpException(SR.GetString(SR.Cannot_set_status_after_headers_sent));
1475 if (_statusCode != value) {
1476 _statusCode = value;
1478 _statusDescription = null;
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
1488 public int SubStatusCode {
1490 if ( !(_wr is IIS7WorkerRequest) ) {
1491 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
1494 return _subStatusCode;
1497 if ( !(_wr is IIS7WorkerRequest) ) {
1498 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
1501 if (_headersWritten) {
1502 throw new HttpException(SR.GetString(SR.Cannot_set_status_after_headers_sent));
1505 _subStatusCode = value;
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;
1520 * Http status description string
1523 // Http status description string
1524 // Gets or sets the HTTP status string of output returned to the client.
1525 public String StatusDescription {
1527 if (_statusDescription == null)
1528 _statusDescription = HttpWorkerRequest.GetStatusDescription(_statusCode);
1530 return _statusDescription;
1534 if (_headersWritten)
1535 throw new HttpException(SR.GetString(SR.Cannot_set_status_after_headers_sent));
1537 if (value != null && value.Length > 512) // ASURT 124743
1538 throw new ArgumentOutOfRangeException("value");
1539 _statusDescription = value;
1544 public bool TrySkipIisCustomErrors {
1547 return _wr.TrySkipIisCustomErrors;
1553 _wr.TrySkipIisCustomErrors = value;
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.
1565 public bool SuppressFormsAuthenticationRedirect {
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 <httpRuntime> or <outputCache>. See
1575 /// http://msdn.microsoft.com/en-us/library/system.web.configuration.httpruntimesection.sendcachecontrolheader.aspx
1576 /// for more information on those config elements.
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.
1584 public bool SuppressDefaultCacheControlHeader {
1589 // Flag indicating to buffer the output
1590 // Gets or sets a value indicating whether HTTP output is buffered.
1591 public bool BufferOutput {
1593 return _bufferOutput;
1597 if (_bufferOutput != value) {
1598 _bufferOutput = value;
1600 if (_httpWriter != null)
1601 _httpWriter.UpdateResponseBuffering();
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"];
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") {
1632 /// <para>Gets or sets the
1633 /// HTTP MIME type of output.</para>
1635 public String ContentType {
1637 return _contentType;
1641 if (_headersWritten) {
1642 // Don't throw if the new content type is the same as the current one
1643 if (_contentType == value)
1646 throw new HttpException(SR.GetString(SR.Cannot_set_content_type_after_headers_sent));
1649 _contentTypeSetByManagedCaller = true;
1650 _contentType = value;
1655 // Gets or sets the HTTP charset of output.
1656 public String Charset {
1658 if (_charSet == null)
1659 _charSet = ContentEncoding.WebName;
1665 if (_headersWritten)
1666 throw new HttpException(SR.GetString(SR.Cannot_set_content_type_after_headers_sent));
1671 _charSet = String.Empty; // to differentiate between not set (default) and empty chatset
1673 _customCharSet = true;
1677 // Content encoding for conversion
1678 // Gets or sets the HTTP character set of output.
1679 public Encoding ContentEncoding {
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;
1687 if (_encoding == null)
1688 _encoding = Encoding.Default;
1696 throw new ArgumentNullException("value");
1698 if (_encoding == null || !_encoding.Equals(value)) {
1700 _encoder = null; // flush cached encoder
1702 if (_httpWriter != null)
1703 _httpWriter.UpdateResponseEncoding();
1709 public Encoding HeaderEncoding {
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;
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;
1722 return _headerEncoding;
1727 throw new ArgumentNullException("value");
1729 if (value.Equals(Encoding.Unicode)) {
1730 throw new HttpException(SR.GetString(SR.Invalid_header_encoding, value.WebName));
1733 if (_headerEncoding == null || !_headerEncoding.Equals(value)) {
1734 if (_headersWritten)
1735 throw new HttpException(SR.GetString(SR.Cannot_set_header_encoding_after_headers_sent));
1737 _headerEncoding = value;
1742 // Encoder cached for the current encoding
1743 internal Encoder ContentEncoder {
1745 if (_encoder == null) {
1746 Encoding e = ContentEncoding;
1747 _encoder = e.GetEncoder();
1749 // enable best fit mapping accoding to config
1750 // (doesn't apply to utf-8 which is the default, thus optimization)
1752 if (!e.Equals(Encoding.UTF8)) {
1753 bool enableBestFit = false;
1755 GlobalizationSection globConfig = RuntimeConfig.GetLKGConfig(_context).Globalization;
1756 if (globConfig != null) {
1757 enableBestFit = globConfig.EnableBestFitResponseEncoding;
1760 if (!enableBestFit) {
1761 // setting 'fallback' disables best fit mapping
1762 _encoder.Fallback = new EncoderReplacementFallback();
1771 // Returns the caching semantics of the Web page (expiration time, privacy, vary clauses).
1772 public HttpCachePolicy Cache {
1774 if (_cachePolicy == null) {
1775 _cachePolicy = new HttpCachePolicy();
1778 return _cachePolicy;
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 {
1786 return _cachePolicy != null;
1790 // Client connected flag
1791 // Gets a value indicating whether the client is still connected to the server.
1792 public bool IsClientConnected {
1794 if (_clientDisconnected)
1797 if (_wr != null && !_wr.IsClientConnected()) {
1798 _clientDisconnected = true;
1807 /// Returns a CancellationToken that is tripped when the client disconnects. This can be used
1808 /// to listen for async disconnect notifications.
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.
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.
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.
1825 /// - DO NOT wait on the CancellationToken.WaitHandle, as this defeats the purpose of an
1826 /// async notification and can cause deadlocks.
1828 /// - DO NOT call the CancellationToken.Register overloads which invoke the callback on
1829 /// the original SynchronizationContext.
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.
1835 /// - DO keep the callback methods short-running and non-blocking. Make every effort to
1836 /// avoid throwing exceptions from within the callback methods.
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.
1842 public CancellationToken ClientDisconnectedToken {
1844 IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
1845 CancellationToken cancellationToken;
1846 if (wr != null && wr.TryGetClientDisconnectedCancellationToken(out cancellationToken)) {
1847 return cancellationToken;
1850 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_75_Integrated));
1855 public bool IsRequestBeingRedirected {
1857 return _isRequestBeingRedirected;
1860 _isRequestBeingRedirected = value;
1866 /// <para>Gets or Sets a redirection string (value of location resposne header) for redirect response.</para>
1868 public String RedirectLocation {
1869 get { return _redirectLocation; }
1871 if (_headersWritten)
1872 throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent));
1874 _redirectLocation = value;
1875 _redirectLocationSet = true;
1884 /// <para>Closes the socket connection to a client.</para>
1886 public void Close() {
1887 if (!_clientDisconnected && !_completed && _wr != null) {
1888 _wr.CloseConnection();
1889 _clientDisconnected = true;
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; }
1900 internal TextWriter SwitchWriter(TextWriter writer) {
1901 TextWriter oldWriter = _writer;
1907 // Enables binary output to the outgoing Http content body.
1908 public Stream OutputStream {
1910 if (!UsingHttpWriter)
1911 throw new HttpException(SR.GetString(SR.OutputStream_NotAvail));
1913 return _httpWriter.OutputStream;
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);
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);
1930 // Specifies a wrapping filter object to modify HTTP entity body before transmission.
1931 public Stream Filter {
1933 if (UsingHttpWriter)
1934 return _httpWriter.GetCurrentFilter();
1940 if (UsingHttpWriter) {
1941 _httpWriter.InstallFilter(value);
1943 IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
1944 if (iis7WorkerRequest != null) {
1945 iis7WorkerRequest.ResponseFilterInstalled();
1949 throw new HttpException(SR.GetString(SR.Filtering_not_allowed));
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 {
1958 return _suppressContent;
1962 _suppressContent = value;
1963 _suppressContentSet = true;
1972 * Add Http custom header
1974 * @param name header name
1975 * @param value header value
1979 /// <para>Adds an HTTP
1980 /// header to the output stream.</para>
1982 public void AppendHeader(String name, String value) {
1983 bool isCacheHeader = false;
1985 if (_headersWritten)
1986 throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent));
1988 // some headers are stored separately or require special action
1989 int knownHeaderIndex = HttpWorkerRequest.GetKnownResponseHeaderIndex(name);
1991 switch (knownHeaderIndex) {
1992 case HttpWorkerRequest.HeaderContentType:
1993 ContentType = value;
1994 return; // don't keep as custom header
1996 case HttpWorkerRequest.HeaderContentLength:
1997 _contentLengthSet = true;
2000 case HttpWorkerRequest.HeaderLocation:
2001 RedirectLocation = value;
2002 return; // don't keep as custom header
2004 case HttpWorkerRequest.HeaderTransferEncoding:
2005 _transferEncodingSet = true;
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;
2019 // In integrated mode, write the headers directly
2020 if (_wr is IIS7WorkerRequest) {
2021 Headers.Add(name, value);
2026 // don't keep as custom header
2027 if (_cacheHeaders == null) {
2028 _cacheHeaders = new ArrayList();
2031 _cacheHeaders.Add(new HttpResponseHeader(knownHeaderIndex, value));
2035 HttpResponseHeader h;
2036 if (knownHeaderIndex >= 0)
2037 h = new HttpResponseHeader(knownHeaderIndex, value);
2039 h = new HttpResponseHeader(name, value);
2051 /// cookie to the output stream.
2054 public void AppendCookie(HttpCookie cookie) {
2055 if (_headersWritten)
2056 throw new HttpException(SR.GetString(SR.Cannot_append_cookie_after_headers_sent));
2058 Cookies.AddCookie(cookie, true);
2059 OnCookieAdd(cookie);
2066 public void SetCookie(HttpCookie cookie) {
2067 if (_headersWritten)
2068 throw new HttpException(SR.GetString(SR.Cannot_append_cookie_after_headers_sent));
2070 Cookies.AddCookie(cookie, false);
2071 OnCookieCollectionChange();
2074 internal void BeforeCookieCollectionChange() {
2075 if (_headersWritten)
2076 throw new HttpException(SR.GetString(SR.Cannot_modify_cookies_after_headers_sent));
2079 internal void OnCookieAdd(HttpCookie cookie) {
2080 // add to request's cookies as well
2081 Request.AddResponseCookie(cookie);
2084 internal void OnCookieCollectionChange() {
2085 // synchronize with request cookie collection
2086 Request.ResetCookies();
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));
2097 _statusDescription = null;
2099 _contentType = "text/html";
2100 _contentTypeSetByManagedCaller = false;
2102 _customCharSet = false;
2103 _contentLengthSet = false;
2105 _redirectLocation = null;
2106 _redirectLocationSet = false;
2107 _isRequestBeingRedirected = false;
2109 _customHeaders = null;
2111 if (_headers != null) {
2112 _headers.ClearInternal();
2115 _transferEncodingSet = false;
2118 if (_cookies != null) {
2120 Request.ResetCookies();
2123 if (_cachePolicy != null) {
2124 _cachePolicy.Reset();
2127 _cacheControlHeaderAdded = false;
2128 _cacheHeaders = null;
2130 _suppressHeaders = false;
2131 _suppressContent = false;
2132 _suppressContentSet = false;
2134 _expiresInMinutes = 0;
2135 _expiresInMinutesSet = false;
2136 _expiresAbsolute = DateTime.MinValue;
2137 _expiresAbsoluteSet = false;
2138 _cacheControl = null;
2140 IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
2141 if (iis7WorkerRequest != null) {
2142 // clear the native response as well
2143 ClearNativeResponse(false, true, iis7WorkerRequest);
2145 // We need to regenerate Cache-Control: private only when the handler is managed and
2146 // configuration has <outputCache sendCacheControlHeader="true" /> in system.web
2148 if (_handlerHeadersGenerated && _sendCacheControlHeader) {
2149 Headers.Set("Cache-Control", "private");
2151 _handlerHeadersGenerated = false;
2157 /// <para>Clears all content output from the buffer stream.</para>
2159 public void ClearContent() {
2164 * Clear response buffer and headers. (For ASP compat doesn't clear headers)
2168 /// <para>Clears all headers and content output from the buffer stream.</para>
2170 public void Clear() {
2171 if (UsingHttpWriter)
2172 _httpWriter.ClearBuffers();
2174 IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
2175 if (iis7WorkerRequest != null) {
2176 // clear the native response buffers too
2177 ClearNativeResponse(true, false, iis7WorkerRequest);
2184 * Clear response buffer and headers. Internal. Used to be 'Clear'.
2186 internal void ClearAll() {
2187 if (!_headersWritten)
2193 * Flush response currently buffered
2197 /// <para>Sends all currently buffered output to the client.</para>
2199 public void Flush() {
2201 throw new HttpException(SR.GetString(SR.Cannot_flush_completed_response));
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:
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
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");
2232 if (!(_wr is IIS7WorkerRequest)) {
2233 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
2236 if (HeadersWritten) {
2237 throw new HttpException(SR.GetString(SR.Cannot_call_method_after_headers_sent_generic));
2240 return _onSendingHeadersSubscriptionQueue.Enqueue(callback);
2244 * Append string to the log record
2246 * @param param string to append to the log record
2250 /// <para>Adds custom log information to the IIS log file.</para>
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);
2263 /// <para>Redirects a client to a new URL.</para>
2265 public void Redirect(String url) {
2266 Redirect(url, true, false);
2270 /// <para>Redirects a client to a new URL.</para>
2272 public void Redirect(String url, bool endResponse) {
2273 Redirect(url, endResponse, false);
2276 public void RedirectToRoute(object routeValues) {
2277 RedirectToRoute(new RouteValueDictionary(routeValues));
2280 public void RedirectToRoute(string routeName) {
2281 RedirectToRoute(routeName, (RouteValueDictionary)null, false);
2284 public void RedirectToRoute(RouteValueDictionary routeValues) {
2285 RedirectToRoute(null /* routeName */, routeValues, false);
2288 public void RedirectToRoute(string routeName, object routeValues) {
2289 RedirectToRoute(routeName, new RouteValueDictionary(routeValues), false);
2292 public void RedirectToRoute(string routeName, RouteValueDictionary routeValues) {
2293 RedirectToRoute(routeName, routeValues, false);
2296 private void RedirectToRoute(string routeName, RouteValueDictionary routeValues, bool permanent) {
2297 string destinationUrl = null;
2298 VirtualPathData data = RouteTable.Routes.GetVirtualPath(Request.RequestContext, routeName, routeValues);
2300 destinationUrl = data.VirtualPath;
2303 if (String.IsNullOrEmpty(destinationUrl)) {
2304 throw new InvalidOperationException(SR.GetString(SR.No_Route_Found_For_Redirect));
2307 Redirect(destinationUrl, false /* endResponse */, permanent);
2310 public void RedirectToRoutePermanent(object routeValues) {
2311 RedirectToRoutePermanent(new RouteValueDictionary(routeValues));
2314 public void RedirectToRoutePermanent(string routeName) {
2315 RedirectToRoute(routeName, (RouteValueDictionary)null, true);
2318 public void RedirectToRoutePermanent(RouteValueDictionary routeValues) {
2319 RedirectToRoute(null /* routeName */, routeValues, true);
2322 public void RedirectToRoutePermanent(string routeName, object routeValues) {
2323 RedirectToRoute(routeName, new RouteValueDictionary(routeValues), true);
2326 public void RedirectToRoutePermanent(string routeName, RouteValueDictionary routeValues) {
2327 RedirectToRoute(routeName, routeValues, true);
2332 /// <para>Redirects a client to a new URL with a 301.</para>
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);
2341 /// <para>Redirects a client to a new URL with a 301.</para>
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);
2349 internal void Redirect(String url, bool endResponse, bool permanent) {
2351 string originalUrl = url;
2354 throw new ArgumentNullException("url");
2356 if (url.IndexOf('\n') >= 0)
2357 throw new ArgumentException(SR.GetString(SR.Cannot_redirect_to_newline));
2359 if (_headersWritten)
2360 throw new HttpException(SR.GetString(SR.Cannot_redirect_after_headers_sent));
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));
2367 url = ApplyRedirectQueryStringIfRequired(url);
2369 url = ApplyAppPathModifier(url);
2371 url = ConvertToFullyQualifiedRedirectUrlIfRequired(url);
2373 url = UrlEncodeRedirect(url);
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>");
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);
2399 url = HttpUtility.HtmlAttributeEncode(HttpUtility.UrlEncode(url));
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");
2406 _isRequestBeingRedirected = true;
2409 Debug.Trace("ClientUrl", "*** Redirect (" + originalUrl + ") --> " + RedirectLocation + " ***");
2412 var redirectingHandler = Redirecting;
2413 if (redirectingHandler != null) {
2414 redirectingHandler(this, EventArgs.Empty);
2421 internal string ApplyRedirectQueryStringIfRequired(string url) {
2422 if (Request == null || (string)Request.Browser["requiresPostRedirectionHandling"] != "true")
2425 Page page = _context.Handler as Page;
2426 if (page != null && !page.IsPostBack)
2429 //do not add __redir=1 if it already exists
2430 int i = url.IndexOf(RedirectQueryStringAssignment, StringComparison.Ordinal);
2432 i = url.IndexOf('?');
2434 url = url.Insert(i + 1, _redirectQueryStringInline);
2437 url = String.Concat(url, _redirectQueryString);
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.
2449 internal RedirectToErrorPageStatus RedirectToErrorPage(String url, CustomErrorsRedirectMode redirectMode) {
2450 const String qsErrorMark = "aspxerrorpath";
2453 if (String.IsNullOrEmpty(url))
2454 return RedirectToErrorPageStatus.NotAttempted; // nowhere to redirect
2456 if (_headersWritten)
2457 return RedirectToErrorPageStatus.NotAttempted;
2459 if (Request.QueryString[qsErrorMark] != null)
2460 return RedirectToErrorPageStatus.Failed; // already in error redirect
2462 if (redirectMode == CustomErrorsRedirectMode.ResponseRewrite) {
2463 Context.Server.Execute(url);
2466 // append query string
2467 if (url.IndexOf('?') < 0)
2468 url = url + "?" + qsErrorMark + "=" + HttpEncoderUtility.UrlEncodeSpaces(Request.Path);
2470 // redirect without response.end
2471 Redirect(url, false /*endResponse*/);
2475 return RedirectToErrorPageStatus.Failed;
2478 return RedirectToErrorPageStatus.Success;
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
2488 // Implementation of the DefaultHttpHandler for IIS6+
2489 internal bool CanExecuteUrlForEntireResponse {
2491 // if anything is sent, too late
2492 if (_headersWritten) {
2496 // must have the right kind of worker request
2497 if (_wr == null || !_wr.SupportsExecuteUrl) {
2501 // must not be capturing output to custom writer
2502 if (!UsingHttpWriter) {
2506 // there is some cached output not yet sent
2507 if (_httpWriter.GetBufferedLength() != 0) {
2511 // can't use execute url with filter installed
2512 if (_httpWriter.FilterInstalled) {
2516 if (_cachePolicy != null && _cachePolicy.IsModified()) {
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);
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;
2537 userName = String.Empty;
2538 userAuthType = String.Empty;
2542 String path = Request.RewrittenUrl; // null is ok
2544 if (pathOverride != null) {
2545 path = pathOverride;
2549 String headers = null;
2551 if (requestHeaders != null) {
2552 int numHeaders = requestHeaders.Count;
2554 if (numHeaders > 0) {
2555 StringBuilder sb = new StringBuilder();
2557 for (int i = 0; i < numHeaders; i++) {
2558 sb.Append(requestHeaders.GetKey(i));
2560 sb.Append(requestHeaders.Get(i));
2564 headers = sb.ToString();
2568 byte[] entity = null;
2569 if (_context != null && _context.Request != null) {
2570 entity = _context.Request.EntityBody;
2573 Debug.Trace("ExecuteUrl", "HttpResponse.BeginExecuteUrlForEntireResponse:" +
2574 " path=" + path + " headers=" + headers +
2575 " userName=" + userName + " authType=" + userAuthType);
2577 // call worker request to start async execute url for this request
2578 IAsyncResult ar = _wr.BeginExecuteUrl(
2580 null, // this method
2582 true, // let execute url send headers
2583 true, // add user info
2591 // suppress further sends from ASP.NET
2592 // (only if succeeded starting async operation - not is 'finally' block)
2593 _headersWritten = true;
2599 internal void EndExecuteUrlForEntireResponse(IAsyncResult result) {
2600 Debug.Trace("ExecuteUrl", "HttpResponse.EndExecuteUrlForEntireResponse");
2601 _wr.EndExecuteUrl(result);
2604 // Methods to write from file
2606 // Writes values to an HTTP output content stream.
2607 public void Write(String s) {
2611 // Writes values to an HTTP output content stream.
2612 public void Write(Object obj) {
2618 /// <para>Writes values to an HTTP output content stream.</para>
2620 public void Write(char ch) {
2626 /// <para>Writes values to an HTTP output content stream.</para>
2628 public void Write(char[] buffer, int index, int count) {
2629 _writer.Write(buffer, index, count);
2634 /// <para>Writes a substition block to the response.</para>
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");
2642 if (UsingHttpWriter) {
2643 // HttpWriter can take substitution blocks
2644 _httpWriter.WriteSubstBlock(callback, _wr as IIS7WorkerRequest);
2647 // text writer -- write as string
2648 _writer.Write(callback(_context));
2651 // set the cache policy: reduce cachability from public to server
2652 if (_cachePolicy != null && _cachePolicy.GetCacheability() == HttpCacheability.Public)
2653 _cachePolicy.SetCacheability(HttpCacheability.Server);
2657 * Helper method to write from file stream
2659 * Handles only TextWriter case. For real requests
2660 * HttpWorkerRequest can take files
2662 private void WriteStreamAsText(Stream f, long offset, long size) {
2664 size = f.Length - offset;
2668 f.Seek(offset, SeekOrigin.Begin);
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));
2676 // support for VirtualPathProvider
2677 internal void WriteVirtualFile(VirtualFile vf) {
2678 Debug.Trace("WriteVirtualFile", vf.Name);
2680 using (Stream s = vf.Open()) {
2681 if (UsingHttpWriter) {
2682 long size = s.Length;
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);
2692 // Write file contents
2693 WriteStreamAsText(s, 0, -1);
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
2705 fn = HostingEnvironment.MapPath(fn);
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");
2718 WriteFile(filename, false);
2724 * @param filename file to write
2725 * @readIntoMemory flag to read contents into memory immediately
2729 /// <para> Reads a file into a memory block.</para>
2731 public void WriteFile(String filename, bool readIntoMemory) {
2732 if (filename == null) {
2733 throw new ArgumentNullException("filename");
2736 filename = GetNormalizedFilename(filename);
2738 FileStream f = null;
2741 f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
2743 if (UsingHttpWriter) {
2744 long size = f.Length;
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);
2754 // write as file block
2755 f.Close(); // close before writing
2757 _httpWriter.WriteFile(filename, 0, size);
2762 // Write file contents
2763 WriteStreamAsText(f, 0, -1);
2773 public void TransmitFile(string filename) {
2774 TransmitFile(filename, 0, -1);
2776 public void TransmitFile(string filename, long offset, long length) {
2777 if (filename == null) {
2778 throw new ArgumentNullException("filename");
2781 throw new ArgumentException(SR.GetString(SR.Invalid_range), "offset");
2783 throw new ArgumentException(SR.GetString(SR.Invalid_range), "length");
2785 filename = GetNormalizedFilename(filename);
2788 using (FileStream f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
2790 // length of -1 means send rest of file
2792 length = size - offset;
2794 if (size < offset) {
2795 throw new ArgumentException(SR.GetString(SR.Invalid_range), "offset");
2797 else if ((size - offset) < length) {
2798 throw new ArgumentException(SR.GetString(SR.Invalid_range), "length");
2800 if (!UsingHttpWriter) {
2801 WriteStreamAsText(f, offset, length);
2807 bool supportsLongTransmitFile = (_wr != null && _wr.SupportsLongTransmitFile);
2809 _httpWriter.TransmitFile(filename, offset, length,
2810 _context.IsClientImpersonationConfigured || HttpRuntime.IsOnUNCShareInternal, supportsLongTransmitFile);
2815 private void ValidateFileRange(String filename, long offset, long length) {
2816 FileStream f = null;
2819 f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
2821 long fileSize = f.Length;
2824 length = fileSize - offset;
2826 if (offset < 0 || length > fileSize - offset)
2827 throw new HttpException(SR.GetString(SR.Invalid_range));
2838 * @param filename file to write
2839 * @param offset file offset to start writing
2840 * @param size number of bytes to write
2844 /// <para>Writes a file directly to an HTTP content output stream.</para>
2846 public void WriteFile(String filename, long offset, long size) {
2847 if (filename == null) {
2848 throw new ArgumentNullException("filename");
2854 filename = GetNormalizedFilename(filename);
2856 ValidateFileRange(filename, offset, size);
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);
2864 FileStream f = null;
2867 f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
2868 WriteStreamAsText(f, offset, size);
2880 * @param handle file to write
2881 * @param offset file offset to start writing
2882 * @param size number of bytes to write
2886 /// <para>Writes a file directly to an HTTP content output stream.</para>
2888 [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]
2889 public void WriteFile(IntPtr fileHandle, long offset, long size) {
2893 FileStream f = null;
2896 f = new FileStream(new Microsoft.Win32.SafeHandles.SafeFileHandle(fileHandle,false), FileAccess.Read);
2898 if (UsingHttpWriter) {
2899 long fileSize = f.Length;
2902 size = fileSize - offset;
2904 if (offset < 0 || size > fileSize - offset)
2905 throw new HttpException(SR.GetString(SR.Invalid_range));
2908 f.Seek(offset, SeekOrigin.Begin);
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);
2916 WriteStreamAsText(f, offset, size);
2926 /// <para>Allows HTTP/2 Server Push</para>
2928 public void PushPromise(string path) {
2932 PushPromise(path, method: "GET", headers: null);
2936 /// <para>Allows HTTP/2 Server Push</para>
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
2946 throw new ArgumentNullException("path");
2949 if (method == null) {
2950 throw new ArgumentNullException("method");
2953 // Extract an optional query string
2954 string queryString = string.Empty;
2955 int i = path.IndexOf('?');
2958 if (i < path.Length - 1) {
2959 queryString = path.Substring(i + 1);
2962 // Remove the query string portion from the path
2963 path = path.Substring(0, i);
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));
2976 VirtualPath virtualPath = Request.FilePathObject.Combine(VirtualPath.Create(path));
2979 if (!HttpRuntime.UseIntegratedPipeline) {
2980 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
2984 IIS7WorkerRequest wr = (IIS7WorkerRequest) _wr;
2985 wr.PushPromise(virtualPath.VirtualPathString, queryString, method, headers);
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);
2996 // Deprecated ASP compatibility methods and properties
3002 /// Same as StatusDescription. Provided only for ASP compatibility.
3005 public string Status {
3007 return this.StatusCode.ToString(NumberFormatInfo.InvariantInfo) + " " + this.StatusDescription;
3012 String descr = "OK";
3015 int i = value.IndexOf(' ');
3016 code = Int32.Parse(value.Substring(0, i), CultureInfo.InvariantCulture);
3017 descr = value.Substring(i+1);
3020 throw new HttpException(SR.GetString(SR.Invalid_status_string));
3023 this.StatusCode = code;
3024 this.StatusDescription = descr;
3031 /// Same as BufferOutput. Provided only for ASP compatibility.
3034 public bool Buffer {
3035 get { return this.BufferOutput;}
3036 set { this.BufferOutput = value;}
3041 /// <para>Same as Appendheader. Provided only for ASP compatibility.</para>
3043 public void AddHeader(String name, String value) {
3044 AppendHeader(name, value);
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.
3054 /// <para>Sends all currently buffered output to the client then closes the
3055 /// socket connection.</para>
3058 if (_context.IsInCancellablePeriod) {
3059 AbortCurrentThread();
3062 // when cannot abort execution, flush and supress further output
3063 _endRequiresObservation = true;
3065 if (!_flushing) { // ignore Reponse.End while flushing (in OnPreSendHeaders)
3069 if (_context.ApplicationInstance != null) {
3070 _context.ApplicationInstance.CompleteRequest();
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();
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));
3091 * ASP compatible caching properties
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.
3103 public int Expires {
3105 return _expiresInMinutes;
3108 if (!_expiresInMinutesSet || value < _expiresInMinutes) {
3109 _expiresInMinutes = value;
3110 Cache.SetExpires(_context.Timestamp + new TimeSpan(0, _expiresInMinutes, 0));
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.
3123 public DateTime ExpiresAbsolute {
3125 return _expiresAbsolute;
3128 if (!_expiresAbsoluteSet || value < _expiresAbsolute) {
3129 _expiresAbsolute = value;
3130 Cache.SetExpires(_expiresAbsolute);
3138 /// Provided for ASP compatiblility. Use the <see cref='System.Web.HttpResponse.Cache'/>
3139 /// property instead.
3142 public string CacheControl {
3144 if (_cacheControl == null) {
3149 return _cacheControl;
3152 if (String.IsNullOrEmpty(value)) {
3153 _cacheControl = null;
3154 Cache.SetCacheability(HttpCacheability.NoCache);
3156 else if (StringUtil.EqualsIgnoreCase(value, "private")) {
3157 _cacheControl = value;
3158 Cache.SetCacheability(HttpCacheability.Private);
3160 else if (StringUtil.EqualsIgnoreCase(value, "public")) {
3161 _cacheControl = value;
3162 Cache.SetCacheability(HttpCacheability.Public);
3164 else if (StringUtil.EqualsIgnoreCase(value, "no-cache")) {
3165 _cacheControl = value;
3166 Cache.SetCacheability(HttpCacheability.NoCache);
3169 throw new ArgumentException(SR.GetString(SR.Invalid_value_for_CacheControl, value));
3174 internal void SetAppPathModifier(string appPathModifier) {
3175 if (appPathModifier != null && (
3176 appPathModifier.Length == 0 ||
3177 appPathModifier[0] == '/' ||
3178 appPathModifier[appPathModifier.Length - 1] == '/')) {
3180 throw new ArgumentException(SR.GetString(SR.InvalidArgumentValue, "appPathModifier"));
3183 _appPathModifier = appPathModifier;
3185 Debug.Trace("ClientUrl", "*** SetAppPathModifier (" + appPathModifier + ") ***");
3189 public string ApplyAppPathModifier(string virtualPath) {
3191 string originalUrl = virtualPath;
3193 object ch = _context.CookielessHelper; // This ensures that the cookieless-helper is initialized and applies the AppPathModifier
3194 if (virtualPath == null)
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);
3203 // ignore paths with http://server/... or //
3204 if (!UrlPath.IsRooted(virtualPath) || virtualPath.StartsWith("//", StringComparison.Ordinal)) {
3208 virtualPath = UrlPath.Reduce(virtualPath);
3211 if (_appPathModifier == null || virtualPath.IndexOf(_appPathModifier, StringComparison.Ordinal) >= 0) {
3213 Debug.Trace("ClientUrl", "*** ApplyAppPathModifier (" + originalUrl + ") --> " + virtualPath + " ***");
3218 string appPath = HttpRuntime.AppDomainAppVirtualPathString;
3220 int compareLength = appPath.Length;
3221 bool isVirtualPathShort = (virtualPath.Length == appPath.Length - 1);
3222 if (isVirtualPathShort) {
3226 // String.Compare will throw exception if there aren't compareLength characters
3227 if (virtualPath.Length < compareLength) {
3231 if (!StringUtil.EqualsIgnoreCase(virtualPath, 0, appPath, 0, compareLength)) {
3235 if (isVirtualPathShort) {
3239 Debug.Assert(virtualPath.Length >= appPath.Length);
3240 if (virtualPath.Length == appPath.Length) {
3241 virtualPath = virtualPath.Substring(0, appPath.Length) + _appPathModifier + "/";
3245 virtualPath.Substring(0, appPath.Length) +
3248 virtualPath.Substring(appPath.Length);
3251 Debug.Trace("ClientUrl", "*** ApplyAppPathModifier (" + originalUrl + ") --> " + virtualPath + " ***");
3257 internal String RemoveAppPathModifier(string virtualPath) {
3258 if (String.IsNullOrEmpty(_appPathModifier))
3261 int pos = virtualPath.IndexOf(_appPathModifier, StringComparison.Ordinal);
3263 if (pos <= 0 || virtualPath[pos-1] != '/')
3266 return virtualPath.Substring(0, pos-1) + virtualPath.Substring(pos + _appPathModifier.Length);
3269 internal bool UsePathModifier {
3271 return !String.IsNullOrEmpty(_appPathModifier);
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 ;
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.");
3293 string schemeAndAuthority;
3295 string queryAndFragment;
3296 bool isValidUrl = UriUtil.TrySplitUriForPathEncode(url, out schemeAndAuthority, out path, out queryAndFragment, checkScheme: true);
3299 // only encode the path portion
3300 return schemeAndAuthority + HttpEncoderUtility.UrlEncodeSpaces(HttpUtility.UrlEncodeNonAscii(path, Encoding.UTF8)) + queryAndFragment;
3303 // encode the entire URL
3304 return HttpEncoderUtility.UrlEncodeSpaces(HttpUtility.UrlEncodeNonAscii(url, Encoding.UTF8));
3308 private String UrlEncodeRedirect(String url) {
3309 // convert all non-ASCII chars before ? to %XX using UTF-8 and
3310 // after ? using Response.ContentEncoding
3312 int iqs = url.IndexOf('?');
3315 Encoding qsEncoding = (Request != null) ? Request.ContentEncoding : ContentEncoding;
3316 url = UrlEncodeIDNSafe(url.Substring(0, iqs)) + HttpUtility.UrlEncodeNonAscii(url.Substring(iqs), qsEncoding);
3319 url = UrlEncodeIDNSafe(url);
3325 internal void UpdateNativeResponse(bool sendHeaders)
3327 IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
3329 if (null == iis7WorkerRequest) {
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)
3340 bool needPush = false;
3341 // NOTE: This also sets the response encoding on the HttpWriter
3342 long bufferedLength = _httpWriter.GetBufferedLength();
3345 // Set headers and status
3347 if (!_headersWritten)
3352 // VSWhidbey 270635: We need to reset the status code for mobile devices.
3353 if (UseAdaptiveError) {
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;
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));
3371 _wr.SendStatus(this.StatusCode, this.SubStatusCode, this.StatusDescription);
3378 if (!_suppressHeaders && !_clientDisconnected)
3381 EnsureSessionStateIfNecessary();
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;
3391 // Check if there is buffered response
3392 bool responseBuffered = bufferedLength > 0 || iis7WorkerRequest.IsResponseBuffered();
3395 // Generate Content-Type
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);
3406 // If cookies have been added/changed, set the corresponding headers
3408 GenerateResponseHeadersForCookies();
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.
3416 SuppressCachingCookiesIfNecessary();
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);
3435 if (_flushing && !_filteringCompleted) {
3436 _httpWriter.FilterIntegrated(false, iis7WorkerRequest);
3437 bufferedLength = _httpWriter.GetBufferedLength();
3440 if (!_clientDisconnected && (bufferedLength > 0 || needPush)) {
3442 if (bufferedLength == 0 ) {
3443 if (_httpWriter.IgnoringFurtherWrites) {
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();
3458 private void ClearNativeResponse(bool clearEntity, bool clearHeaders, IIS7WorkerRequest wr) {
3459 wr.ClearResponse(clearEntity, clearHeaders);
3461 _httpWriter.ClearSubstitutionBlocks();
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.
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
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");
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();
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();
3507 internal enum CacheDependencyType {
3513 struct ResponseDependencyList {
3514 private ArrayList _dependencies;
3515 private string[] _dependencyArray;
3516 private DateTime _oldestDependency;
3517 private string _requestVirtualPath;
3519 internal void AddDependency(string item, string argname) {
3521 throw new ArgumentNullException(argname);
3524 _dependencyArray = null;
3526 if (_dependencies == null) {
3527 _dependencies = new ArrayList(1);
3530 DateTime utcNow = DateTime.UtcNow;
3532 _dependencies.Add(new ResponseDependencyInfo(
3533 new string[] {item}, utcNow));
3535 // _oldestDependency is initialized to MinValue and indicates that it must always be set
3536 if (_oldestDependency == DateTime.MinValue || utcNow < _oldestDependency)
3537 _oldestDependency = utcNow;
3540 internal void AddDependencies(ArrayList items, string argname) {
3541 if (items == null) {
3542 throw new ArgumentNullException(argname);
3545 string[] a = (string[]) items.ToArray(typeof(string));
3546 AddDependencies(a, argname, false);
3549 internal void AddDependencies(string[] items, string argname) {
3550 AddDependencies(items, argname, true);
3553 internal void AddDependencies(string[] items, string argname, bool cloneArray) {
3554 AddDependencies(items, argname, cloneArray, DateTime.UtcNow);
3557 internal void AddDependencies(string[] items, string argname, bool cloneArray, string requestVirtualPath) {
3558 if (requestVirtualPath == null)
3559 throw new ArgumentNullException("requestVirtualPath");
3561 _requestVirtualPath = requestVirtualPath;
3562 AddDependencies(items, argname, cloneArray, DateTime.UtcNow);
3565 internal void AddDependencies(string[] items, string argname, bool cloneArray, DateTime utcDepTime) {
3566 if (items == null) {
3567 throw new ArgumentNullException(argname);
3570 string [] itemsLocal;
3573 itemsLocal = (string[]) items.Clone();
3579 foreach (string item in itemsLocal) {
3580 if (String.IsNullOrEmpty(item)) {
3581 throw new ArgumentNullException(argname);
3585 _dependencyArray = null;
3587 if (_dependencies == null) {
3588 _dependencies = new ArrayList(1);
3591 _dependencies.Add(new ResponseDependencyInfo(itemsLocal, utcDepTime));
3593 // _oldestDependency is initialized to MinValue and indicates that it must always be set
3594 if (_oldestDependency == DateTime.MinValue || utcDepTime < _oldestDependency)
3595 _oldestDependency = utcDepTime;
3598 internal bool HasDependencies() {
3599 if (_dependencyArray == null && _dependencies == null)
3605 internal string[] GetDependencies() {
3606 if (_dependencyArray == null && _dependencies != null) {
3608 foreach (ResponseDependencyInfo info in _dependencies) {
3609 size += info.items.Length;
3612 _dependencyArray = new string[size];
3615 foreach (ResponseDependencyInfo info in _dependencies) {
3616 int length = info.items.Length;
3617 Array.Copy(info.items, 0, _dependencyArray, index, length);
3622 return _dependencyArray;
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;
3633 if (dependencyType == CacheDependencyType.Files) {
3634 dependency = new CacheDependency(0, info.items, null, dependencyOld, info.utcDate);
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));
3643 if (dependencyOld != null) {
3644 dependencyOld.Dispose();
3650 CacheDependency virtualDependency = null;
3651 VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
3652 if (vpp != null && _requestVirtualPath != null) {
3653 virtualDependency = vpp.GetCacheDependency(_requestVirtualPath, GetDependencies(), _oldestDependency);
3655 if (virtualDependency != null) {
3656 AggregateCacheDependency tempDep = new AggregateCacheDependency();
3657 tempDep.Add(virtualDependency);
3658 if (dependency != null) {
3659 tempDep.Add(dependency);
3661 dependency = tempDep;
3670 internal class ResponseDependencyInfo {
3671 internal readonly string[] items;
3672 internal readonly DateTime utcDate;
3674 internal ResponseDependencyInfo(string[] items, DateTime utcDate) {
3676 this.utcDate = utcDate;