// System.Web.HttpResponse
//
// Authors:
-// Patrik Torstensson (Patrik.Torstensson@labs2.com)
+// Patrik Torstensson (Patrik.Torstensson@labs2.com)
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// (c) 2002 Ximian, Inc. (http://www.ximian.com)
//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
using System;
using System.Collections;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Web.Util;
+using System.Web.Caching;
namespace System.Web
{
public sealed class HttpResponse
{
// Chunked encoding static helpers
- static byte [] s_arrChunkSuffix = { 10, 13 };
- static byte [] s_arrChunkEnd = { 10 , 13 };
+ static byte [] s_arrChunkSuffix = {13, 10};
+ static byte [] s_arrChunkEnd = {48, 13, 10, 13, 10};
static string s_sChunkedPrefix = "\r\n";
ArrayList _Headers;
bool filtered;
long _lContentLength;
int _iStatusCode;
+
+ int _expiresInMinutes;
+ bool _expiresInMinutesSet;
+ DateTime _expiresAbsolute;
+ bool _expiresAbsoluteSet;
bool _ClientDisconnected;
+ bool closed;
string _sContentType;
string _sCacheControl;
HttpWorkerRequest _WorkerRequest;
ArrayList fileDependencies;
-
+ CachedRawResponse cached_response;
+ ArrayList cached_headers;
+#if NET_1_1
+ string redirectLocation;
+#endif
+
+ string app_path_mod = null;
+
public HttpResponse (TextWriter output)
{
_bBuffering = true;
_bClientDisconnected = false;
_bChunked = false;
-
- _Writer = new HttpWriter (this);
- _TextWriter = _Writer;
}
- internal Encoder ContentEncoder
+ internal void InitializeWriter ()
{
- get {
- return ContentEncoding.GetEncoder ();
+ // We cannot do this in the .ctor because HttpWriter uses configuration and
+ // it may not be initialized
+ if (_Writer == null) {
+ _Writer = new HttpWriter (this);
+ _TextWriter = _Writer;
}
}
-
+
internal void FinalFlush ()
{
Flush (true);
filtered = true;
}
- [MonoTODO("We need to add cache headers also")]
+ internal bool IsCached {
+ get { return cached_response != null; }
+ }
+
+ internal CachedRawResponse GetCachedResponse () {
+ cached_response.StatusCode = StatusCode;
+ cached_response.StatusDescription = StatusDescription;
+ return cached_response;
+ }
+
+ internal void SetCachedHeaders (ArrayList headers)
+ {
+ cached_headers = headers;
+ }
+
private ArrayList GenerateHeaders ()
{
ArrayList oHeaders = new ArrayList (_Headers.ToArray ());
CultureInfo oSavedInfo = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
- string date = DateTime.Now.ToUniversalTime ().ToString ("ddd, d MMM yyyy HH:mm:ss ");
- oHeaders.Add (new HttpResponseHeader ("Date", date + "GMT"));
+ string date = DateTime.UtcNow.ToString ("ddd, d MMM yyyy HH:mm:ss ");
+ HttpResponseHeader date_header = new HttpResponseHeader ("Date", date + "GMT");
+ oHeaders.Add (date_header);
+
+ if (IsCached)
+ cached_response.DateHeader = date_header;
Thread.CurrentThread.CurrentCulture = oSavedInfo;
_sContentType));
}
+ if (_CachePolicy != null)
+ _CachePolicy.SetHeaders (this, oHeaders);
+
if (_sCacheControl != null) {
oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderPragma,
_sCacheControl));
oHeaders.Add (_Cookies.Get (i).GetCookieHeader ());
}
}
-
+#if NET_1_1
+ if (redirectLocation != null)
+ oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderLocation,
+ redirectLocation));
+#endif
return oHeaders;
}
-
+
private void SendHeaders ()
{
_WorkerRequest.SendStatus (StatusCode, StatusDescription);
- ArrayList oHeaders = GenerateHeaders ();
+ ArrayList oHeaders;
+
+ if (cached_headers != null)
+ oHeaders = cached_headers;
+ else
+ oHeaders = GenerateHeaders ();
+
+ if (cached_response != null)
+ cached_response.SetHeaders (oHeaders);
+
foreach (HttpResponseHeader oHeader in oHeaders)
oHeader.SendContent (_WorkerRequest);
virtualPath = UrlUtils.Reduce (virtualPath);
}
+ if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) {
+ string rvd = _Context.Request.RootVirtualDir;
+ string basevd = rvd.Replace (app_path_mod, "");
+
+ if (!virtualPath.StartsWith (basevd))
+ return virtualPath;
+
+ virtualPath = UrlUtils.Combine (rvd, virtualPath.Substring (basevd.Length));
+ }
+
return virtualPath;
}
+ internal void SetAppPathModifier (string app_path_mod)
+ {
+ this.app_path_mod = app_path_mod;
+ }
+
public bool Buffer
{
get {
public HttpCachePolicy Cache
{
get {
- if (null == _CachePolicy)
+ if (null == _CachePolicy) {
_CachePolicy = new HttpCachePolicy ();
+ _CachePolicy.CacheabilityUpdated += new CacheabilityUpdatedCallback (
+ OnCacheabilityUpdated);
+ }
return _CachePolicy;
}
}
+ private void OnCacheabilityUpdated (object sender, CacheabilityUpdatedEventArgs e)
+ {
+ if (e.Cacheability >= HttpCacheability.Server && !IsCached)
+ cached_response = new CachedRawResponse (_CachePolicy);
+ else if (e.Cacheability <= HttpCacheability.Private)
+ cached_response = null;
+ }
+
[MonoTODO("Set status in the cache policy")]
public string CacheControl
{
{
get {
if (_ContentEncoding == null)
- _ContentEncoding = WebEncoding.Encoding;
+ _ContentEncoding = WebEncoding.ResponseEncoding;
return _ContentEncoding;
}
}
}
- [MonoTODO("Set expires in the cache policy")]
public int Expires
{
get {
- throw new NotImplementedException ();
+ return _expiresInMinutes;
}
set {
- throw new NotImplementedException ();
+ if (!_expiresInMinutesSet || (value < _expiresInMinutes))
+ {
+ _expiresInMinutes = value;
+ Cache.SetExpires(_Context.Timestamp.Add(new TimeSpan(0, _expiresInMinutes, 0)));
+ }
+ _expiresInMinutesSet = true;
}
}
- [MonoTODO("Set expiresabsolute in the cache policy")]
public DateTime ExpiresAbsolute
{
get {
- throw new NotImplementedException ();
+ return _expiresAbsolute;
}
set {
- throw new NotImplementedException ();
+ if (!_expiresAbsoluteSet || value.CompareTo(_expiresAbsolute)<0)
+ {
+ _expiresAbsolute = value;
+ Cache.SetExpires(_expiresAbsolute);
+ }
+ _expiresAbsoluteSet = true;
}
}
}
}
+#if NET_1_1
+ public string RedirectLocation {
+ get { return redirectLocation; }
+ set { redirectLocation = value; }
+ }
+#endif
+
public string StatusDescription
{
get {
}
set {
- if (_bHeadersSent)
- throw new HttpException ("Headers has been sent to the client");
-
_bSuppressContent = true;
}
}
break;
case HttpWorkerRequest.HeaderTransferEncoding:
_sTransferEncoding = value;
- if (value.Equals ("chunked")) {
- _bChunked = true;
- } else {
- _bChunked = false;
- }
+ _bChunked = (value == "chunked");
break;
case HttpWorkerRequest.HeaderPragma:
_sCacheControl = value;
break;
case "transfer-encoding":
_sTransferEncoding = value;
- if (value.Equals ("chunked")) {
- _bChunked = true;
- } else {
- _bChunked = false;
- }
+ _bChunked = (value == "chunked");
break;
case "pragma":
_sCacheControl = value;
OutputStream.Write (buffer, 0, buffer.Length);
}
+ internal void BinaryWrite (byte [] buffer, int start, int length)
+ {
+ OutputStream.Write (buffer, start, length);
+ }
+
public void Clear ()
{
if (_Writer != null)
Clear();
}
+ internal void SetHeadersSent (bool val)
+ {
+ _bHeadersSent = val;
+ }
+
public void ClearHeaders ()
{
if (_bHeadersSent)
public void Close ()
{
- _bClientDisconnected = false;
- _WorkerRequest.CloseConnection ();
- _bClientDisconnected = true;
+ if (closed && !_bClientDisconnected) {
+ _bClientDisconnected = false;
+ _WorkerRequest.CloseConnection ();
+ _bClientDisconnected = true;
+ }
}
internal void Dispose ()
if (_bEnded)
return;
+ if (_Context.TimeoutPossible)
+ Thread.CurrentThread.Abort (new StepCompleteRequest ());
+
Flush ();
_bEnded = true;
_Context.ApplicationInstance.CompleteRequest ();
public void Flush ()
{
+ if (closed)
+ throw new HttpException ("Response already finished.");
+
Flush (false);
}
private void Flush (bool bFinish)
{
- if (_bFlushing)
+ if (_bFlushing || closed)
return;
_bFlushing = true;
_sTransferEncoding == null) {
// Check we are going todo chunked encoding
string sProto = Request.ServerVariables ["SERVER_PROTOCOL"];
- sProto = "HTTP/1.0"; // Remove this line when we support properly
- // chunked content
-
if (sProto != null && sProto == "HTTP/1.1") {
AppendHeader (
HttpWorkerRequest.HeaderTransferEncoding,
}
if (length == 0) {
+ if (bFinish && _bChunked) {
+ _WorkerRequest.SendResponseFromMemory (s_arrChunkEnd,
+ s_arrChunkEnd.Length);
+ }
+
_WorkerRequest.FlushResponse (bFinish);
if (!bFinish)
_Writer.Clear ();
if (!_bSuppressContent && Request.HttpMethod == "HEAD")
_bSuppressContent = true;
+ if (_bSuppressContent)
+ _Writer.Clear ();
+
if (!_bSuppressContent) {
_bClientDisconnected = false;
if (_bChunked) {
} else {
_Writer.SendContent (_WorkerRequest);
}
- } else {
- _Writer.Clear ();
}
_WorkerRequest.FlushResponse (bFinish);
-
- if (!bFinish)
- _Writer.Clear ();
+ if (IsCached) {
+ cached_response.ContentLength = (int) length;
+ cached_response.SetData (_Writer.GetBuffer ());
+ }
+ _Writer.Clear ();
} finally {
+ if (bFinish)
+ closed = true;
_bFlushing = false;
}
}
End ();
}
+ internal bool RedirectCustomError (string errorPage)
+ {
+ if (_bHeadersSent)
+ return false;
+
+ if (Request.QueryString ["aspxerrorpath"] != null)
+ return false; // Prevent endless loop
+
+ Redirect (errorPage + "?aspxerrorpath=" + Request.Path, false);
+ return true;
+ }
+
public void Write (char ch)
{
_TextWriter.Write(ch);
_TextWriter.Write (buffer, index, count);
}
- [MonoTODO()]
public static void RemoveOutputCacheItem (string path)
{
- throw new NotImplementedException ();
+ if (path == null)
+ throw new ArgumentNullException ("path");
+
+ if (!UrlUtils.IsRooted (path))
+ throw new ArgumentException ("Invalid path for HttpResponse.RemoveOutputCacheItem '" +
+ path + "'. An absolute virtual path is expected.");
+
+ Cache cache = HttpRuntime.Cache;
+ cache.Remove (path);
}
public void SetCookie (HttpCookie cookie)