2 // System.Web.HttpResponse
5 // Patrik Torstensson (Patrik.Torstensson@labs2.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // (c) 2002 Ximian, Inc. (http://www.ximian.com)
11 using System.Collections;
12 using System.Globalization;
15 using System.Threading;
16 using System.Web.Util;
20 public sealed class HttpResponse
22 // Chunked encoding static helpers
23 static byte [] s_arrChunkSuffix = { 10, 13 };
24 static byte [] s_arrChunkEnd = { 10 , 13 };
25 static string s_sChunkedPrefix = "\r\n";
29 bool _bClientDisconnected;
30 bool _bSuppressHeaders;
31 bool _bSuppressContent;
40 bool _ClientDisconnected;
43 string _sCacheControl;
44 string _sTransferEncoding;
46 string _sStatusDescription;
48 HttpCookieCollection _Cookies;
49 HttpCachePolicy _CachePolicy;
51 Encoding _ContentEncoding;
55 TextWriter _TextWriter;
57 HttpWorkerRequest _WorkerRequest;
59 public HttpResponse (TextWriter output)
63 _bHeadersSent = false;
65 _Headers = new ArrayList ();
67 _sContentType = "text/html";
71 _sCacheControl = null;
74 _bSuppressContent = false;
75 _bSuppressHeaders = false;
76 _bClientDisconnected = false;
83 internal HttpResponse (HttpWorkerRequest WorkerRequest, HttpContext Context)
86 _WorkerRequest = WorkerRequest;
90 _bHeadersSent = false;
92 _Headers = new ArrayList ();
94 _sContentType = "text/html";
98 _sCacheControl = null;
101 _bSuppressContent = false;
102 _bSuppressHeaders = false;
103 _bClientDisconnected = false;
107 _Writer = new HttpWriter (this);
108 _TextWriter = _Writer;
111 internal Encoder ContentEncoder
114 return ContentEncoding.GetEncoder ();
118 internal void FinalFlush ()
123 internal void DoFilter ()
126 _Writer.FilterData (true);
129 [MonoTODO("We need to add cache headers also")]
130 private ArrayList GenerateHeaders ()
132 ArrayList oHeaders = new ArrayList (_Headers.ToArray ());
134 oHeaders.Add (new HttpResponseHeader ("X-Powered-By", "Mono"));
135 // save culture info, we need us info here
136 CultureInfo oSavedInfo = Thread.CurrentThread.CurrentCulture;
137 Thread.CurrentThread.CurrentCulture = new CultureInfo (0x0409);
139 string date = DateTime.Now.ToUniversalTime ().ToString ("ddd, d MMM yyyy HH:mm:ss ");
140 oHeaders.Add (new HttpResponseHeader ("Date", date + "GMT"));
142 Thread.CurrentThread.CurrentCulture = oSavedInfo;
144 if (_lContentLength > 0) {
145 oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderContentLength,
146 _lContentLength.ToString ()));
149 if (_sContentType != null) {
150 if (_sContentType.IndexOf ("charset=") == -1) {
151 if (Charset.Length == 0) {
152 Charset = ContentEncoding.HeaderName;
155 // Time to build our string
156 if (Charset.Length > 0) {
157 _sContentType += "; charset=" + Charset;
161 oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderContentType,
165 if (_sCacheControl != null) {
166 oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderPragma,
170 if (_sTransferEncoding != null) {
171 oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderTransferEncoding,
172 _sTransferEncoding));
175 // TODO: Add Cookie headers..
180 private void SendHeaders ()
182 _WorkerRequest.SendStatus (StatusCode, StatusDescription);
184 ArrayList oHeaders = GenerateHeaders ();
185 foreach (HttpResponseHeader oHeader in oHeaders)
186 oHeader.SendContent (_WorkerRequest);
188 _bHeadersSent = true;
194 return String.Format ("{0} {1}", StatusCode, StatusDescription);
202 iCode = Int32.Parse (value.Substring (0, value.IndexOf (' ')));
203 sMsg = value.Substring (value.IndexOf (' ') + 1);
204 } catch (Exception) {
205 throw new HttpException ("Invalid status string");
209 StatusDescription = sMsg;
214 public void AddCacheItemDependencies (ArrayList cacheKeys)
216 throw new NotImplementedException ();
220 public void AddCacheItemDependency(string cacheKey)
222 throw new NotImplementedException ();
226 public void AddFileDependencies (ArrayList filenames)
228 //throw new NotImplementedException();
232 public void AddFileDependency (string filename)
234 //throw new NotImplementedException();
237 public void AddHeader (string name, string value)
239 AppendHeader(name, value);
243 public void AppendCookie (HttpCookie cookie)
245 throw new NotImplementedException ();
249 public void AppendToLog (string param)
251 throw new NotImplementedException ();
255 public string ApplyAppPathModifier (string virtualPath)
257 throw new NotImplementedException ();
267 BufferOutput = value;
271 public bool BufferOutput
285 public HttpCachePolicy Cache
288 if (null == _CachePolicy)
289 _CachePolicy = new HttpCachePolicy ();
295 [MonoTODO("Set status in the cache policy")]
296 public string CacheControl
299 return _sCacheControl;
304 throw new HttpException ("Headers has been sent to the client");
306 _sCacheControl = value;
310 public string Charset
313 if (null == _sCharset)
314 _sCharset = ContentEncoding.WebName;
321 throw new HttpException ("Headers has been sent to the client");
327 public Encoding ContentEncoding
330 if (_ContentEncoding == null)
331 _ContentEncoding = WebEncoding.Encoding;
333 return _ContentEncoding;
338 throw new ArgumentException ("Can't set a null as encoding");
340 _ContentEncoding = value;
347 public string ContentType
350 return _sContentType;
355 throw new HttpException ("Headers has been sent to the client");
357 _sContentType = value;
361 public HttpCookieCollection Cookies
364 if (null == _Cookies)
365 _Cookies = new HttpCookieCollection (this, false);
371 [MonoTODO("Set expires in the cache policy")]
375 throw new NotImplementedException ();
379 throw new NotImplementedException ();
383 [MonoTODO("Set expiresabsolute in the cache policy")]
384 public DateTime ExpiresAbsolute
387 throw new NotImplementedException ();
391 throw new NotImplementedException ();
399 return _Writer.GetActiveFilter ();
406 throw new HttpException ("Filtering is not allowed");
408 _Writer.ActivateFilter (value);
412 public bool IsClientConnected
415 if (_ClientDisconnected)
418 if (null != _WorkerRequest && (!_WorkerRequest.IsClientConnected ())) {
419 _ClientDisconnected = false;
427 public TextWriter Output
434 public Stream OutputStream
438 throw new HttpException ("an Output stream not available when " +
439 "running with custom text writer");
441 return _Writer.OutputStream;
445 public string StatusDescription
448 if (null == _sStatusDescription)
449 _sStatusDescription =
450 HttpWorkerRequest.GetStatusDescription (_iStatusCode);
452 return _sStatusDescription;
457 throw new HttpException ("Headers has been sent to the client");
459 _sStatusDescription = value;
463 public int StatusCode
471 throw new HttpException ("Headers has been sent to the client");
473 _sStatusDescription = null;
474 _iStatusCode = value;
478 public bool SuppressContent
481 return _bSuppressContent;
486 throw new HttpException ("Headers has been sent to the client");
488 _bSuppressContent = true;
492 public HttpRequest Request
495 return _Context.Request;
499 internal void AppendHeader (int iIndex, string value)
502 throw new HttpException ("Headers has been sent to the client");
505 case HttpWorkerRequest.HeaderContentLength:
506 _lContentLength = Int64.Parse (value);
508 case HttpWorkerRequest.HeaderContentEncoding:
509 _sContentType = value;
511 case HttpWorkerRequest.HeaderTransferEncoding:
512 _sTransferEncoding = value;
513 if (value.Equals ("chunked")) {
519 case HttpWorkerRequest.HeaderPragma:
520 _sCacheControl = value;
523 _Headers.Add (new HttpResponseHeader (iIndex, value));
528 public void AppendHeader (string name, string value)
531 throw new HttpException ("Headers has been sent to the client");
533 switch (name.ToLower ()) {
534 case "content-length":
535 _lContentLength = Int64.Parse (value);
538 _sContentType = value;
540 case "transfer-encoding":
541 _sTransferEncoding = value;
542 if (value.Equals ("chunked")) {
549 _sCacheControl = value;
552 _Headers.Add (new HttpResponseHeader (name, value));
557 public void BinaryWrite (byte [] buffer)
559 OutputStream.Write (buffer, 0, buffer.Length);
568 public void ClearContent ()
573 public void ClearHeaders ()
576 throw new HttpException ("Headers has been sent to the client");
578 _sContentType = "text/html";
582 _Headers = new ArrayList ();
583 _sCacheControl = null;
584 _sTransferEncoding = null;
587 _bSuppressContent = false;
588 _bSuppressHeaders = false;
589 _bClientDisconnected = false;
594 _bClientDisconnected = false;
595 _WorkerRequest.CloseConnection ();
596 _bClientDisconnected = true;
599 internal void Dispose ()
601 if (_Writer != null) {
607 [MonoTODO("Handle callbacks into before done with session, needs to have a non ended flush here")]
608 internal void FlushAtEndOfRequest ()
613 [MonoTODO("Check timeout and if we can cancel the thread...")]
618 _WorkerRequest.CloseConnection ();
628 private void Flush (bool bFinish)
635 if (_Writer != null) {
636 _Writer.FlushBuffers ();
638 _TextWriter.Flush ();
643 if (!_bHeadersSent && !_bSuppressHeaders && !_bClientDisconnected) {
645 length = _Writer.BufferSize;
646 if (length == 0 && _lContentLength == 0)
647 _sContentType = null;
651 length = _Writer.BufferSize;
653 _WorkerRequest.SendCalculatedContentLength ((int) length);
654 } else if (_lContentLength == 0 && _iStatusCode == 200 &&
655 _sTransferEncoding == null) {
656 // Check we are going todo chunked encoding
657 string sProto = Request.ServerVariables ["SERVER_PROTOCOL"];
659 if (sProto != null && sProto == "HTTP/1.1") {
661 HttpWorkerRequest.HeaderTransferEncoding,
664 // Just in case, the old browsers sends a HTTP/1.0
665 // request with Connection: Keep-Alive
667 HttpWorkerRequest.HeaderConnection,
674 if ((!_bSuppressContent && Request.HttpMethod == "HEAD") || _Writer == null) {
675 _bSuppressContent = true;
678 if (!_bSuppressContent) {
679 _bClientDisconnected = false;
681 Encoding oASCII = Encoding.ASCII;
683 string chunk = Convert.ToString(_Writer.BufferSize, 16);
684 byte [] arrPrefix = oASCII.GetBytes (chunk + s_sChunkedPrefix);
686 _WorkerRequest.SendResponseFromMemory (arrPrefix,
689 _Writer.SendContent (_WorkerRequest);
691 _WorkerRequest.SendResponseFromMemory (s_arrChunkSuffix,
692 s_arrChunkSuffix.Length);
694 _WorkerRequest.SendResponseFromMemory (
695 s_arrChunkEnd, s_arrChunkEnd.Length);
697 _Writer.SendContent (_WorkerRequest);
700 _WorkerRequest.FlushResponse (bFinish);
710 public void Pics (string value)
712 AppendHeader ("PICS-Label", value);
716 public void Redirect (string url)
718 Redirect (url, true);
721 //FIXME: [1] this is an ugly hack to make it work until we have SimpleWorkerRequest!
722 private string redirectLocation;
723 public string RedirectLocation
726 return redirectLocation;
730 public void Redirect (string url, bool endResponse)
733 throw new HttpException ("Headers has been sent to the client");
738 redirectLocation = url;
739 //[1]AppendHeader(HttpWorkerRequest.HeaderLocation, url);
741 // Text for browsers that can't handle location header
742 Write ("<html><head><title>Object moved</title></head><body>\r\n");
743 Write ("<h2>Object moved to <a href='" + url + "'>here</a></h2>\r\n");
744 Write ("</body><html>\r\n");
753 public void Write (char ch)
755 _TextWriter.Write(ch);
758 public void Write (object obj)
760 _TextWriter.Write(obj);
763 public void Write (string str)
765 _TextWriter.Write (str);
768 public void Write (char [] buffer, int index, int count)
770 _TextWriter.Write (buffer, index, count);
774 public static void RemoveOutputCacheItem (string path)
776 throw new NotImplementedException ();
780 public void SetCookie (HttpCookie cookie)
782 throw new NotImplementedException ();
785 private void WriteFromStream (Stream stream, long offset, long length, long bufsize)
787 if (offset < 0 || length <= 0)
790 long stLength = stream.Length;
791 if (offset + length > stLength)
792 length = stLength - offset;
795 stream.Seek (offset, SeekOrigin.Begin);
797 byte [] fileContent = new byte [bufsize];
798 int count = (int) Math.Min (Int32.MaxValue, bufsize);
799 while (length > 0 && (count = stream.Read (fileContent, 0, count)) != 0) {
800 _Writer.WriteBytes (fileContent, 0, count);
802 count = (int) Math.Min (length, fileContent.Length);
806 public void WriteFile (string filename)
808 WriteFile (filename, false);
811 public void WriteFile (string filename, bool readIntoMemory)
813 FileStream fs = null;
815 fs = File.OpenRead (filename);
816 long size = fs.Length;
817 if (readIntoMemory) {
818 WriteFromStream (fs, 0, size, size);
820 WriteFromStream (fs, 0, size, 8192);
828 public void WriteFile (string filename, long offset, long size)
830 FileStream fs = null;
832 fs = File.OpenRead (filename);
833 WriteFromStream (fs, offset, size, 8192);
840 public void WriteFile (IntPtr fileHandle, long offset, long size)
842 FileStream fs = null;
844 fs = new FileStream (fileHandle, FileAccess.Read);
845 WriteFromStream (fs, offset, size, 8192);
853 internal void OnCookieAdd (HttpCookie cookie)
857 [MonoTODO("Do we need this?")]
858 internal void OnCookieChange (HttpCookie cookie)
863 internal void GoingToChangeCookieColl ()
868 internal void ChangedCookieColl ()