using System.Web.Caching;
using System.Threading;
using System.Web.Util;
+using System.Web.Configuration;
using System.Globalization;
using System.Security.Permissions;
+using System.Web.Hosting;
+using System.Web.SessionState;
namespace System.Web {
// CAS - no InheritanceDemand here as the class is sealed
[AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
- public sealed class HttpResponse {
+ public sealed partial class HttpResponse {
internal HttpWorkerRequest WorkerRequest;
internal HttpResponseStream output_stream;
internal bool buffer = true;
+
+ ArrayList fileDependencies;
HttpContext context;
TextWriter writer;
CachedRawResponse cached_response;
string user_cache_control = "private";
string redirect_location;
+ string version_header;
+ bool version_header_checked;
//
// Negative Content-Length means we auto-compute the size of content-length
// The list of the headers that we will send back to the client, except
// the headers that we compute here.
//
- ArrayList headers = new ArrayList ();
+
+ NameValueCollection headers;
bool headers_sent;
- ArrayList cached_headers;
+ NameValueCollection cached_headers;
//
// Transfer encoding state
//
string app_path_mod;
- //
- // Passed as flags
- //
- internal object FlagEnd = new object ();
+#if NET_2_0
+ bool is_request_being_redirected;
+ Encoding headerEncoding;
+#endif
internal HttpResponse ()
{
WorkerRequest = worker_request;
this.context = context;
+#if !TARGET_J2EE
if (worker_request != null)
use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1");
+#endif
}
internal TextWriter SetTextWriter (TextWriter writer)
this.writer = writer;
return prev;
}
+
+ internal string VersionHeader {
+ get {
+ if (!version_header_checked && version_header == null) {
+ version_header_checked = true;
+#if NET_2_0
+ HttpRuntimeSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection;
+#else
+ HttpRuntimeConfig config = HttpContext.GetAppConfig ("system.web/httpRuntime") as HttpRuntimeConfig;
+#endif
+ if (config != null && config.EnableVersionHeader)
+ version_header = Environment.Version.ToString (3);
+ }
+
+ return version_header;
+ }
+ }
+
+ internal string[] FileDependencies {
+ get {
+ if (fileDependencies == null || fileDependencies.Count == 0)
+ return new string[0] {};
+ return (string[]) fileDependencies.ToArray (typeof (string));
+ }
+ }
+
+ ArrayList FileDependenciesArray {
+ get {
+ if (fileDependencies == null)
+ fileDependencies = new ArrayList ();
+ return fileDependencies;
+ }
+ }
public bool Buffer {
get {
}
}
#if NET_2_0
- [MonoTODO ("Not implemented")]
public Encoding HeaderEncoding {
- get { throw new NotImplementedException (); }
+ get {
+ if (headerEncoding == null) {
+ GlobalizationSection gs = WebConfigurationManager.SafeGetSection ("system.web/globalization", typeof (GlobalizationSection)) as GlobalizationSection;
+
+ if (gs == null)
+ headerEncoding = Encoding.UTF8;
+ else {
+ headerEncoding = gs.ResponseHeaderEncoding;
+ if (headerEncoding == Encoding.Unicode)
+ throw new HttpException ("HeaderEncoding must not be Unicode");
+ }
+ }
+ return headerEncoding;
+ }
set {
+ if (headers_sent)
+ throw new HttpException ("headers have already been sent");
if (value == null)
throw new ArgumentNullException ("HeaderEncoding");
- throw new NotImplementedException ();
+ if (value == Encoding.Unicode)
+ throw new HttpException ("HeaderEncoding must not be Unicode");
+ headerEncoding = value;
}
}
+
+ public
+#else
+ internal
#endif
+ NameValueCollection Headers {
+ get {
+ if (headers == null)
+ headers = new NameValueCollection ();
+
+ return headers;
+ }
+ }
+
+
public bool IsClientConnected {
get {
if (WorkerRequest == null)
}
}
#if NET_2_0
- [MonoTODO ("Not implemented")]
public bool IsRequestBeingRedirected {
- get { throw new NotImplementedException (); }
+ get { return is_request_being_redirected; }
}
#endif
public TextWriter Output {
}
public string Status {
- get {
- return String.Format ("{0} {1}", status_code, StatusDescription);
- }
+ get { return String.Concat (status_code.ToString (), " ", StatusDescription); }
set {
int p = value.IndexOf (' ');
}
}
+#if NET_2_0
+ // We ignore the two properties on Mono as they are for use with IIS7, but there is
+ // no point in throwing PlatformNotSupportedException. We might find a use for them
+ // some day.
+ public int SubStatusCode {
+ get;
+ set;
+ }
+
+ public bool TrySkipIisCustomErrors {
+ get;
+ set;
+ }
+#endif
+
public int StatusCode {
get {
return status_code;
suppress_content = value;
}
}
+
#if NET_2_0
[MonoTODO ("Not implemented")]
public void AddCacheDependency (CacheDependency[] dependencies)
{
// TODO: talk to jackson about the cache
}
-
- [MonoTODO("Currently does nothing")]
+
public void AddFileDependencies (ArrayList filenames)
{
- // TODO: talk to jackson about the cache
+ if (filenames == null || filenames.Count == 0)
+ return;
+ FileDependenciesArray.AddRange (filenames);
}
#if NET_2_0
- [MonoTODO ("Not implemented")]
public void AddFileDependencies (string[] filenames)
{
- throw new NotImplementedException ();
+ if (filenames == null || filenames.Length == 0)
+ return;
+ FileDependenciesArray.AddRange (filenames);
}
-#endif
- [MonoTODO ("Currently does nothing")]
+#endif
+
public void AddFileDependency (string filename)
{
- // TODO: talk to jackson about the cache
+ if (filename == null || filename == String.Empty)
+ return;
+ FileDependenciesArray.Add (filename);
}
public void AddHeader (string name, string value)
if (headers_sent)
throw new HttpException ("headers have been already sent");
+#if !TARGET_J2EE
if (String.Compare (name, "content-length", true, CultureInfo.InvariantCulture) == 0){
content_length = (long) UInt64.Parse (value);
use_chunked = false;
return;
}
+#endif
if (String.Compare (name, "content-type", true, CultureInfo.InvariantCulture) == 0){
ContentType = value;
return;
}
+#if !TARGET_J2EE
if (String.Compare (name, "transfer-encoding", true, CultureInfo.InvariantCulture) == 0){
transfer_encoding = value;
use_chunked = false;
return;
}
+#endif
if (String.Compare (name, "cache-control", true, CultureInfo.InvariantCulture) == 0){
user_cache_control = value;
return;
}
- headers.Add (new UnknownResponseHeader (name, value));
+ Headers.Add (name, value);
}
[AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
if (virtualPath == null)
return null;
- if (virtualPath == "")
+ if (virtualPath.Length == 0)
return context.Request.RootVirtualDir;
-
+
if (UrlUtils.IsRelativeUrl (virtualPath)) {
virtualPath = UrlUtils.Combine (context.Request.RootVirtualDir, virtualPath);
} else if (UrlUtils.IsRooted (virtualPath)) {
virtualPath = UrlUtils.Canonic (virtualPath);
}
-
+
+ bool cookieless = false;
+#if NET_2_0
+ SessionStateSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/sessionState") as SessionStateSection;
+ cookieless = SessionStateModule.IsCookieLess (context, config);
+#else
+ SessionConfig config = HttpContext.GetAppConfig ("system.web/sessionState") as SessionConfig;
+ cookieless = config.CookieLess;
+#endif
+ if (!cookieless)
+ return 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 (!StrUtils.StartsWith (virtualPath, basevd))
- return virtualPath;
-
- virtualPath = UrlUtils.Combine (rvd, virtualPath.Substring (basevd.Length));
+ if (UrlUtils.HasSessionId (virtualPath))
+ virtualPath = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (virtualPath), virtualPath);
+ return UrlUtils.InsertSessionId (app_path_mod, virtualPath);
}
return virtualPath;
public void ClearContent ()
{
output_stream.Clear ();
+ content_length = -1;
}
public void ClearHeaders ()
content_length = -1;
content_type = "text/html";
transfer_encoding = null;
- user_cache_control = null;
- headers.Clear ();
+ user_cache_control = "private";
+ if (cache_policy != null)
+ cache_policy.Cacheability = HttpCacheability.Private;
+
+ if (headers != null)
+ headers.Clear ();
}
internal bool HeadersSent {
closed = true;
}
+#if NET_2_0
+ public void DisableKernelCache ()
+ {
+ // does nothing in Mono
+ }
+#endif
+
public void End ()
{
+ if (context == null)
+ return;
+
if (context.TimeoutPossible) {
- Thread.CurrentThread.Abort (FlagEnd);
+ Thread.CurrentThread.Abort (FlagEnd.Value);
} else {
// If this is called from an async event, signal the completion
// but don't throw.
- context.ApplicationInstance.CompleteRequest ();
+ HttpApplication app_instance = context.ApplicationInstance;
+ if (app_instance != null)
+ app_instance.CompleteRequest ();
}
}
// Content-Type
// Transfer-Encoding (chunked)
// Cache-Control
- void AddHeadersNoCache (ArrayList write_headers, bool final_flush)
+ // X-AspNet-Version
+ void AddHeadersNoCache (NameValueCollection write_headers, bool final_flush)
{
+#if !TARGET_J2EE
//
// Transfer-Encoding
//
if (use_chunked)
- write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", "chunked"));
+ write_headers.Add ("Transfer-Encoding", "chunked");
else if (transfer_encoding != null)
- write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", transfer_encoding));
-
- UnknownResponseHeader date_header = new UnknownResponseHeader ("Date",
- DateTime.UtcNow.ToString ("r", CultureInfo.InvariantCulture));
- write_headers.Add (date_header);
-
- if (IsCached)
- cached_response.DateHeader = date_header;
-
+ write_headers.Add ("Transfer-Encoding", transfer_encoding);
+#endif
if (redirect_location != null)
- write_headers.Add (new UnknownResponseHeader ("Location", redirect_location));
+ write_headers.Add ("Location", redirect_location);
+#if !TARGET_J2EE
+ string vh = VersionHeader;
+ if (vh != null)
+ write_headers.Add ("X-AspNet-Version", vh);
+
//
// If Content-Length is set.
//
- if (content_length >= 0){
- write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderContentLength,
- content_length.ToString (CultureInfo.InvariantCulture)));
- } else if (Buffer){
- if (final_flush){
+ if (content_length >= 0) {
+ write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
+ content_length.ToString (CultureInfo.InvariantCulture));
+ } else if (BufferOutput) {
+ if (final_flush) {
//
// If we are buffering and this is the last flush, not a middle-flush,
// we know the content-length.
//
content_length = output_stream.total;
- write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderContentLength,
- content_length.ToString (CultureInfo.InvariantCulture)));
+ write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
+ content_length.ToString (CultureInfo.InvariantCulture));
} else {
//
// We are buffering, and this is a flush in the middle.
// If we are not chunked, we need to set "Connection: close".
//
if (use_chunked){
-#if DEBUG
- Console.WriteLine ("Setting to close2");
-#endif
- write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
+ write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
}
}
} else {
// close at the end.
//
if (use_chunked){
-#if DEBUG
- Console.WriteLine ("Setting to close");
-#endif
- write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
+ write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
}
}
+#endif
//
// Cache Control, the cache policy takes precedence over the cache_control property.
if (cache_policy != null)
cache_policy.SetHeaders (this, headers);
else
- write_headers.Add (new UnknownResponseHeader ("Cache-Control", CacheControl));
+ write_headers.Add ("Cache-Control", CacheControl);
//
// Content-Type
}
}
- write_headers.Add (new UnknownResponseHeader ("Content-Type", header));
+ write_headers.Add ("Content-Type", header);
}
if (cookies != null && cookies.Count != 0){
int n = cookies.Count;
for (int i = 0; i < n; i++)
- write_headers.Add (cookies.Get (i).GetCookieHeader ());
+ write_headers.Add ("Set-Cookie", cookies.Get (i).GetCookieHeaderValue ());
+#if TARGET_J2EE
+ // For J2EE Portal support emulate cookies by storing them in the session.
+ context.Request.SetSessionCookiesForPortal (cookies);
+#endif
}
-
}
internal void WriteHeaders (bool final_flush)
if (headers_sent)
return;
- if (WorkerRequest != null)
- WorkerRequest.SendStatus (status_code, StatusDescription);
+ //
+ // Flush
+ //
+ if (context != null) {
+ HttpApplication app_instance = context.ApplicationInstance;
+ if (app_instance != null)
+ app_instance.TriggerPreSendRequestHeaders ();
+ }
+
+ headers_sent = true;
if (cached_response != null)
cached_response.SetHeaders (headers);
// If this page is cached use the cached headers
// instead of the standard headers
- ArrayList write_headers = headers;
+ NameValueCollection write_headers;
if (cached_headers != null)
write_headers = cached_headers;
- else
+ else {
+ write_headers = Headers;
AddHeadersNoCache (write_headers, final_flush);
-
- //
- // Flush
- //
- if (context != null) {
- HttpApplication app_instance = context.ApplicationInstance;
- if (app_instance != null)
- app_instance.TriggerPreSendRequestHeaders ();
}
+
+ if (WorkerRequest != null)
+ WorkerRequest.SendStatus (status_code, StatusDescription);
+
if (WorkerRequest != null) {
- foreach (BaseResponseHeader header in write_headers){
- header.SendContent (WorkerRequest);
+ string header_name;
+ string[] values;
+ int header_index;
+
+ for (int i = 0; i < write_headers.Count; i++) {
+ header_name = write_headers.GetKey (i);
+ header_index = HttpWorkerRequest.GetKnownResponseHeaderIndex (header_name);
+ values = write_headers.GetValues (i);
+ if (values == null)
+ continue;
+
+ foreach (string val in values) {
+ if (header_index > -1)
+ WorkerRequest.SendKnownResponseHeader (header_index, val);
+ else
+ WorkerRequest.SendUnknownResponseHeader (header_name, val);
+ }
}
}
- headers_sent = true;
}
internal void DoFilter (bool close)
public void Redirect (string url, bool endResponse)
{
if (headers_sent)
- throw new HttpException ("header have been already sent");
+ throw new HttpException ("Headers have already been sent");
+#if NET_2_0
+ is_request_being_redirected = true;
+#endif
ClearHeaders ();
ClearContent ();
StatusCode = 302;
url = ApplyAppPathModifier (url);
- headers.Add (new UnknownResponseHeader ("Location", url));
+ redirect_location = url;
// Text for browsers that can't handle location header
Write ("<html><head><title>Object moved</title></head><body>\r\n");
- Write ("<h2>Object moved to <a href='" + url + "'>here</a></h2>\r\n");
+ Write ("<h2>Object moved to <a href=\"" + url + "\">here</a></h2>\r\n");
Write ("</body><html>\r\n");
if (endResponse)
End ();
+#if NET_2_0
+ is_request_being_redirected = false;
+#endif
}
public static void RemoveOutputCacheItem (string path)
if (path [0] != '/')
throw new ArgumentException ("'" + path + "' is not an absolute virtual path.");
- HttpRuntime.Cache.Remove (path);
+ HttpRuntime.InternalCache.Remove (path);
}
public void SetCookie (HttpCookie cookie)
#if TARGET_JVM
public void WriteFile (IntPtr fileHandle, long offset, long size) {
- throw new NotSupportedException("IntPtr not supported");
+ throw new PlatformNotSupportedException("IntPtr not supported");
}
#else
public void WriteFile (IntPtr fileHandle, long offset, long size)
output_stream.ApplyFilter (final_flush);
Flush (final_flush);
}
+
+#if NET_2_0
+ public void TransmitFile (string filename, long offset, long length)
+ {
+ output_stream.WriteFile (filename, offset, length);
+ output_stream.ApplyFilter (false);
+ Flush (false);
+ }
+ internal void TransmitFile (VirtualFile vf)
+ {
+ TransmitFile (vf, false);
+ }
+
+ const int bufLen = 65535;
+ internal void TransmitFile (VirtualFile vf, bool final_flush)
+ {
+ if (vf == null)
+ throw new ArgumentNullException ("vf");
+ if (vf is DefaultVirtualFile) {
+ TransmitFile (HostingEnvironment.MapPath (vf.VirtualPath), final_flush);
+ return;
+ }
+
+ byte[] buf = new byte [bufLen];
+ using (Stream s = vf.Open ()) {
+ int readBytes;
+ while ((readBytes = s.Read (buf, 0, bufLen)) > 0) {
+ output_stream.Write (buf, 0, readBytes);
+ output_stream.ApplyFilter (final_flush);
+ Flush (false);
+ }
+ if (final_flush)
+ Flush (true);
+ }
+ }
+#endif
+
#region Session state support
internal void SetAppPathModifier (string app_modifier)
{
#endregion
#region Cache Support
- internal void SetCachedHeaders (ArrayList headers)
+ internal void SetCachedHeaders (NameValueCollection headers)
{
cached_headers = headers;
+
}
internal bool IsCached {
- get {
- return cached_response != null;
+ get { return cached_response != null; }
+ set {
+ if (value)
+ cached_response = new CachedRawResponse (cache_policy);
+ else
+ cached_response = null;
}
}
public HttpCachePolicy Cache {
get {
- if (cache_policy == null) {
+ if (cache_policy == null)
cache_policy = new HttpCachePolicy ();
- cache_policy.CacheabilityUpdated += new CacheabilityUpdatedCallback (OnCacheabilityUpdated);
- }
return cache_policy;
}
- }
-
- private void OnCacheabilityUpdated (object sender, CacheabilityUpdatedEventArgs e)
- {
- if (e.Cacheability >= HttpCacheability.Server && !IsCached)
- cached_response = new CachedRawResponse (cache_policy);
- else if (e.Cacheability <= HttpCacheability.Private)
- cached_response = null;
- }
+ }
internal CachedRawResponse GetCachedResponse ()
{
- cached_response.StatusCode = StatusCode;
- cached_response.StatusDescription = StatusDescription;
+ if (cached_response != null) {
+ cached_response.StatusCode = StatusCode;
+ cached_response.StatusDescription = StatusDescription;
+ }
+
return cached_response;
}
output_stream = null;
}
}
+
+#if TARGET_J2EE
+ public
+#endif
+ static class FlagEnd
+ {
+ public static readonly object Value = new object ();
+ }
}