// Author:
// Miguel de Icaza (miguel@novell.com)
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
-//
-
//
// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
//
// 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.Text;
using System.Web.UI;
using System.Collections;
using System.Threading;
using System.Web.Util;
using System.Globalization;
+using System.Security.Permissions;
namespace System.Web {
+ // CAS - no InheritanceDemand here as the class is sealed
+ [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public sealed class HttpResponse {
internal HttpWorkerRequest WorkerRequest;
internal HttpResponseStream output_stream;
string status_description = "OK";
string content_type = "text/html";
- string charset = "utf-8";
+ string charset;
+ bool charset_set;
CachedRawResponse cached_response;
- string cache_control = "private";
+ string user_cache_control = "private";
string redirect_location;
//
// Transfer encoding state
//
string transfer_encoding;
- internal byte [] use_chunked;
+ internal bool use_chunked;
bool closed;
internal bool suppress_content;
//
internal object FlagEnd = new object ();
- internal readonly static byte [] ChunkedNewline = new byte [2] { 13, 10 };
-
internal HttpResponse ()
{
output_stream = new HttpResponseStream (this);
this.context = context;
if (worker_request != null)
- use_chunked = worker_request.GetHttpVersion () == "HTTP/1.1" ? new byte [24] : null;
+ use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1");
}
internal TextWriter SetTextWriter (TextWriter writer)
//
public Encoding ContentEncoding {
get {
- if (encoding == null){
- string client_content_type = context.Request.ContentType;
- if (client_content_type != ""){
- try {
- // Do what the #1 web server does
- encoding = Encoding.GetEncoding (HttpRequest.GetParameter (content_type, "; charset="));
- } catch {
- encoding = WebEncoding.ResponseEncoding;
+ if (encoding == null) {
+ if (context != null) {
+ string client_content_type = context.Request.ContentType;
+ string parameter = HttpRequest.GetParameter (client_content_type, "; charset=");
+ if (parameter != null) {
+ try {
+ // Do what the #1 web server does
+ encoding = Encoding.GetEncoding (parameter);
+ } catch {
+ }
}
- } else
+ }
+ if (encoding == null)
encoding = WebEncoding.ResponseEncoding;
}
return encoding;
}
set {
+ charset_set = true;
charset = value;
}
}
Cache.SetExpires (value);
}
}
-
+
public Stream Filter {
get {
- throw new NotImplementedException ();
+ if (WorkerRequest == null)
+ return null;
+
+ return output_stream.Filter;
}
set {
+ output_stream.Filter = value;
+ }
+ }
+#if NET_2_0
+ [MonoTODO]
+ public Encoding HeaderEncoding {
+ get { throw new NotImplementedException (); }
+ set {
+ if (value == null)
+ throw new ArgumentNullException ("HeaderEncoding");
throw new NotImplementedException ();
}
}
-
+#endif
public bool IsClientConnected {
get {
+ if (WorkerRequest == null)
+ return true; // yep that's true
+
return WorkerRequest.IsClientConnected ();
}
}
-
+#if NET_2_0
+ [MonoTODO]
+ public bool IsRequestBeingRedirected {
+ get { throw new NotImplementedException (); }
+ }
+#endif
public TextWriter Output {
get {
if (writer == null)
suppress_content = value;
}
}
+#if NET_2_0
+ [MonoTODO]
+ public void AddCacheDependency (CacheDependency[] dependencies)
+ {
+ throw new NotImplementedException ();
+ }
+ [MonoTODO]
+ public void AddCacheItemDependencies (string[] cacheKeys)
+ {
+ throw new NotImplementedException ();
+ }
+#endif
+ [MonoTODO]
public void AddCacheItemDependencies (ArrayList cacheKeys)
{
// TODO: talk to jackson about the cache
}
+ [MonoTODO]
public void AddCacheItemDependency (string cacheKey)
{
// TODO: talk to jackson about the cache
}
+ [MonoTODO]
public void AddFileDependencies (ArrayList filenames)
{
// TODO: talk to jackson about the cache
}
-
+#if NET_2_0
+ [MonoTODO]
+ public void AddFileDependencies (string[] filenames)
+ {
+ throw new NotImplementedException ();
+ }
+#endif
+ [MonoTODO]
public void AddFileDependency (string filename)
{
// TODO: talk to jackson about the cache
throw new HttpException ("headers have been already sent");
if (String.Compare (name, "content-length", true, CultureInfo.InvariantCulture) == 0){
- content_length = Int64.Parse (value);
- use_chunked = null;
+ content_length = (long) UInt64.Parse (value);
+ use_chunked = false;
return;
}
if (String.Compare (name, "transfer-encoding", true, CultureInfo.InvariantCulture) == 0){
transfer_encoding = value;
- use_chunked = null;
+ use_chunked = false;
return;
}
if (String.Compare (name, "cache-control", true, CultureInfo.InvariantCulture) == 0){
- cache_control = value;
+ user_cache_control = value;
return;
}
headers.Add (new UnknownResponseHeader (name, value));
}
+ [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
public void AppendToLog (string param)
{
Console.Write ("System.Web: ");
return virtualPath;
}
- internal void SendSize (long l)
- {
- string s = l.ToString ();
- int i;
-
- for (i = 0; i < s.Length; i++){
- use_chunked [i] = (byte) s [i];
- }
- use_chunked [i++] = 13;
- use_chunked [i++] = 10;
-
- WorkerRequest.SendResponseFromMemory (use_chunked, i);
- }
-
public void BinaryWrite (byte [] buffer)
{
- if (this.buffer)
- output_stream.Write (buffer, 0, buffer.Length);
- else {
- if (use_chunked != null)
- SendSize (buffer.Length);
-
- WorkerRequest.SendResponseFromMemory (buffer, buffer.Length);
-
- WorkerRequest.SendResponseFromMemory (ChunkedNewline, 2);
-
- //
- // TODO This hack is for current XSP
- //
- WorkerRequest.FlushResponse (false);
- }
+ output_stream.Write (buffer, 0, buffer.Length);
}
internal void BinaryWrite (byte [] buffer, int start, int len)
content_length = -1;
content_type = "text/html";
transfer_encoding = null;
- cache_control = "private";
+ user_cache_control = null;
headers.Clear ();
}
{
if (closed)
return;
- WorkerRequest.CloseConnection ();
+ if (WorkerRequest != null)
+ WorkerRequest.CloseConnection ();
closed = true;
}
Thread.CurrentThread.Abort (FlagEnd);
} else {
// If this is called from an async event, signal the completion
- // but don't thow.
+ // but don't throw.
context.ApplicationInstance.CompleteRequest ();
}
}
// Content-Type
// Transfer-Encoding (chunked)
// Cache-Control
- void WriteHeaders (bool final_flush)
+ void AddHeadersNoCache (ArrayList write_headers, bool final_flush)
{
- WorkerRequest.SendStatus (status_code, StatusDescription);
-
- 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;
- if (cached_headers != null)
- write_headers = cached_headers;
-
//
// Transfer-Encoding
//
- if (use_chunked != null)
+ if (use_chunked)
write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", "chunked"));
else if (transfer_encoding != null)
write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", transfer_encoding));
// 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 == null){
+ if (use_chunked){
+#if DEBUG
Console.WriteLine ("Setting to close2");
+#endif
write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
}
}
// If the content-length is not set, and we are not buffering, we must
// close at the end.
//
- if (use_chunked == null){
+ if (use_chunked){
+#if DEBUG
Console.WriteLine ("Setting to close");
+#endif
write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
}
}
if (cache_policy != null)
cache_policy.SetHeaders (this, headers);
else
- write_headers.Add (new UnknownResponseHeader ("Cache-Control", cache_control));
+ write_headers.Add (new UnknownResponseHeader ("Cache-Control", CacheControl));
//
// Content-Type
//
if (content_type != null){
- string header = content_type + ((charset == null || charset == "") ? "" : "; " + charset);
-
- if (content_type == "text/html")
- header = "text/html; charset=" + Charset;
- else {
- if (charset == null || charset == "")
- header = content_type;
- else
- header = content_type + "; charset=" + charset;
+ string header = content_type;
+
+ if (charset_set || header == "text/plain" || header == "text/html") {
+ if (header.IndexOf ("charset=") == -1) {
+ if (charset == null || charset == "")
+ charset = ContentEncoding.HeaderName;
+ header += "; charset=" + charset;
+ }
}
write_headers.Add (new UnknownResponseHeader ("Content-Type", header));
write_headers.Add (cookies.Get (i).GetCookieHeader ());
}
+ }
+
+ internal void WriteHeaders (bool final_flush)
+ {
+ if (headers_sent)
+ return;
+
+ if (WorkerRequest != null)
+ WorkerRequest.SendStatus (status_code, StatusDescription);
+
+ 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;
+ if (cached_headers != null)
+ write_headers = cached_headers;
+ else
+ AddHeadersNoCache (write_headers, final_flush);
+
//
// Flush
//
- HttpApplication app_instance = context.ApplicationInstance;
- if (app_instance != null)
- app_instance.TriggerPreSendRequestHeaders ();
-
- foreach (BaseResponseHeader header in write_headers){
- header.SendContent (WorkerRequest);
+ if (context != null) {
+ HttpApplication app_instance = context.ApplicationInstance;
+ if (app_instance != null)
+ app_instance.TriggerPreSendRequestHeaders ();
+ }
+ if (WorkerRequest != null) {
+ foreach (BaseResponseHeader header in write_headers){
+ header.SendContent (WorkerRequest);
+ }
}
headers_sent = true;
}
-
+
+ internal void DoFilter (bool close)
+ {
+ if (output_stream.HaveFilter && context != null && context.Error == null)
+ output_stream.ApplyFilter (close);
+ }
+
internal void Flush (bool final_flush)
{
+ DoFilter (final_flush);
if (!headers_sent){
if (final_flush || status_code != 200)
- use_chunked = null;
-
- WriteHeaders (final_flush);
+ use_chunked = false;
}
- if (suppress_content)
+ bool head = ((context != null) && (context.Request.HttpMethod == "HEAD"));
+ if (suppress_content || head) {
+ if (!headers_sent)
+ WriteHeaders (true);
+ output_stream.Clear ();
+ if (WorkerRequest != null)
+ output_stream.Flush (WorkerRequest, true); // ignore final_flush here.
return;
+ }
- HttpApplication app_instance = context.ApplicationInstance;
- if (app_instance != null)
- app_instance.TriggerPreSendRequestContent ();
+ if (!headers_sent)
+ WriteHeaders (final_flush);
+
+ if (context != null) {
+ HttpApplication app_instance = context.ApplicationInstance;
+ if (app_instance != null)
+ app_instance.TriggerPreSendRequestContent ();
+ }
if (IsCached) {
- byte [] data = output_stream.GetData ();
- cached_response.ContentLength = data.Length;
- cached_response.SetData (data);
+ MemoryStream ms = output_stream.GetData ();
+ cached_response.ContentLength = (int) ms.Length;
+ cached_response.SetData (ms.GetBuffer ());
}
- output_stream.Flush (WorkerRequest, final_flush);
-
- if (final_flush && use_chunked != null)
- Write ("0\r\n\r\n");
+ if (WorkerRequest != null)
+ output_stream.Flush (WorkerRequest, final_flush);
}
public void Flush ()
if (buffer)
return;
+ output_stream.ApplyFilter (false);
Flush ();
}
+#if TARGET_JVM
+ public void WriteFile (IntPtr fileHandle, long offset, long size) {
+ throw new NotSupportedException("IntPtr not supported");
+ }
+#else
public void WriteFile (IntPtr fileHandle, long offset, long size)
{
if (offset < 0)
if (size == 0)
return;
+ // Note: this .ctor will throw a SecurityException if the caller
+ // doesn't have the UnmanagedCode permission
using (FileStream fs = new FileStream (fileHandle, FileAccess.Read))
WriteFile (fs, offset, size);
if (buffer)
return;
+ output_stream.ApplyFilter (false);
Flush ();
}
+#endif
public void WriteFile (string filename, long offset, long size)
{
if (buffer)
return;
+ output_stream.ApplyFilter (false);
Flush ();
}
-
+#if NET_2_0
+ [MonoTODO]
+ public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
+ {
+ throw new NotImplementedException ();
+ }
+#endif
//
// Like WriteFile, but never buffers, so we manually Flush here
//
if (filename == null)
throw new ArgumentNullException ("filename");
+ TransmitFile (filename, false);
+ }
+
+ internal void TransmitFile (string filename, bool final_flush)
+ {
FileInfo fi = new FileInfo (filename);
+ using (Stream s = fi.OpenRead ()); // Just check if we can read.
output_stream.WriteFile (filename, 0, fi.Length);
- Flush ();
+ output_stream.ApplyFilter (final_flush);
+ Flush (final_flush);
}
//
public string CacheControl {
set {
- if (String.Compare (value, "public", true, CultureInfo.InvariantCulture) == 0 ||
- String.Compare (value, "private", true, CultureInfo.InvariantCulture) == 0 ||
- String.Compare (value, "no-cache", true, CultureInfo.InvariantCulture) == 0)
- cache_control = value;
- else
+ if (value == null || value == "") {
+ Cache.SetCacheability (HttpCacheability.NoCache);
+ user_cache_control = null;
+ } else if (String.Compare (value, "public", true, CultureInfo.InvariantCulture) == 0) {
+ Cache.SetCacheability (HttpCacheability.Public);
+ user_cache_control = "public";
+ } else if (String.Compare (value, "private", true, CultureInfo.InvariantCulture) == 0) {
+ Cache.SetCacheability (HttpCacheability.Private);
+ user_cache_control = "private";
+ } else if (String.Compare (value, "no-cache", true, CultureInfo.InvariantCulture) == 0) {
+ Cache.SetCacheability (HttpCacheability.NoCache);
+ user_cache_control = "no-cache";
+ } else
throw new ArgumentException ("CacheControl property only allows `public', " +
"`private' or no-cache, for different uses, use " +
"Response.AppendHeader");
-
}
- get {
- if (cache_policy != null){
- switch (Cache.Cacheability){
- case HttpCacheability.NoCache: return "private";
- case HttpCacheability.Private: return "private";
- case HttpCacheability.Server: return "private";
- case HttpCacheability.Public: return "public";
- case HttpCacheability.ServerAndPrivate: return "private";
- }
- throw new Exception ("Unknown internal state: " + Cache.Cacheability);
- } else
- return cache_control;
- }
+ get { return (user_cache_control != null) ? user_cache_control : "private"; }
}
#endregion
+ internal int GetOutputByteCount ()
+ {
+ return output_stream.GetTotalLength ();
+ }
+
internal void ReleaseResources ()
{
- output_stream.ReleaseResources ();
+ output_stream.ReleaseResources (true);
output_stream = null;
}
}
-
}
+