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 SetResponseBuffers(rawResponse.Buffers);
1131 _suppressContent = !sendBody;
1134 // set the response content bufffers
1135 internal void SetResponseBuffers(ArrayList buffers) {
1136 if (_httpWriter == null) {
1137 throw new HttpException(SR.GetString(SR.Cannot_use_snapshot_for_TextWriter));
1140 _httpWriter.UseSnapshot(buffers);
1143 internal void CloseConnectionAfterError() {
1144 _closeConnectionAfterError = true;
1147 private void WriteErrorMessage(Exception e, bool dontShowSensitiveErrors) {
1148 ErrorFormatter errorFormatter = null;
1149 CultureInfo uiculture = null, savedUiculture = null;
1150 bool needToRestoreUiculture = false;
1152 if (_context.DynamicUICulture != null) {
1153 // if the user set the culture dynamically use it
1154 uiculture = _context.DynamicUICulture;
1157 // get the UI culture under which the error text must be created (use LKG to avoid errors while reporting error)
1158 GlobalizationSection globConfig = RuntimeConfig.GetLKGConfig(_context).Globalization;
1159 if ((globConfig != null) && (!String.IsNullOrEmpty(globConfig.UICulture))) {
1161 uiculture = HttpServerUtility.CreateReadOnlyCultureInfo(globConfig.UICulture);
1168 // In Integrated mode, generate the necessary response headers for the error
1169 GenerateResponseHeadersForHandler();
1171 // set the UI culture
1172 if (uiculture != null) {
1173 savedUiculture = Thread.CurrentThread.CurrentUICulture;
1174 Thread.CurrentThread.CurrentUICulture = uiculture;
1175 needToRestoreUiculture = true;
1180 // Try to get an error formatter
1181 errorFormatter = GetErrorFormatter(e);
1183 Debug.Trace("internal", "Error stack for " + Request.Path, e);
1185 if (dontShowSensitiveErrors && !errorFormatter.CanBeShownToAllUsers)
1186 errorFormatter = new GenericApplicationErrorFormatter(Request.IsLocal);
1188 Debug.Trace("internal", "errorFormatter's type = " + errorFormatter.GetType());
1190 if (ErrorFormatter.RequiresAdaptiveErrorReporting(Context)) {
1191 _writer.Write(errorFormatter.GetAdaptiveErrorMessage(Context, dontShowSensitiveErrors));
1194 _writer.Write(errorFormatter.GetHtmlErrorMessage(dontShowSensitiveErrors));
1196 // Write a stack dump in an HTML comment for debugging purposes
1197 // Only show it for Asp permission medium or higher (ASURT 126373)
1198 if (!dontShowSensitiveErrors &&
1199 HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
1200 _writer.Write("<!-- \r\n");
1201 WriteExceptionStack(e);
1202 _writer.Write("-->");
1204 if (!dontShowSensitiveErrors && !Request.IsLocal ) {
1205 _writer.Write("<!-- \r\n");
1206 _writer.Write(SR.GetString(SR.Information_Disclosure_Warning));
1207 _writer.Write("-->");
1211 if (_closeConnectionAfterError) {
1217 // restore ui culture
1218 if (needToRestoreUiculture)
1219 Thread.CurrentThread.CurrentUICulture = savedUiculture;
1222 catch { // Protect against exception filters
1227 internal void SetOverrideErrorFormatter(ErrorFormatter errorFormatter) {
1228 _overrideErrorFormatter = errorFormatter;
1231 internal ErrorFormatter GetErrorFormatter(Exception e) {
1232 ErrorFormatter errorFormatter = null;
1234 if (_overrideErrorFormatter != null) {
1235 return _overrideErrorFormatter;
1238 // Try to get an error formatter
1239 errorFormatter = HttpException.GetErrorFormatter(e);
1241 if (errorFormatter == null) {
1242 ConfigurationException ce = e as ConfigurationException;
1243 if (ce != null && !String.IsNullOrEmpty(ce.Filename))
1244 errorFormatter = new ConfigErrorFormatter(ce);
1247 // If we couldn't get one, create one here
1248 if (errorFormatter == null) {
1249 // If it's a 404, use a special error page, otherwise, use a more
1251 if (_statusCode == 404)
1252 errorFormatter = new PageNotFoundErrorFormatter(Request.Path);
1253 else if (_statusCode == 403)
1254 errorFormatter = new PageForbiddenErrorFormatter(Request.Path);
1256 if (e is System.Security.SecurityException)
1257 errorFormatter = new SecurityErrorFormatter(e);
1259 errorFormatter = new UnhandledErrorFormatter(e);
1263 // Show config source only on local request for security reasons
1264 // Config file snippet may unintentionally reveal sensitive information (not related to the error)
1265 ConfigErrorFormatter configErrorFormatter = errorFormatter as ConfigErrorFormatter;
1266 if (configErrorFormatter != null) {
1267 configErrorFormatter.AllowSourceCode = Request.IsLocal;
1270 return errorFormatter;
1273 private void WriteOneExceptionStack(Exception e) {
1274 Exception subExcep = e.InnerException;
1275 if (subExcep != null)
1276 WriteOneExceptionStack(subExcep);
1278 string title = "[" + e.GetType().Name + "]";
1279 if (e.Message != null && e.Message.Length > 0)
1280 title += ": " + HttpUtility.HtmlEncode(e.Message);
1282 _writer.WriteLine(title);
1283 if (e.StackTrace != null)
1284 _writer.WriteLine(e.StackTrace);
1287 private void WriteExceptionStack(Exception e) {
1288 ConfigurationErrorsException errors = e as ConfigurationErrorsException;
1289 if (errors == null) {
1290 WriteOneExceptionStack(e);
1293 // Write the original exception to get the first error with
1294 // a full stack trace
1295 WriteOneExceptionStack(e);
1297 // Write additional errors, which will contain truncated stacks
1298 ICollection col = errors.Errors;
1299 if (col.Count > 1) {
1300 bool firstSkipped = false;
1301 foreach (ConfigurationException ce in col) {
1302 if (!firstSkipped) {
1303 firstSkipped = true;
1307 _writer.WriteLine("---");
1308 WriteOneExceptionStack(ce);
1314 internal void ReportRuntimeError(Exception e, bool canThrow, bool localExecute) {
1315 CustomErrorsSection customErrorsSetting = null;
1316 bool useCustomErrors = false;
1322 // always try to disable IIS custom errors when we send an error
1324 _wr.TrySkipIisCustomErrors = true;
1327 if (!localExecute) {
1328 code = HttpException.GetHttpCodeForException(e);
1330 // Don't raise event for 404. See VSWhidbey 124147.
1332 WebBaseEvent.RaiseRuntimeError(e, this);
1335 // This cannot use the HttpContext.IsCustomErrorEnabled property, since it must call
1336 // GetSettings() with the canThrow parameter.
1337 customErrorsSetting = CustomErrorsSection.GetSettings(_context, canThrow);
1338 if (customErrorsSetting != null)
1339 useCustomErrors = customErrorsSetting.CustomErrorsEnabled(Request);
1341 useCustomErrors = true;
1344 if (!_headersWritten) {
1345 // nothing sent yet - entire response
1348 code = HttpException.GetHttpCodeForException(e);
1351 // change 401 to 500 in case the config is not to impersonate
1352 if (code == 401 && !_context.IsClientImpersonationConfigured)
1355 if (_context.TraceIsEnabled)
1356 _context.Trace.StatusCode = code;
1358 if (!localExecute && useCustomErrors) {
1359 String redirect = (customErrorsSetting != null) ? customErrorsSetting.GetRedirectString(code) : null;
1361 RedirectToErrorPageStatus redirectStatus = RedirectToErrorPage(redirect, customErrorsSetting.RedirectMode);
1362 switch (redirectStatus) {
1363 case RedirectToErrorPageStatus.Success:
1364 // success - nothing to do
1367 case RedirectToErrorPageStatus.NotAttempted:
1368 // if no redirect display generic error
1371 WriteErrorMessage(e, dontShowSensitiveErrors: true);
1375 // DevDiv #70492 - If we tried to display the custom error page but failed in doing so, we should display
1376 // a generic error message instead of trying to display the original error. We have a compat switch on
1377 // the <customErrors> element to control this behavior.
1379 if (customErrorsSetting.AllowNestedErrors) {
1380 // The user has set the compat switch to use the original (pre-bug fix) behavior.
1381 goto case RedirectToErrorPageStatus.NotAttempted;
1386 HttpException dummyException = new HttpException();
1387 dummyException.SetFormatter(new CustomErrorFailedErrorFormatter());
1388 WriteErrorMessage(dummyException, dontShowSensitiveErrors: true);
1395 WriteErrorMessage(e, dontShowSensitiveErrors: false);
1401 if (_contentType != null && _contentType.Equals("text/html")) {
1402 // in the middle of Html - break Html
1403 Write("\r\n\r\n</pre></table></table></table></table></table>");
1404 Write("</font></font></font></font></font>");
1405 Write("</i></i></i></i></i></b></b></b></b></b></u></u></u></u></u>");
1406 Write("<p> </p><hr>\r\n\r\n");
1409 WriteErrorMessage(e, useCustomErrors);
1413 internal void SynchronizeStatus(int statusCode, int subStatusCode, string description) {
1414 _statusCode = statusCode;
1415 _subStatusCode = subStatusCode;
1416 _statusDescription = description;
1420 internal void SynchronizeHeader(int knownHeaderIndex, string name, string value) {
1421 HttpHeaderCollection headers = Headers as HttpHeaderCollection;
1422 headers.SynchronizeHeader(name, value);
1424 // unknown headers have an index < 0
1425 if (knownHeaderIndex < 0) {
1429 bool fHeadersWritten = HeadersWritten;
1430 HeadersWritten = false; // Turn off the warning for "Headers have been written and can not be set"
1432 switch (knownHeaderIndex) {
1433 case HttpWorkerRequest.HeaderCacheControl:
1434 _cacheControlHeaderAdded = true;
1436 case HttpWorkerRequest.HeaderContentType:
1437 _contentType = value;
1439 case HttpWorkerRequest.HeaderLocation:
1440 _redirectLocation = value;
1441 _redirectLocationSet = false;
1443 case HttpWorkerRequest.HeaderSetCookie:
1444 // If the header is Set-Cookie, update the corresponding
1445 // cookie in the cookies collection
1446 if (value != null) {
1447 HttpCookie cookie = HttpRequest.CreateCookieFromString(value);
1448 // do not write this cookie back to IIS
1449 cookie.FromHeader = true;
1450 Cookies.Set(cookie);
1451 cookie.Changed = false;
1452 cookie.Added = false;
1457 HeadersWritten = fHeadersWritten;
1461 internal void SyncStatusIntegrated() {
1462 Debug.Assert(_wr is IIS7WorkerRequest, "_wr is IIS7WorkerRequest");
1463 if (!_headersWritten && _statusSet) {
1464 // For integrated pipeline, synchronize the status immediately so that the FREB log
1465 // correctly indicates the module and notification that changed the status.
1466 _wr.SendStatus(_statusCode, _subStatusCode, this.StatusDescription);
1471 // Public properties
1474 // Gets or sets the HTTP status code of output returned to client.
1475 public int StatusCode {
1481 if (_headersWritten)
1482 throw new HttpException(SR.GetString(SR.Cannot_set_status_after_headers_sent));
1484 if (_statusCode != value) {
1485 _statusCode = value;
1487 _statusDescription = null;
1493 // the IIS sub status code
1494 // since this doesn't get emitted in the protocol
1495 // we won't send it through the worker request interface
1497 public int SubStatusCode {
1499 if ( !(_wr is IIS7WorkerRequest) ) {
1500 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
1503 return _subStatusCode;
1506 if ( !(_wr is IIS7WorkerRequest) ) {
1507 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
1510 if (_headersWritten) {
1511 throw new HttpException(SR.GetString(SR.Cannot_set_status_after_headers_sent));
1514 _subStatusCode = value;
1519 // Allows setting both the status and the substatus individually. If not in IIS7 integrated mode,
1520 // the substatus code is ignored so as not to throw an exception.
1521 internal void SetStatusCode(int statusCode, int subStatus = -1) {
1522 StatusCode = statusCode;
1523 if (subStatus >= 0 && _wr is IIS7WorkerRequest) {
1524 SubStatusCode = subStatus;
1529 * Http status description string
1532 // Http status description string
1533 // Gets or sets the HTTP status string of output returned to the client.
1534 public String StatusDescription {
1536 if (_statusDescription == null)
1537 _statusDescription = HttpWorkerRequest.GetStatusDescription(_statusCode);
1539 return _statusDescription;
1543 if (_headersWritten)
1544 throw new HttpException(SR.GetString(SR.Cannot_set_status_after_headers_sent));
1546 if (value != null && value.Length > 512) // ASURT 124743
1547 throw new ArgumentOutOfRangeException("value");
1548 _statusDescription = value;
1553 public bool TrySkipIisCustomErrors {
1556 return _wr.TrySkipIisCustomErrors;
1562 _wr.TrySkipIisCustomErrors = value;
1568 /// By default, the FormsAuthenticationModule hooks EndRequest and converts HTTP 401 status codes to
1569 /// HTTP 302, redirecting to the login page. This isn't appropriate for certain classes of errors,
1570 /// e.g. where authentication succeeded but authorization failed, or where the current request is
1571 /// an AJAX or web service request. This property provides a way to suppress the redirect behavior
1572 /// and send the original status code to the client.
1574 public bool SuppressFormsAuthenticationRedirect {
1580 /// By default, ASP.NET sends a "Cache-Control: private" response header unless an explicit cache
1581 /// policy has been specified for this response. This property allows suppressing this default
1582 /// response header on a per-request basis. It can still be suppressed for the entire application
1583 /// by setting the appropriate value in <httpRuntime> or <outputCache>. See
1584 /// http://msdn.microsoft.com/en-us/library/system.web.configuration.httpruntimesection.sendcachecontrolheader.aspx
1585 /// for more information on those config elements.
1588 /// Developers should use caution when suppressing the default Cache-Control header, as proxies
1589 /// and other intermediaries may treat responses without this header as cacheable by default.
1590 /// This could lead to the inadvertent caching of sensitive information.
1591 /// See RFC 2616, Sec. 13.4, for more information.
1593 public bool SuppressDefaultCacheControlHeader {
1598 // Flag indicating to buffer the output
1599 // Gets or sets a value indicating whether HTTP output is buffered.
1600 public bool BufferOutput {
1602 return _bufferOutput;
1606 if (_bufferOutput != value) {
1607 _bufferOutput = value;
1609 if (_httpWriter != null)
1610 _httpWriter.UpdateResponseBuffering();
1615 // Gets the Content-Encoding HTTP response header.
1616 internal String GetHttpHeaderContentEncoding() {
1617 string coding = null;
1618 if (_wr is IIS7WorkerRequest) {
1619 if (_headers != null) {
1620 coding = _headers["Content-Encoding"];
1623 else if (_customHeaders != null) {
1624 int numCustomHeaders = _customHeaders.Count;
1625 for (int i = 0; i < numCustomHeaders; i++) {
1626 HttpResponseHeader h = (HttpResponseHeader)_customHeaders[i];
1627 if (h.Name == "Content-Encoding") {
1641 /// <para>Gets or sets the
1642 /// HTTP MIME type of output.</para>
1644 public String ContentType {
1646 return _contentType;
1650 if (_headersWritten) {
1651 // Don't throw if the new content type is the same as the current one
1652 if (_contentType == value)
1655 throw new HttpException(SR.GetString(SR.Cannot_set_content_type_after_headers_sent));
1658 _contentTypeSetByManagedCaller = true;
1659 _contentType = value;
1664 // Gets or sets the HTTP charset of output.
1665 public String Charset {
1667 if (_charSet == null)
1668 _charSet = ContentEncoding.WebName;
1674 if (_headersWritten)
1675 throw new HttpException(SR.GetString(SR.Cannot_set_content_type_after_headers_sent));
1680 _charSet = String.Empty; // to differentiate between not set (default) and empty chatset
1682 _customCharSet = true;
1686 // Content encoding for conversion
1687 // Gets or sets the HTTP character set of output.
1688 public Encoding ContentEncoding {
1690 if (_encoding == null) {
1691 // use LKG config because Response.ContentEncoding is need to display [config] error
1692 GlobalizationSection globConfig = RuntimeConfig.GetLKGConfig(_context).Globalization;
1693 if (globConfig != null)
1694 _encoding = globConfig.ResponseEncoding;
1696 if (_encoding == null)
1697 _encoding = Encoding.Default;
1705 throw new ArgumentNullException("value");
1707 if (_encoding == null || !_encoding.Equals(value)) {
1709 _encoder = null; // flush cached encoder
1711 if (_httpWriter != null)
1712 _httpWriter.UpdateResponseEncoding();
1718 public Encoding HeaderEncoding {
1720 if (_headerEncoding == null) {
1721 // use LKG config because Response.ContentEncoding is need to display [config] error
1722 GlobalizationSection globConfig = RuntimeConfig.GetLKGConfig(_context).Globalization;
1723 if (globConfig != null)
1724 _headerEncoding = globConfig.ResponseHeaderEncoding;
1726 // default to UTF-8 (also for Unicode as headers cannot be double byte encoded)
1727 if (_headerEncoding == null || _headerEncoding.Equals(Encoding.Unicode))
1728 _headerEncoding = Encoding.UTF8;
1731 return _headerEncoding;
1736 throw new ArgumentNullException("value");
1738 if (value.Equals(Encoding.Unicode)) {
1739 throw new HttpException(SR.GetString(SR.Invalid_header_encoding, value.WebName));
1742 if (_headerEncoding == null || !_headerEncoding.Equals(value)) {
1743 if (_headersWritten)
1744 throw new HttpException(SR.GetString(SR.Cannot_set_header_encoding_after_headers_sent));
1746 _headerEncoding = value;
1751 // Encoder cached for the current encoding
1752 internal Encoder ContentEncoder {
1754 if (_encoder == null) {
1755 Encoding e = ContentEncoding;
1756 _encoder = e.GetEncoder();
1758 // enable best fit mapping accoding to config
1759 // (doesn't apply to utf-8 which is the default, thus optimization)
1761 if (!e.Equals(Encoding.UTF8)) {
1762 bool enableBestFit = false;
1764 GlobalizationSection globConfig = RuntimeConfig.GetLKGConfig(_context).Globalization;
1765 if (globConfig != null) {
1766 enableBestFit = globConfig.EnableBestFitResponseEncoding;
1769 if (!enableBestFit) {
1770 // setting 'fallback' disables best fit mapping
1771 _encoder.Fallback = new EncoderReplacementFallback();
1780 // Returns the caching semantics of the Web page (expiration time, privacy, vary clauses).
1781 public HttpCachePolicy Cache {
1783 if (_cachePolicy == null) {
1784 _cachePolicy = new HttpCachePolicy();
1787 return _cachePolicy;
1791 // Return whether or not we have cache policy. We don't want to create it in
1792 // situations where we don't modify it.
1793 internal bool HasCachePolicy {
1795 return _cachePolicy != null;
1799 // Client connected flag
1800 // Gets a value indicating whether the client is still connected to the server.
1801 public bool IsClientConnected {
1803 if (_clientDisconnected)
1806 if (_wr != null && !_wr.IsClientConnected()) {
1807 _clientDisconnected = true;
1816 /// Returns a CancellationToken that is tripped when the client disconnects. This can be used
1817 /// to listen for async disconnect notifications.
1820 /// This method requires that the application be hosted on IIS 7.5 or higher and that the
1821 /// application pool be running the integrated mode pipeline.
1823 /// Consumers should be aware of some restrictions when consuming this CancellationToken.
1824 /// Failure to heed these warnings can lead to race conditions, deadlocks, or other
1825 /// undefined behavior.
1827 /// - This API is thread-safe. However, ASP.NET will dispose of the token object at the
1828 /// end of the request. Consumers should exercise caution and ensure that they're not
1829 /// calling into this API outside the bounds of a single request. This is similar to
1830 /// the contract with BCL Task-returning methods which take a CancellationToken as a
1831 /// parameter: the callee should not touch the CancellationToken after the returned
1832 /// Task transitions to a terminal state.
1834 /// - DO NOT wait on the CancellationToken.WaitHandle, as this defeats the purpose of an
1835 /// async notification and can cause deadlocks.
1837 /// - DO NOT call the CancellationToken.Register overloads which invoke the callback on
1838 /// the original SynchronizationContext.
1840 /// - DO NOT consume HttpContext or other non-thread-safe ASP.NET intrinsic objects from
1841 /// within the callback provided to Register. Remember: the callback may be running
1842 /// concurrently with other ASP.NET or application code.
1844 /// - DO keep the callback methods short-running and non-blocking. Make every effort to
1845 /// avoid throwing exceptions from within the callback methods.
1847 /// - We do not guarantee that we will ever transition the token to a canceled state.
1848 /// For example, if the request finishes without the client having disconnected, we
1849 /// will dispose of this token as mentioned earlier without having first canceled it.
1851 public CancellationToken ClientDisconnectedToken {
1853 IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
1854 CancellationToken cancellationToken;
1855 if (wr != null && wr.TryGetClientDisconnectedCancellationToken(out cancellationToken)) {
1856 return cancellationToken;
1859 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_75_Integrated));
1864 public bool IsRequestBeingRedirected {
1866 return _isRequestBeingRedirected;
1869 _isRequestBeingRedirected = value;
1875 /// <para>Gets or Sets a redirection string (value of location resposne header) for redirect response.</para>
1877 public String RedirectLocation {
1878 get { return _redirectLocation; }
1880 if (_headersWritten)
1881 throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent));
1883 _redirectLocation = value;
1884 _redirectLocationSet = true;
1893 /// <para>Closes the socket connection to a client.</para>
1895 public void Close() {
1896 if (!_clientDisconnected && !_completed && _wr != null) {
1897 _wr.CloseConnection();
1898 _clientDisconnected = true;
1902 // TextWriter object
1903 // Enables custom output to the outgoing Http content body.
1904 public TextWriter Output {
1905 get { return _writer;}
1906 set { _writer = value; }
1909 internal TextWriter SwitchWriter(TextWriter writer) {
1910 TextWriter oldWriter = _writer;
1916 // Enables binary output to the outgoing Http content body.
1917 public Stream OutputStream {
1919 if (!UsingHttpWriter)
1920 throw new HttpException(SR.GetString(SR.OutputStream_NotAvail));
1922 return _httpWriter.OutputStream;
1926 // ASP classic compat
1927 // Writes a string of binary characters to the HTTP output stream.
1928 public void BinaryWrite(byte[] buffer) {
1929 OutputStream.Write(buffer, 0, buffer.Length);
1933 // Appends a PICS (Platform for Internet Content Selection) label HTTP header to the output stream.
1934 public void Pics(String value) {
1935 AppendHeader("PICS-Label", value);
1939 // Specifies a wrapping filter object to modify HTTP entity body before transmission.
1940 public Stream Filter {
1942 if (UsingHttpWriter)
1943 return _httpWriter.GetCurrentFilter();
1949 if (UsingHttpWriter) {
1950 _httpWriter.InstallFilter(value);
1952 IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
1953 if (iis7WorkerRequest != null) {
1954 iis7WorkerRequest.ResponseFilterInstalled();
1958 throw new HttpException(SR.GetString(SR.Filtering_not_allowed));
1963 // Flag to suppress writing of content
1964 // Gets or sets a value indicating that HTTP content will not be sent to client.
1965 public bool SuppressContent {
1967 return _suppressContent;
1971 _suppressContent = value;
1972 _suppressContentSet = true;
1981 * Add Http custom header
1983 * @param name header name
1984 * @param value header value
1988 /// <para>Adds an HTTP
1989 /// header to the output stream.</para>
1991 public void AppendHeader(String name, String value) {
1992 bool isCacheHeader = false;
1994 if (_headersWritten)
1995 throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent));
1997 // some headers are stored separately or require special action
1998 int knownHeaderIndex = HttpWorkerRequest.GetKnownResponseHeaderIndex(name);
2000 switch (knownHeaderIndex) {
2001 case HttpWorkerRequest.HeaderContentType:
2002 ContentType = value;
2003 return; // don't keep as custom header
2005 case HttpWorkerRequest.HeaderContentLength:
2006 _contentLengthSet = true;
2009 case HttpWorkerRequest.HeaderLocation:
2010 RedirectLocation = value;
2011 return; // don't keep as custom header
2013 case HttpWorkerRequest.HeaderTransferEncoding:
2014 _transferEncodingSet = true;
2017 case HttpWorkerRequest.HeaderCacheControl:
2018 _cacheControlHeaderAdded = true;
2019 goto case HttpWorkerRequest.HeaderExpires;
2020 case HttpWorkerRequest.HeaderExpires:
2021 case HttpWorkerRequest.HeaderLastModified:
2022 case HttpWorkerRequest.HeaderEtag:
2023 case HttpWorkerRequest.HeaderVary:
2024 isCacheHeader = true;
2028 // In integrated mode, write the headers directly
2029 if (_wr is IIS7WorkerRequest) {
2030 Headers.Add(name, value);
2035 // don't keep as custom header
2036 if (_cacheHeaders == null) {
2037 _cacheHeaders = new ArrayList();
2040 _cacheHeaders.Add(new HttpResponseHeader(knownHeaderIndex, value));
2044 HttpResponseHeader h;
2045 if (knownHeaderIndex >= 0)
2046 h = new HttpResponseHeader(knownHeaderIndex, value);
2048 h = new HttpResponseHeader(name, value);
2060 /// cookie to the output stream.
2063 public void AppendCookie(HttpCookie cookie) {
2064 if (_headersWritten)
2065 throw new HttpException(SR.GetString(SR.Cannot_append_cookie_after_headers_sent));
2067 Cookies.AddCookie(cookie, true);
2068 OnCookieAdd(cookie);
2075 public void SetCookie(HttpCookie cookie) {
2076 if (_headersWritten)
2077 throw new HttpException(SR.GetString(SR.Cannot_append_cookie_after_headers_sent));
2079 Cookies.AddCookie(cookie, false);
2080 OnCookieCollectionChange();
2083 internal void BeforeCookieCollectionChange() {
2084 if (_headersWritten)
2085 throw new HttpException(SR.GetString(SR.Cannot_modify_cookies_after_headers_sent));
2088 internal void OnCookieAdd(HttpCookie cookie) {
2089 // add to request's cookies as well
2090 Request.AddResponseCookie(cookie);
2093 internal void OnCookieCollectionChange() {
2094 // synchronize with request cookie collection
2095 Request.ResetCookies();
2098 // Clear response headers
2099 // Clears all headers from the buffer stream.
2100 public void ClearHeaders() {
2101 if (_headersWritten)
2102 throw new HttpException(SR.GetString(SR.Cannot_clear_headers_after_headers_sent));
2106 _statusDescription = null;
2108 _contentType = "text/html";
2109 _contentTypeSetByManagedCaller = false;
2111 _customCharSet = false;
2112 _contentLengthSet = false;
2114 _redirectLocation = null;
2115 _redirectLocationSet = false;
2116 _isRequestBeingRedirected = false;
2118 _customHeaders = null;
2120 if (_headers != null) {
2121 _headers.ClearInternal();
2124 _transferEncodingSet = false;
2127 if (_cookies != null) {
2129 Request.ResetCookies();
2132 if (_cachePolicy != null) {
2133 _cachePolicy.Reset();
2136 _cacheControlHeaderAdded = false;
2137 _cacheHeaders = null;
2139 _suppressHeaders = false;
2140 _suppressContent = false;
2141 _suppressContentSet = false;
2143 _expiresInMinutes = 0;
2144 _expiresInMinutesSet = false;
2145 _expiresAbsolute = DateTime.MinValue;
2146 _expiresAbsoluteSet = false;
2147 _cacheControl = null;
2149 IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
2150 if (iis7WorkerRequest != null) {
2151 // clear the native response as well
2152 ClearNativeResponse(false, true, iis7WorkerRequest);
2154 // We need to regenerate Cache-Control: private only when the handler is managed and
2155 // configuration has <outputCache sendCacheControlHeader="true" /> in system.web
2157 if (_handlerHeadersGenerated && _sendCacheControlHeader) {
2158 Headers.Set("Cache-Control", "private");
2160 _handlerHeadersGenerated = false;
2166 /// <para>Clears all content output from the buffer stream.</para>
2168 public void ClearContent() {
2173 * Clear response buffer and headers. (For ASP compat doesn't clear headers)
2177 /// <para>Clears all headers and content output from the buffer stream.</para>
2179 public void Clear() {
2180 if (UsingHttpWriter)
2181 _httpWriter.ClearBuffers();
2183 IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
2184 if (iis7WorkerRequest != null) {
2185 // clear the native response buffers too
2186 ClearNativeResponse(true, false, iis7WorkerRequest);
2193 * Clear response buffer and headers. Internal. Used to be 'Clear'.
2195 internal void ClearAll() {
2196 if (!_headersWritten)
2202 * Flush response currently buffered
2206 /// <para>Sends all currently buffered output to the client.</para>
2208 public void Flush() {
2210 throw new HttpException(SR.GetString(SR.Cannot_flush_completed_response));
2215 // Registers a callback that the ASP.NET runtime will invoke immediately before
2216 // response headers are sent for this request. This differs from the IHttpModule-
2217 // level pipeline event in that this is a per-request subscription rather than
2218 // a per-application subscription. The intent is that the callback may modify
2219 // the response status code or may set a response cookie or header. Other usage
2220 // notes and caveats:
2222 // - This API is available only in the IIS integrated mode pipeline and only
2223 // if response headers haven't yet been sent for this request.
2224 // - The ASP.NET runtime does not guarantee anything about the thread that the
2225 // callback is invoked on. For example, the callback may be invoked synchronously
2226 // on a background thread if a background flush is being performed.
2227 // HttpContext.Current is not guaranteed to be available on such a thread.
2228 // - The callback must not call any API that manipulates the response entity body
2229 // or that results in a flush. For example, the callback must not call
2230 // Response.Redirect, as that method may manipulate the response entity body.
2231 // - The callback must contain only short-running synchronous code. Trying to kick
2232 // off an asynchronous operation or wait on such an operation could result in
2234 // - The callback must not throw, otherwise behavior is undefined.
2235 [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = @"The normal event pattern doesn't work between HttpResponse and HttpResponseBase since the signatures differ.")]
2236 public ISubscriptionToken AddOnSendingHeaders(Action<HttpContext> callback) {
2237 if (callback == null) {
2238 throw new ArgumentNullException("callback");
2241 if (!(_wr is IIS7WorkerRequest)) {
2242 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
2245 if (HeadersWritten) {
2246 throw new HttpException(SR.GetString(SR.Cannot_call_method_after_headers_sent_generic));
2249 return _onSendingHeadersSubscriptionQueue.Enqueue(callback);
2253 * Append string to the log record
2255 * @param param string to append to the log record
2259 /// <para>Adds custom log information to the IIS log file.</para>
2261 [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Medium)]
2262 public void AppendToLog(String param) {
2263 // only makes sense for IIS
2264 if (_wr is System.Web.Hosting.ISAPIWorkerRequest)
2265 ((System.Web.Hosting.ISAPIWorkerRequest)_wr).AppendLogParameter(param);
2266 else if (_wr is System.Web.Hosting.IIS7WorkerRequest)
2267 _context.Request.AppendToLogQueryString(param);
2272 /// <para>Redirects a client to a new URL.</para>
2274 public void Redirect(String url) {
2275 Redirect(url, true, false);
2279 /// <para>Redirects a client to a new URL.</para>
2281 public void Redirect(String url, bool endResponse) {
2282 Redirect(url, endResponse, false);
2285 public void RedirectToRoute(object routeValues) {
2286 RedirectToRoute(new RouteValueDictionary(routeValues));
2289 public void RedirectToRoute(string routeName) {
2290 RedirectToRoute(routeName, (RouteValueDictionary)null, false);
2293 public void RedirectToRoute(RouteValueDictionary routeValues) {
2294 RedirectToRoute(null /* routeName */, routeValues, false);
2297 public void RedirectToRoute(string routeName, object routeValues) {
2298 RedirectToRoute(routeName, new RouteValueDictionary(routeValues), false);
2301 public void RedirectToRoute(string routeName, RouteValueDictionary routeValues) {
2302 RedirectToRoute(routeName, routeValues, false);
2305 private void RedirectToRoute(string routeName, RouteValueDictionary routeValues, bool permanent) {
2306 string destinationUrl = null;
2307 VirtualPathData data = RouteTable.Routes.GetVirtualPath(Request.RequestContext, routeName, routeValues);
2309 destinationUrl = data.VirtualPath;
2312 if (String.IsNullOrEmpty(destinationUrl)) {
2313 throw new InvalidOperationException(SR.GetString(SR.No_Route_Found_For_Redirect));
2316 Redirect(destinationUrl, false /* endResponse */, permanent);
2319 public void RedirectToRoutePermanent(object routeValues) {
2320 RedirectToRoutePermanent(new RouteValueDictionary(routeValues));
2323 public void RedirectToRoutePermanent(string routeName) {
2324 RedirectToRoute(routeName, (RouteValueDictionary)null, true);
2327 public void RedirectToRoutePermanent(RouteValueDictionary routeValues) {
2328 RedirectToRoute(null /* routeName */, routeValues, true);
2331 public void RedirectToRoutePermanent(string routeName, object routeValues) {
2332 RedirectToRoute(routeName, new RouteValueDictionary(routeValues), true);
2335 public void RedirectToRoutePermanent(string routeName, RouteValueDictionary routeValues) {
2336 RedirectToRoute(routeName, routeValues, 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) {
2346 Redirect(url, true, true);
2350 /// <para>Redirects a client to a new URL with a 301.</para>
2352 [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
2353 Justification = "Warning was suppressed for consistency with existing similar Redirect API")]
2354 public void RedirectPermanent(String url, bool endResponse) {
2355 Redirect(url, endResponse, true);
2358 internal void Redirect(String url, bool endResponse, bool permanent) {
2360 string originalUrl = url;
2363 throw new ArgumentNullException("url");
2365 if (url.IndexOf('\n') >= 0)
2366 throw new ArgumentException(SR.GetString(SR.Cannot_redirect_to_newline));
2368 if (_headersWritten)
2369 throw new HttpException(SR.GetString(SR.Cannot_redirect_after_headers_sent));
2371 Page page = _context.Handler as Page;
2372 if ((page != null) && page.IsCallback) {
2373 throw new ApplicationException(SR.GetString(SR.Redirect_not_allowed_in_callback));
2376 url = ApplyRedirectQueryStringIfRequired(url);
2378 url = ApplyAppPathModifier(url);
2380 url = ConvertToFullyQualifiedRedirectUrlIfRequired(url);
2382 url = UrlEncodeRedirect(url);
2386 // If it's a Page and SmartNavigation is on, return a short script
2387 // to perform the redirect instead of returning a 302 (bugs ASURT 82331/86782)
2388 #pragma warning disable 0618 // To avoid SmartNavigation deprecation warning
2389 if (page != null && page.IsPostBack && page.SmartNavigation && (Request["__smartNavPostBack"] == "true")) {
2390 #pragma warning restore 0618
2391 Write("<BODY><ASP_SMARTNAV_RDIR url=\"");
2392 Write(HttpUtility.HtmlEncode(url));
2393 Write("\"></ASP_SMARTNAV_RDIR>");
2398 this.StatusCode = permanent ? 301 : 302;
2399 RedirectLocation = url;
2400 // DevDivBugs 158137: 302 Redirect vulnerable to XSS
2401 // A ---- of protocol identifiers. We don't want to UrlEncode
2402 // URLs matching these schemes in order to not break the
2403 // physical Object Moved to link.
2404 if (UriUtil.IsSafeScheme(url)) {
2405 url = HttpUtility.HtmlAttributeEncode(url);
2408 url = HttpUtility.HtmlAttributeEncode(HttpUtility.UrlEncode(url));
2410 Write("<html><head><title>Object moved</title></head><body>\r\n");
2411 Write("<h2>Object moved to <a href=\"" + url + "\">here</a>.</h2>\r\n");
2412 Write("</body></html>\r\n");
2415 _isRequestBeingRedirected = true;
2418 Debug.Trace("ClientUrl", "*** Redirect (" + originalUrl + ") --> " + RedirectLocation + " ***");
2421 var redirectingHandler = Redirecting;
2422 if (redirectingHandler != null) {
2423 redirectingHandler(this, EventArgs.Empty);
2430 internal string ApplyRedirectQueryStringIfRequired(string url) {
2431 if (Request == null || (string)Request.Browser["requiresPostRedirectionHandling"] != "true")
2434 Page page = _context.Handler as Page;
2435 if (page != null && !page.IsPostBack)
2438 //do not add __redir=1 if it already exists
2439 int i = url.IndexOf(RedirectQueryStringAssignment, StringComparison.Ordinal);
2441 i = url.IndexOf('?');
2443 url = url.Insert(i + 1, _redirectQueryStringInline);
2446 url = String.Concat(url, _redirectQueryString);
2453 // Redirect to error page appending ?aspxerrorpath if no query string in the url.
2454 // Fails to redirect if request is already for error page.
2455 // Suppresses all errors.
2456 // See comments on RedirectToErrorPageStatus type for meaning of return values.
2458 internal RedirectToErrorPageStatus RedirectToErrorPage(String url, CustomErrorsRedirectMode redirectMode) {
2459 const String qsErrorMark = "aspxerrorpath";
2462 if (String.IsNullOrEmpty(url))
2463 return RedirectToErrorPageStatus.NotAttempted; // nowhere to redirect
2465 if (_headersWritten)
2466 return RedirectToErrorPageStatus.NotAttempted;
2468 if (Request.QueryString[qsErrorMark] != null)
2469 return RedirectToErrorPageStatus.Failed; // already in error redirect
2471 if (redirectMode == CustomErrorsRedirectMode.ResponseRewrite) {
2472 Context.Server.Execute(url);
2475 // append query string
2476 if (url.IndexOf('?') < 0)
2477 url = url + "?" + qsErrorMark + "=" + HttpEncoderUtility.UrlEncodeSpaces(Request.Path);
2479 // redirect without response.end
2480 Redirect(url, false /*endResponse*/);
2484 return RedirectToErrorPageStatus.Failed;
2487 return RedirectToErrorPageStatus.Success;
2490 // Represents the result of calling RedirectToErrorPage
2491 internal enum RedirectToErrorPageStatus {
2492 NotAttempted, // Redirect or rewrite was not attempted, possibly because no redirect URL was specified
2493 Success, // Redirect or rewrite was attempted and succeeded
2494 Failed // Redirect or rewrite was attempted and failed, possibly due to the error page throwing
2497 // Implementation of the DefaultHttpHandler for IIS6+
2498 internal bool CanExecuteUrlForEntireResponse {
2500 // if anything is sent, too late
2501 if (_headersWritten) {
2505 // must have the right kind of worker request
2506 if (_wr == null || !_wr.SupportsExecuteUrl) {
2510 // must not be capturing output to custom writer
2511 if (!UsingHttpWriter) {
2515 // there is some cached output not yet sent
2516 if (_httpWriter.GetBufferedLength() != 0) {
2520 // can't use execute url with filter installed
2521 if (_httpWriter.FilterInstalled) {
2525 if (_cachePolicy != null && _cachePolicy.IsModified()) {
2533 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This is a safe critical method.")]
2534 internal IAsyncResult BeginExecuteUrlForEntireResponse(
2535 String pathOverride, NameValueCollection requestHeaders,
2536 AsyncCallback cb, Object state) {
2537 Debug.Assert(CanExecuteUrlForEntireResponse);
2539 // prepare user information
2540 String userName, userAuthType;
2541 if (_context != null && _context.User != null) {
2542 userName = _context.User.Identity.Name;
2543 userAuthType = _context.User.Identity.AuthenticationType;
2546 userName = String.Empty;
2547 userAuthType = String.Empty;
2551 String path = Request.RewrittenUrl; // null is ok
2553 if (pathOverride != null) {
2554 path = pathOverride;
2558 String headers = null;
2560 if (requestHeaders != null) {
2561 int numHeaders = requestHeaders.Count;
2563 if (numHeaders > 0) {
2564 StringBuilder sb = new StringBuilder();
2566 for (int i = 0; i < numHeaders; i++) {
2567 sb.Append(requestHeaders.GetKey(i));
2569 sb.Append(requestHeaders.Get(i));
2573 headers = sb.ToString();
2577 byte[] entity = null;
2578 if (_context != null && _context.Request != null) {
2579 entity = _context.Request.EntityBody;
2582 Debug.Trace("ExecuteUrl", "HttpResponse.BeginExecuteUrlForEntireResponse:" +
2583 " path=" + path + " headers=" + headers +
2584 " userName=" + userName + " authType=" + userAuthType);
2586 // call worker request to start async execute url for this request
2587 IAsyncResult ar = _wr.BeginExecuteUrl(
2589 null, // this method
2591 true, // let execute url send headers
2592 true, // add user info
2600 // suppress further sends from ASP.NET
2601 // (only if succeeded starting async operation - not is 'finally' block)
2602 _headersWritten = true;
2608 internal void EndExecuteUrlForEntireResponse(IAsyncResult result) {
2609 Debug.Trace("ExecuteUrl", "HttpResponse.EndExecuteUrlForEntireResponse");
2610 _wr.EndExecuteUrl(result);
2613 // Methods to write from file
2615 // Writes values to an HTTP output content stream.
2616 public void Write(String s) {
2620 // Writes values to an HTTP output content stream.
2621 public void Write(Object obj) {
2627 /// <para>Writes values to an HTTP output content stream.</para>
2629 public void Write(char ch) {
2635 /// <para>Writes values to an HTTP output content stream.</para>
2637 public void Write(char[] buffer, int index, int count) {
2638 _writer.Write(buffer, index, count);
2643 /// <para>Writes a substition block to the response.</para>
2645 public void WriteSubstitution(HttpResponseSubstitutionCallback callback) {
2646 // cannot be instance method on a control
2647 if (callback.Target != null && callback.Target is Control) {
2648 throw new ArgumentException(SR.GetString(SR.Invalid_substitution_callback), "callback");
2651 if (UsingHttpWriter) {
2652 // HttpWriter can take substitution blocks
2653 _httpWriter.WriteSubstBlock(callback, _wr as IIS7WorkerRequest);
2656 // text writer -- write as string
2657 _writer.Write(callback(_context));
2660 // set the cache policy: reduce cachability from public to server
2661 if (_cachePolicy != null && _cachePolicy.GetCacheability() == HttpCacheability.Public)
2662 _cachePolicy.SetCacheability(HttpCacheability.Server);
2666 * Helper method to write from file stream
2668 * Handles only TextWriter case. For real requests
2669 * HttpWorkerRequest can take files
2671 private void WriteStreamAsText(Stream f, long offset, long size) {
2673 size = f.Length - offset;
2677 f.Seek(offset, SeekOrigin.Begin);
2679 byte[] fileBytes = new byte[(int)size];
2680 int bytesRead = f.Read(fileBytes, 0, (int)size);
2681 _writer.Write(Encoding.Default.GetChars(fileBytes, 0, bytesRead));
2685 // support for VirtualPathProvider
2686 internal void WriteVirtualFile(VirtualFile vf) {
2687 Debug.Trace("WriteVirtualFile", vf.Name);
2689 using (Stream s = vf.Open()) {
2690 if (UsingHttpWriter) {
2691 long size = s.Length;
2694 // write as memory block
2695 byte[] fileBytes = new byte[(int)size];
2696 int bytesRead = s.Read(fileBytes, 0, (int) size);
2697 _httpWriter.WriteBytes(fileBytes, 0, bytesRead);
2701 // Write file contents
2702 WriteStreamAsText(s, 0, -1);
2707 // Helper method to get absolute physical filename from the argument to WriteFile
2708 private String GetNormalizedFilename(String fn) {
2709 // If it's not a physical path, call MapPath on it
2710 if (!UrlPath.IsAbsolutePhysicalPath(fn)) {
2711 if (Request != null)
2712 fn = Request.MapPath(fn); // relative to current request
2714 fn = HostingEnvironment.MapPath(fn);
2721 /// Writes a named file directly to an HTTP content output stream.
2722 public void WriteFile(String filename) {
2723 if (filename == null) {
2724 throw new ArgumentNullException("filename");
2727 WriteFile(filename, false);
2733 * @param filename file to write
2734 * @readIntoMemory flag to read contents into memory immediately
2738 /// <para> Reads a file into a memory block.</para>
2740 public void WriteFile(String filename, bool readIntoMemory) {
2741 if (filename == null) {
2742 throw new ArgumentNullException("filename");
2745 filename = GetNormalizedFilename(filename);
2747 FileStream f = null;
2750 f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
2752 if (UsingHttpWriter) {
2753 long size = f.Length;
2756 if (readIntoMemory) {
2757 // write as memory block
2758 byte[] fileBytes = new byte[(int)size];
2759 int bytesRead = f.Read(fileBytes, 0, (int) size);
2760 _httpWriter.WriteBytes(fileBytes, 0, bytesRead);
2763 // write as file block
2764 f.Close(); // close before writing
2766 _httpWriter.WriteFile(filename, 0, size);
2771 // Write file contents
2772 WriteStreamAsText(f, 0, -1);
2782 public void TransmitFile(string filename) {
2783 TransmitFile(filename, 0, -1);
2785 public void TransmitFile(string filename, long offset, long length) {
2786 if (filename == null) {
2787 throw new ArgumentNullException("filename");
2790 throw new ArgumentException(SR.GetString(SR.Invalid_range), "offset");
2792 throw new ArgumentException(SR.GetString(SR.Invalid_range), "length");
2794 filename = GetNormalizedFilename(filename);
2797 using (FileStream f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
2799 // length of -1 means send rest of file
2801 length = size - offset;
2803 if (size < offset) {
2804 throw new ArgumentException(SR.GetString(SR.Invalid_range), "offset");
2806 else if ((size - offset) < length) {
2807 throw new ArgumentException(SR.GetString(SR.Invalid_range), "length");
2809 if (!UsingHttpWriter) {
2810 WriteStreamAsText(f, offset, length);
2816 bool supportsLongTransmitFile = (_wr != null && _wr.SupportsLongTransmitFile);
2818 _httpWriter.TransmitFile(filename, offset, length,
2819 _context.IsClientImpersonationConfigured || HttpRuntime.IsOnUNCShareInternal, supportsLongTransmitFile);
2824 private void ValidateFileRange(String filename, long offset, long length) {
2825 FileStream f = null;
2828 f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
2830 long fileSize = f.Length;
2833 length = fileSize - offset;
2835 if (offset < 0 || length > fileSize - offset)
2836 throw new HttpException(SR.GetString(SR.Invalid_range));
2847 * @param filename file to write
2848 * @param offset file offset to start writing
2849 * @param size number of bytes to write
2853 /// <para>Writes a file directly to an HTTP content output stream.</para>
2855 public void WriteFile(String filename, long offset, long size) {
2856 if (filename == null) {
2857 throw new ArgumentNullException("filename");
2863 filename = GetNormalizedFilename(filename);
2865 ValidateFileRange(filename, offset, size);
2867 if (UsingHttpWriter) {
2868 // HttpWriter can take files -- don't open here (but Demand permission)
2869 InternalSecurityPermissions.FileReadAccess(filename).Demand();
2870 _httpWriter.WriteFile(filename, offset, size);
2873 FileStream f = null;
2876 f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
2877 WriteStreamAsText(f, offset, size);
2889 * @param handle file to write
2890 * @param offset file offset to start writing
2891 * @param size number of bytes to write
2895 /// <para>Writes a file directly to an HTTP content output stream.</para>
2897 [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]
2898 public void WriteFile(IntPtr fileHandle, long offset, long size) {
2902 FileStream f = null;
2905 f = new FileStream(new Microsoft.Win32.SafeHandles.SafeFileHandle(fileHandle,false), FileAccess.Read);
2907 if (UsingHttpWriter) {
2908 long fileSize = f.Length;
2911 size = fileSize - offset;
2913 if (offset < 0 || size > fileSize - offset)
2914 throw new HttpException(SR.GetString(SR.Invalid_range));
2917 f.Seek(offset, SeekOrigin.Begin);
2919 // write as memory block
2920 byte[] fileBytes = new byte[(int)size];
2921 int bytesRead = f.Read(fileBytes, 0, (int)size);
2922 _httpWriter.WriteBytes(fileBytes, 0, bytesRead);
2925 WriteStreamAsText(f, offset, size);
2935 /// <para>Allows HTTP/2 Server Push</para>
2937 public void PushPromise(string path) {
2941 PushPromise(path, method: "GET", headers: null);
2945 /// <para>Allows HTTP/2 Server Push</para>
2947 public void PushPromise(string path, string method, NameValueCollection headers) {
2948 // PushPromise is non-deterministic and application shouldn't have logic that depends on it.
2949 // It's only purpose is performance advantage in some cases.
2950 // There are many conditions (protocol and implementation) that may cause to
2951 // ignore the push requests completely.
2952 // The expectation is based on fire-and-forget
2955 throw new ArgumentNullException("path");
2958 if (method == null) {
2959 throw new ArgumentNullException("method");
2962 // Extract an optional query string
2963 string queryString = string.Empty;
2964 int i = path.IndexOf('?');
2967 if (i < path.Length - 1) {
2968 queryString = path.Substring(i + 1);
2971 // Remove the query string portion from the path
2972 path = path.Substring(0, i);
2976 // Only virtual path is allowed:
2977 // "/path" - origin relative
2978 // "~/path" - app relative
2979 // "path" - request relative
2980 // "../path" - reduced
2981 if (string.IsNullOrEmpty(path) || !UrlPath.IsValidVirtualPathWithoutProtocol(path)) {
2982 throw new ArgumentException(SR.GetString(SR.Invalid_path_for_push_promise, path));
2985 VirtualPath virtualPath = Request.FilePathObject.Combine(VirtualPath.Create(path));
2988 if (!HttpRuntime.UseIntegratedPipeline) {
2989 throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
2993 IIS7WorkerRequest wr = (IIS7WorkerRequest) _wr;
2994 wr.PushPromise(virtualPath.VirtualPathString, queryString, method, headers);
2996 catch (PlatformNotSupportedException e) {
2997 // Ignore errors if push promise is not supported
2998 if (Context.TraceIsEnabled) {
2999 Context.Trace.Write("aspx", "Push promise is not supported", e);
3005 // Deprecated ASP compatibility methods and properties
3011 /// Same as StatusDescription. Provided only for ASP compatibility.
3014 public string Status {
3016 return this.StatusCode.ToString(NumberFormatInfo.InvariantInfo) + " " + this.StatusDescription;
3021 String descr = "OK";
3024 int i = value.IndexOf(' ');
3025 code = Int32.Parse(value.Substring(0, i), CultureInfo.InvariantCulture);
3026 descr = value.Substring(i+1);
3029 throw new HttpException(SR.GetString(SR.Invalid_status_string));
3032 this.StatusCode = code;
3033 this.StatusDescription = descr;
3040 /// Same as BufferOutput. Provided only for ASP compatibility.
3043 public bool Buffer {
3044 get { return this.BufferOutput;}
3045 set { this.BufferOutput = value;}
3050 /// <para>Same as Appendheader. Provided only for ASP compatibility.</para>
3052 public void AddHeader(String name, String value) {
3053 AppendHeader(name, value);
3057 * Cancelles handler processing of the current request
3058 * throws special [non-]exception uncatchable by the user code
3059 * to tell application to stop module execution.
3063 /// <para>Sends all currently buffered output to the client then closes the
3064 /// socket connection.</para>
3067 if (_context.IsInCancellablePeriod) {
3068 AbortCurrentThread();
3071 // when cannot abort execution, flush and supress further output
3072 _endRequiresObservation = true;
3074 if (!_flushing) { // ignore Reponse.End while flushing (in OnPreSendHeaders)
3078 if (_context.ApplicationInstance != null) {
3079 _context.ApplicationInstance.CompleteRequest();
3085 // Aborts the current thread if Response.End was called and not yet observed.
3086 internal void ObserveResponseEndCalled() {
3087 if (_endRequiresObservation) {
3088 _endRequiresObservation = false;
3089 AbortCurrentThread();
3093 [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "Known issue, but required for proper operation of ASP.NET.")]
3094 [SecurityPermission(SecurityAction.Assert, ControlThread = true)]
3095 private static void AbortCurrentThread() {
3096 Thread.CurrentThread.Abort(new HttpApplication.CancelModuleException(false));
3100 * ASP compatible caching properties
3106 /// Gets or sets the time, in minutes, until cached
3107 /// information will be removed from the cache. Provided for ASP compatiblility. Use
3108 /// the <see cref='System.Web.HttpResponse.Cache'/>
3109 /// Property instead.
3112 public int Expires {
3114 return _expiresInMinutes;
3117 if (!_expiresInMinutesSet || value < _expiresInMinutes) {
3118 _expiresInMinutes = value;
3119 Cache.SetExpires(_context.Timestamp + new TimeSpan(0, _expiresInMinutes, 0));
3127 /// Gets or sets the absolute time that cached information
3128 /// will be removed from the cache. Provided for ASP compatiblility. Use the <see cref='System.Web.HttpResponse.Cache'/>
3129 /// property instead.
3132 public DateTime ExpiresAbsolute {
3134 return _expiresAbsolute;
3137 if (!_expiresAbsoluteSet || value < _expiresAbsolute) {
3138 _expiresAbsolute = value;
3139 Cache.SetExpires(_expiresAbsolute);
3147 /// Provided for ASP compatiblility. Use the <see cref='System.Web.HttpResponse.Cache'/>
3148 /// property instead.
3151 public string CacheControl {
3153 if (_cacheControl == null) {
3158 return _cacheControl;
3161 if (String.IsNullOrEmpty(value)) {
3162 _cacheControl = null;
3163 Cache.SetCacheability(HttpCacheability.NoCache);
3165 else if (StringUtil.EqualsIgnoreCase(value, "private")) {
3166 _cacheControl = value;
3167 Cache.SetCacheability(HttpCacheability.Private);
3169 else if (StringUtil.EqualsIgnoreCase(value, "public")) {
3170 _cacheControl = value;
3171 Cache.SetCacheability(HttpCacheability.Public);
3173 else if (StringUtil.EqualsIgnoreCase(value, "no-cache")) {
3174 _cacheControl = value;
3175 Cache.SetCacheability(HttpCacheability.NoCache);
3178 throw new ArgumentException(SR.GetString(SR.Invalid_value_for_CacheControl, value));
3183 internal void SetAppPathModifier(string appPathModifier) {
3184 if (appPathModifier != null && (
3185 appPathModifier.Length == 0 ||
3186 appPathModifier[0] == '/' ||
3187 appPathModifier[appPathModifier.Length - 1] == '/')) {
3189 throw new ArgumentException(SR.GetString(SR.InvalidArgumentValue, "appPathModifier"));
3192 _appPathModifier = appPathModifier;
3194 Debug.Trace("ClientUrl", "*** SetAppPathModifier (" + appPathModifier + ") ***");
3198 public string ApplyAppPathModifier(string virtualPath) {
3200 string originalUrl = virtualPath;
3202 object ch = _context.CookielessHelper; // This ensures that the cookieless-helper is initialized and applies the AppPathModifier
3203 if (virtualPath == null)
3206 if (UrlPath.IsRelativeUrl(virtualPath)) {
3207 // DevDiv 173208: RewritePath returns an HTTP 500 error code when requested with certain user agents
3208 // We should use ClientBaseDir instead of FilePathObject.
3209 virtualPath = UrlPath.Combine(Request.ClientBaseDir.VirtualPathString, virtualPath);
3212 // ignore paths with http://server/... or //
3213 if (!UrlPath.IsRooted(virtualPath) || virtualPath.StartsWith("//", StringComparison.Ordinal)) {
3217 virtualPath = UrlPath.Reduce(virtualPath);
3220 if (_appPathModifier == null || virtualPath.IndexOf(_appPathModifier, StringComparison.Ordinal) >= 0) {
3222 Debug.Trace("ClientUrl", "*** ApplyAppPathModifier (" + originalUrl + ") --> " + virtualPath + " ***");
3227 string appPath = HttpRuntime.AppDomainAppVirtualPathString;
3229 int compareLength = appPath.Length;
3230 bool isVirtualPathShort = (virtualPath.Length == appPath.Length - 1);
3231 if (isVirtualPathShort) {
3235 // String.Compare will throw exception if there aren't compareLength characters
3236 if (virtualPath.Length < compareLength) {
3240 if (!StringUtil.EqualsIgnoreCase(virtualPath, 0, appPath, 0, compareLength)) {
3244 if (isVirtualPathShort) {
3248 Debug.Assert(virtualPath.Length >= appPath.Length);
3249 if (virtualPath.Length == appPath.Length) {
3250 virtualPath = virtualPath.Substring(0, appPath.Length) + _appPathModifier + "/";
3254 virtualPath.Substring(0, appPath.Length) +
3257 virtualPath.Substring(appPath.Length);
3260 Debug.Trace("ClientUrl", "*** ApplyAppPathModifier (" + originalUrl + ") --> " + virtualPath + " ***");
3266 internal String RemoveAppPathModifier(string virtualPath) {
3267 if (String.IsNullOrEmpty(_appPathModifier))
3270 int pos = virtualPath.IndexOf(_appPathModifier, StringComparison.Ordinal);
3272 if (pos <= 0 || virtualPath[pos-1] != '/')
3275 return virtualPath.Substring(0, pos-1) + virtualPath.Substring(pos + _appPathModifier.Length);
3278 internal bool UsePathModifier {
3280 return !String.IsNullOrEmpty(_appPathModifier);
3284 private String ConvertToFullyQualifiedRedirectUrlIfRequired(String url) {
3285 HttpRuntimeSection runtimeConfig = RuntimeConfig.GetConfig(_context).HttpRuntime;
3286 if ( runtimeConfig.UseFullyQualifiedRedirectUrl ||
3287 (Request != null && (string)Request.Browser["requiresFullyQualifiedRedirectUrl"] == "true")) {
3288 return (new Uri(Request.Url, url)).AbsoluteUri ;
3295 private String UrlEncodeIDNSafe(String url) {
3296 // Bug 86594: Should not encode the domain part of the url. For example,
3297 // http://Übersite/Überpage.aspx should only encode the 2nd Ü.
3298 // To accomplish this we must separate the scheme+host+port portion of the url from the path portion,
3299 // encode the path portion, then reconstruct the url.
3300 Debug.Assert(!url.Contains("?"), "Querystring should have been stripped off.");
3302 string schemeAndAuthority;
3304 string queryAndFragment;
3305 bool isValidUrl = UriUtil.TrySplitUriForPathEncode(url, out schemeAndAuthority, out path, out queryAndFragment, checkScheme: true);
3308 // only encode the path portion
3309 return schemeAndAuthority + HttpEncoderUtility.UrlEncodeSpaces(HttpUtility.UrlEncodeNonAscii(path, Encoding.UTF8)) + queryAndFragment;
3312 // encode the entire URL
3313 return HttpEncoderUtility.UrlEncodeSpaces(HttpUtility.UrlEncodeNonAscii(url, Encoding.UTF8));
3317 private String UrlEncodeRedirect(String url) {
3318 // convert all non-ASCII chars before ? to %XX using UTF-8 and
3319 // after ? using Response.ContentEncoding
3321 int iqs = url.IndexOf('?');
3324 Encoding qsEncoding = (Request != null) ? Request.ContentEncoding : ContentEncoding;
3325 url = UrlEncodeIDNSafe(url.Substring(0, iqs)) + HttpUtility.UrlEncodeNonAscii(url.Substring(iqs), qsEncoding);
3328 url = UrlEncodeIDNSafe(url);
3334 internal void UpdateNativeResponse(bool sendHeaders)
3336 IIS7WorkerRequest iis7WorkerRequest = _wr as IIS7WorkerRequest;
3338 if (null == iis7WorkerRequest) {
3342 // WOS 1841024 - Don't set _suppressContent to true for HEAD requests. IIS needs the content
3343 // in order to correctly set the Content-Length header.
3344 // WOS 1634512 - need to clear buffers if _ended == true
3345 // WOS 1850019 - Breaking Change: ASP.NET v2.0: Content-Length is not correct for pages that call HttpResponse.SuppressContent
3346 if ((_suppressContent && Request != null && Request.HttpVerb != HttpVerb.HEAD) || _ended)
3349 bool needPush = false;
3350 // NOTE: This also sets the response encoding on the HttpWriter
3351 long bufferedLength = _httpWriter.GetBufferedLength();
3354 // Set headers and status
3356 if (!_headersWritten)
3361 // VSWhidbey 270635: We need to reset the status code for mobile devices.
3362 if (UseAdaptiveError) {
3364 // VSWhidbey 288054: We should change the status code for cases
3365 // that cannot be handled by mobile devices
3366 // 4xx for Client Error and 5xx for Server Error in HTTP spec
3367 int statusCode = StatusCode;
3368 if (statusCode >= 400 && statusCode < 600) {
3369 this.StatusCode = 200;
3373 // DevDiv #782830: Provide a hook where the application can change the response status code
3374 // or response headers.
3375 if (sendHeaders && !_onSendingHeadersSubscriptionQueue.IsEmpty) {
3376 _onSendingHeadersSubscriptionQueue.FireAndComplete(cb => cb(Context));
3380 _wr.SendStatus(this.StatusCode, this.SubStatusCode, this.StatusDescription);
3387 if (!_suppressHeaders && !_clientDisconnected)
3390 EnsureSessionStateIfNecessary();
3393 // If redirect location set, write it through to IIS as a header
3394 if (_redirectLocation != null && _redirectLocationSet) {
3395 HttpHeaderCollection headers = Headers as HttpHeaderCollection;
3396 headers.Set("Location", _redirectLocation);
3397 _redirectLocationSet = false;
3400 // Check if there is buffered response
3401 bool responseBuffered = bufferedLength > 0 || iis7WorkerRequest.IsResponseBuffered();
3404 // Generate Content-Type
3406 if (_contentType != null // Valid Content-Type
3407 && (_contentTypeSetByManagedCaller // Explicitly set by managed caller
3408 || (_contentTypeSetByManagedHandler && responseBuffered))) { // Implicitly set by managed handler and response is non-empty
3409 HttpHeaderCollection headers = Headers as HttpHeaderCollection;
3410 String contentType = AppendCharSetToContentType(_contentType);
3411 headers.Set("Content-Type", contentType);
3415 // If cookies have been added/changed, set the corresponding headers
3417 GenerateResponseHeadersForCookies();
3419 // Not calling WriteHeaders headers in Integrated mode.
3420 // Instead, most headers are generated when the handler runs,
3421 // or on demand as necessary.
3422 // The only exception are the cache policy headers.
3425 SuppressCachingCookiesIfNecessary();
3427 if (_cachePolicy != null) {
3428 if (_cachePolicy.IsModified()) {
3429 ArrayList cacheHeaders = new ArrayList();
3430 _cachePolicy.GetHeaders(cacheHeaders, this);
3431 HttpHeaderCollection headers = Headers as HttpHeaderCollection;
3432 foreach (HttpResponseHeader header in cacheHeaders) {
3433 // set and override the header
3434 headers.Set(header.Name, header.Value);
3444 if (_flushing && !_filteringCompleted) {
3445 _httpWriter.FilterIntegrated(false, iis7WorkerRequest);
3446 bufferedLength = _httpWriter.GetBufferedLength();
3449 if (!_clientDisconnected && (bufferedLength > 0 || needPush)) {
3451 if (bufferedLength == 0 ) {
3452 if (_httpWriter.IgnoringFurtherWrites) {
3457 // push HttpWriter buffers to worker request
3458 _httpWriter.Send(_wr);
3459 // push buffers through into native
3460 iis7WorkerRequest.PushResponseToNative();
3461 // dispose them (since they're copied or
3462 // owned by native request)
3463 _httpWriter.DisposeIntegratedBuffers();
3467 private void ClearNativeResponse(bool clearEntity, bool clearHeaders, IIS7WorkerRequest wr) {
3468 wr.ClearResponse(clearEntity, clearHeaders);
3470 _httpWriter.ClearSubstitutionBlocks();
3474 private void SuppressCachingCookiesIfNecessary() {
3475 // MSRC 11855 (DevDiv 297240 / 362405)
3476 // We should suppress caching cookies if non-shareable cookies are
3477 // present in the response. Since these cookies can cary sensitive information,
3478 // we should set Cache-Control: no-cache=set-cookie if there is such cookie
3479 // This prevents all well-behaved caches (both intermediary proxies and any local caches
3480 // on the client) from storing this sensitive information.
3482 // Additionally, we should not set this header during an SSL request, as certain versions
3483 // of IE don't handle it properly and simply refuse to render the page. More info:
3484 // http://blogs.msdn.com/b/ieinternals/archive/2009/10/02/internet-explorer-cannot-download-over-https-when-no-cache.aspx
3486 // Finally, we don't need to set 'no-cache' if the response is not publicly cacheable,
3487 // as ASP.NET won't cache the response (due to the cookies) and proxies won't cache
3488 // the response (due to Cache-Control: private).
3489 // If _cachePolicy isn't set, then Cache.GetCacheability() will contruct a default one (which causes Cache-Control: private)
3490 if (!Request.IsSecureConnection && ContainsNonShareableCookies() && Cache.GetCacheability() == HttpCacheability.Public) {
3491 Cache.SetCacheability(HttpCacheability.NoCache, "Set-Cookie");
3494 // if there are any cookies, do not kernel cache the response
3495 if (_cachePolicy != null && _cookies != null && _cookies.Count != 0) {
3496 _cachePolicy.SetHasSetCookieHeader();
3497 // In integrated mode, the cookies will eventually be sent to IIS via IIS7WorkerRequest.SetUnknownResponseHeader,
3498 // where we will disable both HTTP.SYS kernel cache and IIS user mode cache (DevDiv 113142 & 255268). In classic
3499 // mode, the cookies will be sent to IIS via ISAPIWorkerRequest.SendUnknownResponseHeader and
3500 // ISAPIWorkerRequest.SendKnownResponseHeader (DevDiv 113142), where we also disables the kernel cache. So the
3501 // call of DisableKernelCache below is not really needed.
3502 DisableKernelCache();
3506 private void EnsureSessionStateIfNecessary() {
3507 // Ensure the session state is in complete state before sending the response headers
3508 // Due to optimization and delay initialization sometimes we create and store the session state id in ReleaseSessionState.
3509 // But it's too late in case of Flush. Session state id must be written (if used) before sending the headers.
3510 if (AppSettings.EnsureSessionStateLockedOnFlush) {
3511 _context.EnsureSessionStateIfNecessary();
3516 internal enum CacheDependencyType {
3522 struct ResponseDependencyList {
3523 private ArrayList _dependencies;
3524 private string[] _dependencyArray;
3525 private DateTime _oldestDependency;
3526 private string _requestVirtualPath;
3528 internal void AddDependency(string item, string argname) {
3530 throw new ArgumentNullException(argname);
3533 _dependencyArray = null;
3535 if (_dependencies == null) {
3536 _dependencies = new ArrayList(1);
3539 DateTime utcNow = DateTime.UtcNow;
3541 _dependencies.Add(new ResponseDependencyInfo(
3542 new string[] {item}, utcNow));
3544 // _oldestDependency is initialized to MinValue and indicates that it must always be set
3545 if (_oldestDependency == DateTime.MinValue || utcNow < _oldestDependency)
3546 _oldestDependency = utcNow;
3549 internal void AddDependencies(ArrayList items, string argname) {
3550 if (items == null) {
3551 throw new ArgumentNullException(argname);
3554 string[] a = (string[]) items.ToArray(typeof(string));
3555 AddDependencies(a, argname, false);
3558 internal void AddDependencies(string[] items, string argname) {
3559 AddDependencies(items, argname, true);
3562 internal void AddDependencies(string[] items, string argname, bool cloneArray) {
3563 AddDependencies(items, argname, cloneArray, DateTime.UtcNow);
3566 internal void AddDependencies(string[] items, string argname, bool cloneArray, string requestVirtualPath) {
3567 if (requestVirtualPath == null)
3568 throw new ArgumentNullException("requestVirtualPath");
3570 _requestVirtualPath = requestVirtualPath;
3571 AddDependencies(items, argname, cloneArray, DateTime.UtcNow);
3574 internal void AddDependencies(string[] items, string argname, bool cloneArray, DateTime utcDepTime) {
3575 if (items == null) {
3576 throw new ArgumentNullException(argname);
3579 string [] itemsLocal;
3582 itemsLocal = (string[]) items.Clone();
3588 foreach (string item in itemsLocal) {
3589 if (String.IsNullOrEmpty(item)) {
3590 throw new ArgumentNullException(argname);
3594 _dependencyArray = null;
3596 if (_dependencies == null) {
3597 _dependencies = new ArrayList(1);
3600 _dependencies.Add(new ResponseDependencyInfo(itemsLocal, utcDepTime));
3602 // _oldestDependency is initialized to MinValue and indicates that it must always be set
3603 if (_oldestDependency == DateTime.MinValue || utcDepTime < _oldestDependency)
3604 _oldestDependency = utcDepTime;
3607 internal bool HasDependencies() {
3608 if (_dependencyArray == null && _dependencies == null)
3614 internal string[] GetDependencies() {
3615 if (_dependencyArray == null && _dependencies != null) {
3617 foreach (ResponseDependencyInfo info in _dependencies) {
3618 size += info.items.Length;
3621 _dependencyArray = new string[size];
3624 foreach (ResponseDependencyInfo info in _dependencies) {
3625 int length = info.items.Length;
3626 Array.Copy(info.items, 0, _dependencyArray, index, length);
3631 return _dependencyArray;
3634 // The caller of this method must dispose the cache dependencies
3635 internal CacheDependency CreateCacheDependency(CacheDependencyType dependencyType, CacheDependency dependency) {
3636 if (_dependencies != null) {
3637 if (dependencyType == CacheDependencyType.Files
3638 || dependencyType == CacheDependencyType.CacheItems) {
3639 foreach (ResponseDependencyInfo info in _dependencies) {
3640 CacheDependency dependencyOld = dependency;
3642 if (dependencyType == CacheDependencyType.Files) {
3643 dependency = new CacheDependency(0, info.items, null, dependencyOld, info.utcDate);
3646 // We create a "public" CacheDepdency here, since the keys are for public items.
3647 dependency = new CacheDependency(null, info.items, dependencyOld,
3648 DateTimeUtil.ConvertToLocalTime(info.utcDate));
3652 if (dependencyOld != null) {
3653 dependencyOld.Dispose();
3659 CacheDependency virtualDependency = null;
3660 VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
3661 if (vpp != null && _requestVirtualPath != null) {
3662 virtualDependency = vpp.GetCacheDependency(_requestVirtualPath, GetDependencies(), _oldestDependency);
3664 if (virtualDependency != null) {
3665 AggregateCacheDependency tempDep = new AggregateCacheDependency();
3666 tempDep.Add(virtualDependency);
3667 if (dependency != null) {
3668 tempDep.Add(dependency);
3670 dependency = tempDep;
3679 internal class ResponseDependencyInfo {
3680 internal readonly string[] items;
3681 internal readonly DateTime utcDate;
3683 internal ResponseDependencyInfo(string[] items, DateTime utcDate) {
3685 this.utcDate = utcDate;