2 // System.Web.HttpResponse
\r
5 // Patrik Torstensson (Patrik.Torstensson@labs2.com)
\r
8 using System.Collections;
\r
11 using System.Globalization;
\r
13 namespace System.Web {
\r
14 public sealed class HttpResponse {
\r
15 // Chunked encoding static helpers
\r
16 private static byte [] s_arrChunkSuffix = { 10, 13 };
\r
17 private static byte [] s_arrChunkEnd = { 10 , 13 };
\r
18 private static string s_sChunkedPrefix = "\r\n";
\r
20 private ArrayList _Headers;
\r
22 private bool _bClientDisconnected;
\r
23 private bool _bSuppressHeaders;
\r
24 private bool _bSuppressContent;
\r
25 private bool _bChunked;
\r
26 private bool _bEnded;
\r
27 private bool _bBuffering;
\r
28 private bool _bHeadersSent;
\r
29 private bool _bFlushing;
\r
30 private long _lContentLength;
\r
31 private int _iStatusCode;
\r
33 private bool _ClientDisconnected;
\r
35 private string _sContentType;
\r
36 private string _sCacheControl;
\r
37 private string _sTransferEncoding;
\r
38 private string _sCharset;
\r
39 private string _sStatusDescription;
\r
41 private HttpCookieCollection _Cookies;
\r
42 private HttpCachePolicy _CachePolicy;
\r
44 private Encoding _ContentEncoding;
\r
46 private HttpContext _Context;
\r
47 private HttpWriter _Writer;
\r
48 private TextWriter _TextWriter;
\r
50 private HttpWorkerRequest _WorkerRequest;
\r
52 public HttpResponse(TextWriter output) {
\r
55 _bHeadersSent = false;
\r
57 _Headers = new ArrayList();
\r
59 _sContentType = "text/html";
\r
63 _sCacheControl = null;
\r
65 _lContentLength = 0;
\r
66 _bSuppressContent = false;
\r
67 _bSuppressHeaders = false;
\r
68 _bClientDisconnected = false;
\r
72 _TextWriter = output;
\r
75 internal HttpResponse(HttpWorkerRequest WorkerRequest, HttpContext Context) {
\r
77 _WorkerRequest = WorkerRequest;
\r
81 _bHeadersSent = false;
\r
83 _Headers = new ArrayList();
\r
85 _sContentType = "text/html";
\r
89 _sCacheControl = null;
\r
91 _lContentLength = 0;
\r
92 _bSuppressContent = false;
\r
93 _bSuppressHeaders = false;
\r
94 _bClientDisconnected = false;
\r
98 _Writer = new HttpWriter(this);
\r
99 _TextWriter = _Writer;
\r
102 internal System.Text.Encoder ContentEncoder {
\r
104 return ContentEncoding.GetEncoder();
\r
108 internal void FinalFlush() {
\r
112 internal void DoFilter() {
\r
113 if (null != _Writer)
\r
114 _Writer.FilterData(true);
\r
117 [MonoTODO("We need to add cache headers also")]
\r
118 private ArrayList GenerateHeaders() {
\r
119 ArrayList oHeaders = new ArrayList(_Headers.ToArray());
\r
121 // save culture info, we need us info here
\r
122 CultureInfo oSavedInfo = System.Threading.Thread.CurrentThread.CurrentCulture;
\r
123 System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo(0x0409);
\r
125 oHeaders.Add(new HttpResponseHeader("Date", DateTime.Now.ToUniversalTime().ToString("ddd, d MMM yyyy HH:mm:ss") + " GMT"));
\r
127 System.Threading.Thread.CurrentThread.CurrentCulture = oSavedInfo;
\r
129 if (_lContentLength > 0) {
\r
130 oHeaders.Add(new HttpResponseHeader(HttpWorkerRequest.HeaderContentLength, _lContentLength.ToString()));
\r
133 if (_sContentType != null) {
\r
134 if (_sContentType.IndexOf("charset=") == -1) {
\r
135 if (Charset.Length == 0) {
\r
136 Charset = ContentEncoding.HeaderName;
\r
139 // Time to build our string
\r
140 if (Charset.Length > 0) {
\r
141 _sContentType += "; charset=" + Charset;
\r
145 oHeaders.Add(new HttpResponseHeader(HttpWorkerRequest.HeaderContentType, _sContentType));
\r
148 if (_sCacheControl != null) {
\r
149 oHeaders.Add(new HttpResponseHeader(HttpWorkerRequest.HeaderPragma, _sCacheControl));
\r
152 if (_sTransferEncoding != null) {
\r
153 oHeaders.Add(new HttpResponseHeader(HttpWorkerRequest.HeaderTransferEncoding, _sTransferEncoding));
\r
156 // TODO: Add Cookie headers..
\r
161 private void SendHeaders() {
\r
162 _WorkerRequest.SendStatus(StatusCode, StatusDescription);
\r
164 ArrayList oHeaders = GenerateHeaders();
\r
165 foreach (object oHeader in oHeaders) {
\r
166 ((HttpResponseHeader) oHeader).SendContent(_WorkerRequest);
\r
169 _bHeadersSent = true;
\r
172 public string Status {
\r
174 return StatusCode.ToString() + " " + StatusDescription;
\r
178 string sMsg = "OK";
\r
182 iCode = Int32.Parse(value.Substring(0, value.IndexOf(' ')));
\r
183 sMsg = value.Substring(value.IndexOf(' ') + 1);
\r
186 throw new HttpException("Invalid status string");
\r
189 StatusCode = iCode;
\r
190 StatusDescription = sMsg;
\r
195 public void AddCacheItemDependencies(ArrayList cacheKeys) {
\r
196 throw new NotImplementedException();
\r
200 public void AddCacheItemDependency(string cacheKey) {
\r
201 throw new NotImplementedException();
\r
205 public void AddFileDependencies(ArrayList filenames) {
\r
206 //throw new NotImplementedException();
\r
210 public void AddFileDependency(string filename) {
\r
211 //throw new NotImplementedException();
\r
214 public void AddHeader(string name, string value) {
\r
215 AppendHeader(name, value);
\r
219 public void AppendCookie(HttpCookie cookie) {
\r
220 throw new NotImplementedException();
\r
224 public void AppendToLog(string param) {
\r
225 throw new NotImplementedException();
\r
229 public string ApplyAppPathModifier(string virtualPath) {
\r
230 throw new NotImplementedException();
\r
233 public bool Buffer {
\r
235 return BufferOutput;
\r
239 BufferOutput = value;
\r
243 public bool BufferOutput {
\r
245 return _bBuffering;
\r
249 if (_Writer != null) {
\r
253 _bBuffering = value;
\r
257 public HttpCachePolicy Cache {
\r
259 if (null == _CachePolicy) {
\r
260 _CachePolicy = new HttpCachePolicy();
\r
263 return _CachePolicy;
\r
267 [MonoTODO("Set status in the cache policy")]
\r
268 public string CacheControl {
\r
270 return _sCacheControl;
\r
274 if (_bHeadersSent) {
\r
275 throw new System.Web.HttpException("Headers has been sent to the client");
\r
278 _sCacheControl = value;
\r
282 public string Charset {
\r
284 if (null == _sCharset) {
\r
285 _sCharset = ContentEncoding.WebName;
\r
291 if (_bHeadersSent) {
\r
292 throw new System.Web.HttpException("Headers has been sent to the client");
\r
299 public Encoding ContentEncoding {
\r
301 if (_ContentEncoding == null) {
\r
302 _ContentEncoding = Encoding.UTF8;
\r
305 return _ContentEncoding;
\r
309 if (value == null) {
\r
310 throw new ArgumentException("Can't set a null as encoding");
\r
313 _ContentEncoding = value;
\r
315 if (_Writer != null) {
\r
321 public string ContentType {
\r
323 return _sContentType;
\r
327 if (_bHeadersSent) {
\r
328 throw new System.Web.HttpException("Headers has been sent to the client");
\r
331 _sContentType = value;
\r
335 public HttpCookieCollection Cookies {
\r
337 if (null == _Cookies) {
\r
338 _Cookies = new HttpCookieCollection(this, false);
\r
344 [MonoTODO("Set expires in the cache policy")]
\r
345 public int Expires {
\r
347 throw new NotImplementedException();
\r
351 throw new NotImplementedException();
\r
355 [MonoTODO("Set expiresabsolute in the cache policy")]
\r
356 public DateTime ExpiresAbsolute {
\r
358 throw new NotImplementedException();
\r
362 throw new NotImplementedException();
\r
366 public Stream Filter {
\r
368 if (_Writer != null) {
\r
369 return _Writer.GetActiveFilter();
\r
375 if (_Writer == null) {
\r
376 throw new HttpException("Filtering is not allowed");
\r
379 _Writer.ActivateFilter(value);
\r
383 public bool IsClientConnected {
\r
385 if (_ClientDisconnected) {
\r
389 if (null != _WorkerRequest && (!_WorkerRequest.IsClientConnected())) {
\r
390 _ClientDisconnected = false;
\r
398 public TextWriter Output {
\r
400 return _TextWriter;
\r
404 public Stream OutputStream {
\r
406 if (_Writer == null) {
\r
407 throw new System.Web.HttpException("a Output stream not available when running with custom text writer");
\r
410 return _Writer.OutputStream;
\r
414 public string StatusDescription {
\r
416 if (null == _sStatusDescription) {
\r
417 _sStatusDescription = HttpWorkerRequest.GetStatusDescription(_iStatusCode);
\r
420 return _sStatusDescription;
\r
424 if (_bHeadersSent) {
\r
425 throw new System.Web.HttpException("Headers has been sent to the client");
\r
428 _sStatusDescription = value;
\r
432 public int StatusCode {
\r
434 return _iStatusCode;
\r
437 if (_bHeadersSent) {
\r
438 throw new System.Web.HttpException("Headers has been sent to the client");
\r
441 _sStatusDescription = null;
\r
442 _iStatusCode = value;
\r
446 public bool SuppressContent {
\r
448 return _bSuppressContent;
\r
452 if (_bHeadersSent) {
\r
453 throw new System.Web.HttpException("Headers has been sent to the client");
\r
456 _bSuppressContent = true;
\r
460 public HttpRequest Request {
\r
462 return _Context.Request;
\r
466 internal void AppendHeader(int iIndex, string value) {
\r
467 if (_bHeadersSent) {
\r
468 throw new System.Web.HttpException("Headers has been sent to the client");
\r
471 case HttpWorkerRequest.HeaderContentLength :
\r
472 _lContentLength = Int64.Parse(value);
\r
474 case HttpWorkerRequest.HeaderContentEncoding :
\r
475 _sContentType = value;
\r
477 case HttpWorkerRequest.HeaderTransferEncoding :
\r
478 _sTransferEncoding = value;
\r
479 if (value.Equals("chunked")) {
\r
486 case HttpWorkerRequest.HeaderPragma :
\r
487 _sCacheControl = value;
\r
491 _Headers.Add(new HttpResponseHeader(iIndex, value));
\r
498 public void AppendHeader(string name, string value) {
\r
499 if (_bHeadersSent) {
\r
500 throw new System.Web.HttpException("Headers has been sent to the client");
\r
503 switch (name.ToLower()) {
\r
504 case "content-length" : _lContentLength = Int64.Parse(value);
\r
506 case "content-type" : _sContentType = value;
\r
508 case "transfer-encoding" : _sTransferEncoding = value;
\r
509 if (value.Equals("chunked")) {
\r
516 case "pragma" : _sCacheControl = value;
\r
519 default : _Headers.Add(new HttpResponseHeader(name, value));
\r
524 public void BinaryWrite(byte [] buffer) {
\r
525 OutputStream.Write(buffer, 0, buffer.Length);
\r
528 public void Clear() {
\r
529 if (_Writer != null) {
\r
534 public void ClearContent() {
\r
538 public void ClearHeaders() {
\r
539 if (_bHeadersSent) {
\r
540 throw new System.Web.HttpException("Headers has been sent to the client");
\r
543 _sContentType = "text/html";
\r
545 _iStatusCode = 200;
\r
547 _Headers = new ArrayList();
\r
548 _sCacheControl = null;
\r
549 _sTransferEncoding = null;
\r
551 _lContentLength = 0;
\r
552 _bSuppressContent = false;
\r
553 _bSuppressHeaders = false;
\r
554 _bClientDisconnected = false;
\r
557 public void Close() {
\r
558 _bClientDisconnected = false;
\r
560 _WorkerRequest.CloseConnection();
\r
562 _bClientDisconnected = true;
\r
565 internal void Dispose() {
\r
566 if (_Writer != null) {
\r
572 [MonoTODO("Handle callbacks into before done with session, needs to have a non ended flush here")]
\r
573 internal void FlushAtEndOfRequest()
\r
578 [MonoTODO("Check timeout and if we can cancel the thread...")]
\r
579 public void End() {
\r
582 _WorkerRequest.CloseConnection();
\r
588 public void Flush() {
\r
592 private void Flush(bool bFinish) {
\r
599 if (_Writer != null) {
\r
600 _Writer.FlushBuffers();
\r
602 _TextWriter.Flush();
\r
606 if (!_bHeadersSent) {
\r
607 if (!_bSuppressHeaders && !_bClientDisconnected) {
\r
608 if (_Writer != null && BufferOutput) {
\r
609 _lContentLength = _Writer.BufferSize;
\r
612 _lContentLength = 0;
\r
615 if (_lContentLength == 0 && _iStatusCode == 200 && _sTransferEncoding == null) {
\r
616 // Check we are going todo chunked encoding
\r
617 string sProto = _Context.Request.ServerVariables["SERVER_PROTOCOL"];
\r
618 if (sProto != null && sProto == "HTTP/1.1") {
\r
619 AppendHeader(HttpWorkerRequest.HeaderTransferEncoding, "chunked");
\r
622 // Just in case, the old browsers sends a HTTP/1.0 request with Connection: Keep-Alive
\r
623 AppendHeader(HttpWorkerRequest.HeaderConnection, "Close");
\r
630 if ((!_bSuppressContent && Request.HttpMethod.Equals("HEAD")) || _Writer == null) {
\r
631 _bSuppressContent = true;
\r
634 if (!_bSuppressContent) {
\r
635 _bClientDisconnected = false;
\r
637 Encoding oASCII = Encoding.ASCII;
\r
639 byte [] arrPrefix = oASCII.GetBytes(Convert.ToString(_Writer.BufferSize, 16) + s_sChunkedPrefix);
\r
640 _WorkerRequest.SendResponseFromMemory(arrPrefix, arrPrefix.Length);
\r
642 _Writer.SendContent(_WorkerRequest);
\r
644 _WorkerRequest.SendResponseFromMemory(s_arrChunkSuffix, s_arrChunkSuffix.Length);
\r
646 _WorkerRequest.SendResponseFromMemory(s_arrChunkEnd, s_arrChunkEnd.Length);
\r
650 _Writer.SendContent(_WorkerRequest);
\r
653 _WorkerRequest.FlushResponse(bFinish);
\r
661 _bFlushing = false;
\r
665 public void Pics(string value) {
\r
666 AppendHeader("PICS-Label", value);
\r
670 public void Redirect(string url) {
\r
671 Redirect(url, true);
\r
674 //FIXME: [1] this is an ugly hack to make it work until we have SimpleWorkerRequest!
\r
675 private string redirectLocation;
\r
676 public string RedirectLocation
\r
679 return redirectLocation;
\r
682 public void Redirect(string url, bool endResponse) {
\r
683 if (_bHeadersSent) {
\r
684 throw new System.Web.HttpException("Headers has been sent to the client");
\r
690 redirectLocation = url;
\r
691 //[1]AppendHeader(HttpWorkerRequest.HeaderLocation, url);
\r
693 // Text for browsers that can't handle location header
\r
694 Write("<html><head><title>Object moved</title></head><body>\r\n");
\r
695 Write("<h2>Object moved to <a href='" + url + "'>here</a></h2>\r\n");
\r
696 Write("</body><html>\r\n");
\r
705 public void Write(char ch) {
\r
706 _TextWriter.Write(ch);
\r
709 public void Write(object obj) {
\r
710 _TextWriter.Write(obj);
\r
713 public void Write(string str) {
\r
714 _TextWriter.Write(str);
\r
717 public void Write(char [] buffer, int index, int count) {
\r
718 _TextWriter.Write(buffer, index, count);
\r
722 public static void RemoveOutputCacheItem(string path) {
\r
723 throw new NotImplementedException();
\r
727 public void SetCookie(HttpCookie cookie) {
\r
728 throw new NotImplementedException();
\r
731 public void WriteFile(string filename) {
\r
732 WriteFile(filename, false);
\r
736 public void WriteFile(string filename, bool readIntoMemory) {
\r
737 throw new NotImplementedException();
\r
741 public void WriteFile(string filename, long offset, long size) {
\r
742 throw new NotImplementedException();
\r
745 [MonoTODO("Should we support fileHandle ptrs?")]
\r
746 public void WriteFile(IntPtr fileHandle, long offset, long size) {
\r
750 internal void OnCookieAdd(HttpCookie cookie) {
\r
753 [MonoTODO("Do we need this?")]
\r
754 internal void OnCookieChange(HttpCookie cookie) {
\r
758 internal void GoingToChangeCookieColl() {
\r
762 internal void ChangedCookieColl() {
\r