// Author:
// Miguel de Icaza (miguel@novell.com)
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
+// Marek Habersack <mhabersack@novell.com>
//
-// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
using System.Web.Hosting;
using System.Web.SessionState;
-namespace System.Web {
-
+#if NET_4_0
+using System.Web.Routing;
+#endif
+
+namespace System.Web
+{
// CAS - no InheritanceDemand here as the class is sealed
[AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
- public sealed partial class HttpResponse {
+ public sealed partial class HttpResponse
+ {
internal HttpWorkerRequest WorkerRequest;
internal HttpResponseStream output_stream;
internal bool buffer = true;
CachedRawResponse cached_response;
string user_cache_control = "private";
string redirect_location;
-
- static string version_header;
+ string version_header;
+ bool version_header_checked;
//
// Negative Content-Length means we auto-compute the size of content-length
// the headers that we compute here.
//
- NameValueCollection headers;
+ HttpHeaderCollection headers;
bool headers_sent;
NameValueCollection cached_headers;
// Session State
//
string app_path_mod;
-
-#if NET_2_0
bool is_request_being_redirected;
Encoding headerEncoding;
-#endif
- static HttpResponse ()
- {
-#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);
- }
-
internal HttpResponse ()
{
output_stream = new HttpResponseStream (this);
+ writer = new HttpWriter (this);
}
public HttpResponse (TextWriter writer) : this ()
{
+
this.writer = writer;
}
if (worker_request != null)
use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1");
#endif
+ writer = new HttpWriter (this);
}
-
+
internal TextWriter SetTextWriter (TextWriter writer)
{
TextWriter prev = this.writer;
return prev;
}
+ internal string VersionHeader {
+ get {
+ if (!version_header_checked && version_header == null) {
+ version_header_checked = true;
+ HttpRuntimeSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection;
+ if (config != null && config.EnableVersionHeader)
+ version_header = Environment.Version.ToString (3);
+ }
+
+ return version_header;
+ }
+ }
+
+ internal HttpContext Context {
+ get { return context; }
+ set { context = value; }
+ }
+
internal string[] FileDependencies {
get {
if (fileDependencies == null || fileDependencies.Count == 0)
output_stream.Filter = value;
}
}
-#if NET_2_0
+
public Encoding HeaderEncoding {
get {
if (headerEncoding == null) {
}
}
- public
-#else
- internal
-#endif
- NameValueCollection Headers {
+ public NameValueCollection Headers {
get {
if (headers == null)
- headers = new NameValueCollection ();
+ headers = new HttpHeaderCollection ();
return headers;
}
return WorkerRequest.IsClientConnected ();
}
}
-#if NET_2_0
+
public bool IsRequestBeingRedirected {
get { return is_request_being_redirected; }
}
-#endif
+
public TextWriter Output {
get {
- if (writer == null)
- writer = new HttpWriter (this);
-
return writer;
}
+#if NET_4_0
+ set { writer = value; }
+#endif
}
public Stream OutputStream {
string s = value.Substring (0, p);
-#if NET_2_0
if (!Int32.TryParse (s, out status_code))
throw new HttpException ("Invalid format for the Status property");
-#else
-
- try {
- status_code = Int32.Parse (s);
- } catch {
- throw new HttpException ("Invalid format for the Status property");
- }
-#endif
status_description = value.Substring (p+1);
}
}
-#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.
get;
set;
}
-#endif
public int StatusCode {
get {
}
}
-#if NET_2_0
[MonoTODO ("Not implemented")]
public void AddCacheDependency (CacheDependency[] dependencies)
{
{
throw new NotImplementedException ();
}
-#endif
+
[MonoTODO("Currently does nothing")]
public void AddCacheItemDependencies (ArrayList cacheKeys)
{
return;
FileDependenciesArray.AddRange (filenames);
}
-#if NET_2_0
+
public void AddFileDependencies (string[] filenames)
{
if (filenames == null || filenames.Length == 0)
return;
FileDependenciesArray.AddRange (filenames);
}
-#endif
public void AddFileDependency (string filename)
{
public void AppendHeader (string name, string value)
{
if (headers_sent)
- throw new HttpException ("headers have been already sent");
-
+ throw new HttpException ("Headers have been already sent");
#if !TARGET_J2EE
- if (String.Compare (name, "content-length", true, CultureInfo.InvariantCulture) == 0){
+ if (String.Compare (name, "content-length", true, Helpers.InvariantCulture) == 0){
content_length = (long) UInt64.Parse (value);
use_chunked = false;
return;
}
#endif
- if (String.Compare (name, "content-type", true, CultureInfo.InvariantCulture) == 0){
+ if (String.Compare (name, "content-type", true, Helpers.InvariantCulture) == 0){
ContentType = value;
return;
}
#if !TARGET_J2EE
- if (String.Compare (name, "transfer-encoding", true, CultureInfo.InvariantCulture) == 0){
+ if (String.Compare (name, "transfer-encoding", true, Helpers.InvariantCulture) == 0){
transfer_encoding = value;
use_chunked = false;
return;
}
#endif
- if (String.Compare (name, "cache-control", true, CultureInfo.InvariantCulture) == 0){
+ if (String.Compare (name, "cache-control", true, Helpers.InvariantCulture) == 0){
user_cache_control = value;
return;
}
public string ApplyAppPathModifier (string virtualPath)
{
- if (virtualPath == null)
+ if (virtualPath == null || context == null)
return null;
if (virtualPath.Length == 0)
}
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;
content_length = -1;
content_type = "text/html";
transfer_encoding = null;
- user_cache_control = null;
+ user_cache_control = "private";
+ if (cache_policy != null)
+ cache_policy.Cacheability = HttpCacheability.Private;
+
if (headers != null)
headers.Clear ();
}
closed = true;
}
-#if NET_2_0
public void DisableKernelCache ()
{
// does nothing in Mono
}
-#endif
public void End ()
{
write_headers.Add ("Location", redirect_location);
#if !TARGET_J2EE
- if (version_header != null)
- write_headers.Add ("X-AspNet-Version", version_header);
+ 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 (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
- content_length.ToString (CultureInfo.InvariantCulture));
+ content_length.ToString (Helpers.InvariantCulture));
} else if (BufferOutput) {
if (final_flush) {
//
//
content_length = output_stream.total;
write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
- content_length.ToString (CultureInfo.InvariantCulture));
+ content_length.ToString (Helpers.InvariantCulture));
} else {
//
// We are buffering, and this is a flush in the middle.
if (WorkerRequest != null) {
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)
- WorkerRequest.SendUnknownResponseHeader (header_name, val);
+ foreach (string val in values) {
+ if (header_index > -1)
+ WorkerRequest.SendKnownResponseHeader (header_index, val);
+ else
+ WorkerRequest.SendUnknownResponseHeader (header_name, val);
+ }
}
}
}
app_instance.TriggerPreSendRequestContent ();
}
- if (IsCached) {
- MemoryStream ms = output_stream.GetData ();
- cached_response.ContentLength = (int) ms.Length;
- cached_response.SetData (ms.GetBuffer ());
- }
+ if (IsCached)
+ cached_response.SetData (output_stream.GetData ());
if (WorkerRequest != null)
output_stream.Flush (WorkerRequest, final_flush);
AppendHeader ("PICS-Label", value);
}
- public void Redirect (string url)
- {
- Redirect (url, true);
- }
-
- public void Redirect (string url, bool endResponse)
+ void Redirect (string url, bool endResponse, int code)
{
+ if (url == null)
+ throw new ArgumentNullException ("url");
+
if (headers_sent)
throw new HttpException ("Headers have already been sent");
-#if NET_2_0
+ if (url.IndexOf ('\n') != -1)
+ throw new ArgumentException ("Redirect URI cannot contain newline characters.", "url");
+
is_request_being_redirected = true;
-#endif
ClearHeaders ();
ClearContent ();
- StatusCode = 302;
+ StatusCode = code;
url = ApplyAppPathModifier (url);
+
+ bool isFullyQualified;
+ if (StrUtils.StartsWith (url, "http:", true) ||
+ StrUtils.StartsWith (url, "https:", true) ||
+ StrUtils.StartsWith (url, "file:", true) ||
+ StrUtils.StartsWith (url, "ftp:", true))
+ isFullyQualified = true;
+ else
+ isFullyQualified = false;
+
+ if (!isFullyQualified) {
+ HttpRuntimeSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection;
+ if (config != null && config.UseFullyQualifiedRedirectUrl) {
+ var ub = new UriBuilder (context.Request.Url);
+ ub.Path = url;
+ ub.Fragment = null;
+ ub.Password = null;
+ ub.Query = null;
+ ub.UserName = null;
+ url = ub.Uri.ToString ();
+ }
+ }
+
redirect_location = url;
// Text for browsers that can't handle location header
if (endResponse)
End ();
-#if NET_2_0
is_request_being_redirected = false;
-#endif
+ }
+
+ public void Redirect (string url)
+ {
+ Redirect (url, true);
}
+ public void Redirect (string url, bool endResponse)
+ {
+ Redirect (url, endResponse, 302);
+ }
+#if NET_4_0
+ public void RedirectPermanent (string url)
+ {
+ RedirectPermanent (url, true);
+ }
+
+ public void RedirectPermanent (string url, bool endResponse)
+ {
+ Redirect (url, endResponse, 301);
+ }
+
+ public void RedirectToRoute (object routeValues)
+ {
+ RedirectToRoute ("RedirectToRoute", null, new RouteValueDictionary (routeValues), 302, true);
+ }
+
+ public void RedirectToRoute (RouteValueDictionary routeValues)
+ {
+ RedirectToRoute ("RedirectToRoute", null, routeValues, 302, true);
+ }
+
+ public void RedirectToRoute (string routeName)
+ {
+ RedirectToRoute ("RedirectToRoute", routeName, null, 302, true);
+ }
+
+ public void RedirectToRoute (string routeName, object routeValues)
+ {
+ RedirectToRoute ("RedirectToRoute", routeName, new RouteValueDictionary (routeValues), 302, true);
+ }
+
+ public void RedirectToRoute (string routeName, RouteValueDictionary routeValues)
+ {
+ RedirectToRoute ("RedirectToRoute", routeName, routeValues, 302, true);
+ }
+
+ public void RedirectToRoutePermanent (object routeValues)
+ {
+ RedirectToRoute ("RedirectToRoutePermanent", null, new RouteValueDictionary (routeValues), 301, false);
+ }
+
+ public void RedirectToRoutePermanent (RouteValueDictionary routeValues)
+ {
+ RedirectToRoute ("RedirectToRoutePermanent", null, routeValues, 301, false);
+ }
+
+ public void RedirectToRoutePermanent (string routeName)
+ {
+ RedirectToRoute ("RedirectToRoutePermanent", routeName, null, 301, false);
+ }
+
+ public void RedirectToRoutePermanent (string routeName, object routeValues)
+ {
+ RedirectToRoute ("RedirectToRoutePermanent", routeName, new RouteValueDictionary (routeValues), 301, false);
+ }
+
+ public void RedirectToRoutePermanent (string routeName, RouteValueDictionary routeValues)
+ {
+ RedirectToRoute ("RedirectToRoutePermanent", routeName, routeValues, 301, false);
+ }
+
+ void RedirectToRoute (string callerName, string routeName, RouteValueDictionary routeValues, int redirectCode, bool endResponse)
+ {
+ HttpContext ctx = context ?? HttpContext.Current;
+ HttpRequest req = ctx != null ? ctx.Request : null;
+
+ if (req == null)
+ // Let's emulate .NET
+ throw new NullReferenceException ();
+
+ VirtualPathData vpd = RouteTable.Routes.GetVirtualPath (req.RequestContext, routeName, routeValues);
+ string redirectUrl = vpd != null ? vpd.VirtualPath : null;
+ if (String.IsNullOrEmpty (redirectUrl))
+ throw new InvalidOperationException ("No matching route found for RedirectToRoute");
+
+ Redirect (redirectUrl, true, redirectCode);
+ }
+
+ public static void RemoveOutputCacheItem (string path, string providerName)
+ {
+ if (path == null)
+ throw new ArgumentNullException ("path");
+
+ if (path.Length > 0 && path [0] != '/')
+ throw new ArgumentException ("Invalid path for HttpResponse.RemoveOutputCacheItem: '" + path + "'. An absolute virtual path is expected");
+
+ OutputCache.RemoveFromProvider (path, providerName);
+ }
+#endif
public static void RemoveOutputCacheItem (string path)
{
if (path == null)
if (path [0] != '/')
throw new ArgumentException ("'" + path + "' is not an absolute virtual path.");
- HttpRuntime.InternalCache.Remove (path);
+#if NET_4_0
+ RemoveOutputCacheItem (path, OutputCache.DefaultProviderName);
+#else
+ HttpContext context = HttpContext.Current;
+ HttpApplication app_instance = context != null ? context.ApplicationInstance : null;
+ HttpModuleCollection modules = app_instance != null ? app_instance.Modules : null;
+ OutputCacheModule ocm = modules != null ? modules.Get ("OutputCache") as OutputCacheModule : null;
+ OutputCacheProvider internalProvider = ocm != null ? ocm.InternalProvider : null;
+ if (internalProvider == null)
+ return;
+
+ internalProvider.Remove (path);
+#endif
}
public void SetCookie (HttpCookie cookie)
public void Write (char ch)
{
- Output.Write (ch);
+ TextWriter writer = Output;
+#if NET_4_0
+ // Emulating .NET
+ if (writer == null)
+ throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
+#endif
+ writer.Write (ch);
}
public void Write (object obj)
{
+ TextWriter writer = Output;
+#if NET_4_0
+ // Emulating .NET
+ if (writer == null)
+ throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
+#endif
if (obj == null)
return;
- Output.Write (obj.ToString ());
+ writer.Write (obj.ToString ());
}
public void Write (string s)
{
- Output.Write (s);
+ TextWriter writer = Output;
+#if NET_4_0
+ // Emulating .NET
+ if (writer == null)
+ throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
+#endif
+ writer.Write (s);
}
public void Write (char [] buffer, int index, int count)
{
- Output.Write (buffer, index, count);
+ TextWriter writer = Output;
+#if NET_4_0
+ // Emulating .NET
+ if (writer == null)
+ throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
+#endif
+ writer.Write (buffer, index, count);
}
internal void WriteFile (FileStream fs, long offset, long size)
output_stream.ApplyFilter (false);
Flush ();
}
-#if NET_2_0
- [MonoTODO ("Not implemented")]
+
public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
{
- throw new NotImplementedException ();
+ // Emulation of .NET behavior
+ if (callback == null)
+ throw new NullReferenceException ();
+
+ object target = callback.Target;
+ if (target != null && target.GetType () == typeof (Control))
+ throw new ArgumentException ("callback");
+
+ string s = callback (context);
+ if (!IsCached) {
+ Write (s);
+ return;
+ }
+
+ Cache.Cacheability = HttpCacheability.Server;
+ Flush ();
+ if (WorkerRequest == null)
+ Write (s); // better this than nothing
+ else {
+ byte[] bytes = WebEncoding.ResponseEncoding.GetBytes (s);
+ WorkerRequest.SendResponseFromMemory (bytes, bytes.Length);
+ }
+
+ cached_response.SetData (callback);
}
-#endif
+
//
// Like WriteFile, but never buffers, so we manually Flush here
//
Flush (final_flush);
}
-#if NET_2_0
public void TransmitFile (string filename, long offset, long length)
{
output_stream.WriteFile (filename, offset, length);
Flush (true);
}
}
-#endif
#region Session state support
internal void SetAppPathModifier (string app_modifier)
if (value == null || value == "") {
Cache.SetCacheability (HttpCacheability.NoCache);
user_cache_control = null;
- } else if (String.Compare (value, "public", true, CultureInfo.InvariantCulture) == 0) {
+ } else if (String.Compare (value, "public", true, Helpers.InvariantCulture) == 0) {
Cache.SetCacheability (HttpCacheability.Public);
user_cache_control = "public";
- } else if (String.Compare (value, "private", true, CultureInfo.InvariantCulture) == 0) {
+ } else if (String.Compare (value, "private", true, Helpers.InvariantCulture) == 0) {
Cache.SetCacheability (HttpCacheability.Private);
user_cache_control = "private";
- } else if (String.Compare (value, "no-cache", true, CultureInfo.InvariantCulture) == 0) {
+ } else if (String.Compare (value, "no-cache", true, Helpers.InvariantCulture) == 0) {
Cache.SetCacheability (HttpCacheability.NoCache);
user_cache_control = "no-cache";
} else