1 //
2 // System.Web.HttpResponseTest.cs - Unit tests for System.Web.HttpResponse
3 //
4 // Author:
5 //	Miguel de Icaza  <miguel@ximian.com>
6 //
7 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 
29 using System;
30 using System.Collections;
31 using System.Collections.Specialized;
32 using System.Configuration.Provider;
33 using System.IO;
34 using System.Text;
35 using System.Web;
36 using System.Web.Routing;
37 using System.Web.Caching;
38 
39 using NUnit.Framework;
40 
41 namespace MonoTests.System.Web {
42 
43 	public class FakeHttpWorkerRequest2 : HttpWorkerRequest {
44 		public Hashtable KnownResponseHeaders;
45 		public Hashtable UnknownResponseHeaders;
46 		public int return_kind;
47 
FakeHttpWorkerRequest2(int re)48 		public FakeHttpWorkerRequest2 (int re)
49 		{
50 			KnownResponseHeaders = CollectionsUtil.CreateCaseInsensitiveHashtable();
51 			UnknownResponseHeaders = CollectionsUtil.CreateCaseInsensitiveHashtable();
52 			return_kind = re;
53 		}
54 
GetUriPath()55 		public override string GetUriPath()
56 		{
57 			return "/fake";
58 		}
59 
GetQueryString()60 		public override string GetQueryString()
61 		{
62 			return "GetQueryString";
63 		}
64 
GetRawUrl()65 		public override string GetRawUrl()
66 		{
67 			return "/GetRawUrl";
68 		}
69 
GetHttpVerbName()70 		public override string GetHttpVerbName()
71 		{
72 			return "GET";
73 		}
74 
GetHttpVersion()75 		public override string GetHttpVersion()
76 		{
77 			if (return_kind == 1)
78 				return "HTTP/1.0";
79 			else
80 				return "HTTP/1.1";
81 		}
82 
GetRemoteAddress()83 		public override string GetRemoteAddress()
84 		{
85 			return "__GetRemoteAddress";
86 			}
87 
GetRemotePort()88 		public override int GetRemotePort()
89 		{
90 			return 1010;
91 		}
92 
GetLocalAddress()93 		public override string GetLocalAddress()
94 		{
95 			return "GetLocalAddress";
96 		}
97 
GetAppPath()98 		public override string GetAppPath ()
99 		{
100 			return "AppPath";
101 		}
102 
GetLocalPort()103 		public override int GetLocalPort()
104 		{
105 			return 2020;
106 		}
107 
108 		public bool status_sent;
109 		public int status_code;
110 		public string status_string;
111 
SendStatus(int s, string x)112 		public override void SendStatus(int s, string x)
113 		{
114 			status_sent = true;
115 			status_code = s;
116 			status_string = x;
117 		}
118 
AddHeader(Hashtable table, string header_name, object header)119 		void AddHeader (Hashtable table, string header_name, object header)
120 		{
121 			object o = table [header_name];
122 			if (o == null)
123 				table.Add (header_name, header);
124 			else {
125 				ArrayList al = o as ArrayList;
126 				if (al == null) {
127 					al = new ArrayList ();
128 					al.Add (o);
129 					table [header_name] = al;
130 				} else
131 					al = o as ArrayList;
132 
133 				al.Add (header);
134 			}
135 		}
136 
137 		bool headers_sent;
SendKnownResponseHeader(int x, string j)138 		public override void SendKnownResponseHeader(int x, string j)
139 		{
140 			string header_name = HttpWorkerRequest.GetKnownRequestHeaderName (x);
141 			AddHeader (KnownResponseHeaders, header_name, new KnownResponseHeader (x, j));
142 			headers_sent = true;
143 		}
144 
SendUnknownResponseHeader(string a, string b)145 		public override void SendUnknownResponseHeader(string a, string b)
146 		{
147 			AddHeader (UnknownResponseHeaders, a, new UnknownResponseHeader (a, b));
148 			headers_sent = true;
149 		}
150 
151 		bool data_sent;
152 		public byte [] data;
153 		public int data_len;
154 		public int total = 0;
155 
SendResponseFromMemory(byte[] arr, int x)156 		public override void SendResponseFromMemory(byte[] arr, int x)
157 		{
158 			data_sent = true;
159 			if (data != null) {
160 				byte [] tmp = new byte [data.Length + x];
161 				Array.Copy (data, tmp, data.Length);
162 				Array.Copy (arr, 0, tmp, data.Length, x);
163 				data = tmp;
164 				data_len = data.Length;
165 			} else {
166 				data = new byte [x];
167 				for (int i = 0; i < x; i++)
168 					data [i] = arr [i];
169 				data_len = x;
170 			}
171 			total += x;
172 		}
173 
SendResponseFromFile(string a, long b , long c)174 		public override void SendResponseFromFile(string a, long b , long c)
175 		{
176 			data_sent = true;
177 		}
178 
SendResponseFromFile(IntPtr a, long b, long c)179 		public override void SendResponseFromFile (IntPtr a, long b, long c)
180 		{
181 			data_sent = true;
182 		}
183 
FlushResponse(bool x)184 		public override void FlushResponse(bool x)
185 		{
186 		}
187 
EndOfRequest()188 		public override void EndOfRequest() {
189 		}
190 
GetKnownRequestHeader(int index)191 		public override string GetKnownRequestHeader (int index)
192 		{
193 			return null;
194 		}
195 
196 		public bool OutputProduced {
197 			get {
198 				return headers_sent || data_sent;
199 			}
200 		}
201 	}
202 
203 	class KnownResponseHeader
204 	{
205 		private int index;
206 		private string value;
207 
KnownResponseHeader(int index, string value)208 		public KnownResponseHeader (int index, string value)
209 		{
210 			this.index = index;
211 			this.value = value;
212 		}
213 
214 		public int Index {
215 			get { return index; }
216 		}
217 
218 		public string Value {
219 			get { return value; }
220 		}
221 	}
222 
223 	class UnknownResponseHeader
224 	{
225 		private string name;
226 		private string value;
227 
UnknownResponseHeader(string name, string value)228 		public UnknownResponseHeader (string name, string value)
229 		{
230 			this.name = name;
231 			this.value = value;
232 		}
233 
234 		public string Name {
235 			get { return name; }
236 		}
237 
238 		public string Value {
239 			get { return value; }
240 		}
241 	}
242 
243 	[TestFixture]
244 	public class HttpResponseTest {
Cook(int re, out FakeHttpWorkerRequest2 f)245 		public static HttpContext Cook (int re, out FakeHttpWorkerRequest2 f)
246 		{
247 			f = new FakeHttpWorkerRequest2 (re);
248 			HttpContext c = new HttpContext (f);
249 
250 			return c;
251 		}
252 
253 		[SetUp]
SetUp()254 		public void SetUp ()
255 		{
256 			AppDomain.CurrentDomain.SetData (".appPath", AppDomain.CurrentDomain.BaseDirectory);
257 		}
258 
259 		[Test]
Test_Response()260 		public void Test_Response ()
261 		{
262 			FakeHttpWorkerRequest2 f;
263 			HttpContext c = Cook (1, out f);
264 
265 			c.Response.Write ("a");
266 			Assert.AreEqual (false, f.OutputProduced, "T1");
267 			c.Response.Flush ();
268 			Assert.AreEqual (1, f.data_len, "T2");
269 			c.Response.Write ("Hola");
270 			Assert.AreEqual (1, f.data_len, "T3");
271 			c.Response.Flush ();
272 			Assert.AreEqual (5, f.data_len, "T4");
273 			Assert.AreEqual ((byte) 'a', f.data [0], "T5");
274 			Assert.AreEqual ((byte) 'H', f.data [1], "T6");
275 			Assert.AreEqual ((byte) 'o', f.data [2], "T7");
276 			Assert.AreEqual ((byte) 'l', f.data [3], "T8");
277 			Assert.AreEqual ((byte) 'a', f.data [4], "T9");
278 		}
279 
280 		[Test]
TestResponse_Chunked()281 		public void TestResponse_Chunked ()
282 		{
283 			FakeHttpWorkerRequest2 f;
284 			HttpContext c = Cook (2, out f);
285 
286 			c.Response.Write ("a");
287 			Assert.AreEqual (false, f.OutputProduced, "CT1");
288 			c.Response.Flush ();
289 			Assert.AreEqual (6, f.total, "CT2");
290 			c.Response.Write ("Hola");
291 			Assert.AreEqual (6, f.total, "CT3");
292 			c.Response.Flush ();
293 
294 		}
295 
296 		[Test]
Status1()297 		public void Status1 ()
298 		{
299 			FakeHttpWorkerRequest2 f;
300 			HttpContext c = Cook (2, out f);
301 
302 			HttpResponse resp = c.Response;
303 			resp.Status = "200 Lalala";
304 			Assert.AreEqual (200, resp.StatusCode, "ST1");
305 			Assert.AreEqual ("Lalala", resp.StatusDescription, "ST2");
306 
307 			resp.Status = "10000 La la la";
308 			Assert.AreEqual (10000, resp.StatusCode, "ST3");
309 			Assert.AreEqual ("La la la", resp.StatusDescription, "ST4");
310 
311 			resp.Status = "-1 La la la";
312 			Assert.AreEqual (-1, resp.StatusCode, "ST5");
313 			Assert.AreEqual ("La la la", resp.StatusDescription, "ST6");
314 
315 			resp.Status = "-200 La la la";
316 			Assert.AreEqual (-200, resp.StatusCode, "ST7");
317 			Assert.AreEqual ("La la la", resp.StatusDescription, "ST8");
318 
319 			resp.Status = "200 ";
320 			Assert.AreEqual (200, resp.StatusCode, "ST7");
321 			Assert.AreEqual ("", resp.StatusDescription, "ST8");
322 		}
323 
324 		[Test]
Status2()325 		public void Status2 ()
326 		{
327 			FakeHttpWorkerRequest2 f;
328 			HttpContext c = Cook (2, out f);
329 
330 			HttpResponse resp = c.Response;
331 			try {
332 				resp.Status = "200";
333 				Assert.Fail ("#1");
334 			} catch (HttpException) {
335 			}
336 		}
337 
338 		[Test]
Status3()339 		public void Status3 ()
340 		{
341 			FakeHttpWorkerRequest2 f;
342 			HttpContext c = Cook (2, out f);
343 
344 			HttpResponse resp = c.Response;
345 			try {
346 				resp.Status = "200\t";
347 				Assert.Fail ("#1");
348 			} catch (HttpException) {
349 			}
350 		}
351 
352 		[Test]
Status4()353 		public void Status4 ()
354 		{
355 			FakeHttpWorkerRequest2 f;
356 			HttpContext c = Cook (2, out f);
357 
358 			HttpResponse resp = c.Response;
359 
360 			Assert.AreEqual (200, resp.StatusCode, "STT1");
361 			Assert.AreEqual (HttpWorkerRequest.GetStatusDescription (200), resp.StatusDescription, "STT2");
362 
363 			resp.StatusCode = 400;
364 			Assert.AreEqual (400, resp.StatusCode, "STT3");
365 			Assert.AreEqual (HttpWorkerRequest.GetStatusDescription (400), resp.StatusDescription, "STT4");
366 
367 			resp.StatusDescription = "Something else";
368 			Assert.AreEqual (400, resp.StatusCode, "STT5");
369 			Assert.AreEqual ("Something else", resp.StatusDescription, "STT6");
370 
371 			resp.StatusDescription = null;
372 			Assert.AreEqual (400, resp.StatusCode, "STT7");
373 			Assert.AreEqual (HttpWorkerRequest.GetStatusDescription (400), resp.StatusDescription, "STT8");
374 		}
375 
376 		//
377 		// TODO: Add test for BinaryWrite and the various writes to check for Chunked Mode
378 		//`
379 
380 		[Test]
SetCacheability()381 		public void SetCacheability ()
382 		{
383 			FakeHttpWorkerRequest2 f;
384 			HttpContext c = Cook (1, out f);
385 
386 			//
387 			// Basically the values from CacheControl are useless once Response.Cache is used
388 			//
389 			c.Response.Cache.SetCacheability (HttpCacheability.ServerAndNoCache);
390 			Assert.AreEqual ("private", c.Response.CacheControl, "C1");
391 
392 			c.Response.Cache.SetCacheability (HttpCacheability.ServerAndPrivate);
393 			Assert.AreEqual ("private", c.Response.CacheControl, "C2");
394 
395 			c.Response.Cache.SetCacheability (HttpCacheability.NoCache);
396 			Assert.AreEqual ("private", c.Response.CacheControl, "C3");
397 
398 			c.Response.Cache.SetCacheability (HttpCacheability.Private);
399 			Assert.AreEqual ("private", c.Response.CacheControl, "C4");
400 
401 			c.Response.Cache.SetCacheability (HttpCacheability.Server);
402 			Assert.AreEqual ("private", c.Response.CacheControl, "C5");
403 
404 			c.Response.Cache.SetCacheability (HttpCacheability.Public);
405 			Assert.AreEqual ("private", c.Response.CacheControl, "C6");
406 		}
407 
408 		//
409 		// Test the values allowed;  .NET only documents private and public, but
410 		// "no-cache" from the spec is also allowed
411 		//
412 		[Test]
CacheControl()413 		public void CacheControl ()
414 		{
415 			FakeHttpWorkerRequest2 f;
416 			HttpContext c = Cook (1, out f);
417 
418 			// Default value.
419 			Assert.AreEqual ("private", c.Response.CacheControl, "D1");
420 
421 			c.Response.CacheControl = "private";
422 			Assert.AreEqual ("private", c.Response.CacheControl, "D2");
423 
424 			c.Response.CacheControl = "public";
425 			Assert.AreEqual ("public", c.Response.CacheControl, "D3");
426 
427 			c.Response.CacheControl = "no-cache";
428 			Assert.AreEqual ("no-cache", c.Response.CacheControl, "D4");
429 
430 			c.Response.CacheControl = null;
431 			Assert.AreEqual ("private", c.Response.CacheControl, "D5");
432 
433 			c.Response.CacheControl = "";
434 			Assert.AreEqual ("private", c.Response.CacheControl, "D6");
435 		}
436 
437 		//
438 		// Just checks if the AddFileDepend* methods accept values, added after bug #342511
439 		[Test]
AddFileDependencies()440 		public void AddFileDependencies ()
441 		{
442 			FakeHttpWorkerRequest2 f;
443 			HttpContext c = Cook (1, out f);
444 
445 			ArrayList a = new ArrayList (1);
446 			a.Add ("somefile.txt");
447 			c.Response.AddFileDependencies (a);
448 
449 			string[] sa = new string [1] {"somefile.txt"};
450 			c = Cook (1, out f);
451 			c.Response.AddFileDependencies (sa);
452 
453 			c = Cook (1, out f);
454 			c.Response.AddFileDependency ("somefile.txt");
455 		}
456 
457 		[Test] // bug #488702
WriteHeaders()458 		public void WriteHeaders ()
459 		{
460 			FakeHttpWorkerRequest2 f;
461 			HttpContext c = Cook (2, out f);
462 
463 			HttpResponse resp = c.Response;
464 			resp.CacheControl = "public";
465 			resp.Cache.SetCacheability (HttpCacheability.NoCache);
466 			resp.ContentType = "text/xml";
467 			resp.AppendHeader ("Content-Disposition", "inline");
468 			resp.AppendHeader ("Content-Type", "application/ms-word");
469 			resp.AppendHeader ("Content-Length", "40");
470 			resp.AppendHeader ("Transfer-Encoding", "compress");
471 			resp.AppendHeader ("My-Custom-Header", "never");
472 			resp.AppendHeader ("My-Custom-Header", "always");
473 
474 			Assert.AreEqual ("public", resp.CacheControl, "#A1");
475 			Assert.AreEqual ("application/ms-word", resp.ContentType, "#A2");
476 			Assert.AreEqual (0, f.KnownResponseHeaders.Count, "#A3");
477 			Assert.AreEqual (0, f.UnknownResponseHeaders.Count, "#A4");
478 
479 			resp.Flush ();
480 
481 			KnownResponseHeader known;
482 
483 			Assert.AreEqual (6, f.KnownResponseHeaders.Count, "#B1");
484 
485 			known = (KnownResponseHeader)f.KnownResponseHeaders ["Content-Length"];
486 			Assert.AreEqual (HttpWorkerRequest.HeaderContentLength, known.Index, "#B2");
487 			Assert.AreEqual ("40", known.Value, "#B3");
488 
489 			known = (KnownResponseHeader)f.KnownResponseHeaders["Transfer-Encoding"];
490 			Assert.AreEqual (HttpWorkerRequest.HeaderTransferEncoding, known.Index, "#B4");
491 			Assert.AreEqual ("compress", known.Value, "#B5");
492 
493 			known = (KnownResponseHeader)f.KnownResponseHeaders["Cache-Control"];
494 			Assert.AreEqual (HttpWorkerRequest.HeaderCacheControl, known.Index, "#B6");
495 			Assert.AreEqual ("no-cache", known.Value, "#B7");
496 
497 			known = (KnownResponseHeader)f.KnownResponseHeaders["Pragma"];
498 			Assert.AreEqual (HttpWorkerRequest.HeaderPragma, known.Index, "#B8");
499 			Assert.AreEqual ("no-cache", known.Value, "#B9");
500 
501 			known = (KnownResponseHeader)f.KnownResponseHeaders["Expires"];
502 			Assert.AreEqual (HttpWorkerRequest.HeaderExpires, known.Index, "#B10");
503 			Assert.AreEqual ("-1", known.Value, "#B11");
504 
505 			known = (KnownResponseHeader)f.KnownResponseHeaders["Content-Type"];
506 			Assert.AreEqual (HttpWorkerRequest.HeaderContentType, known.Index, "#B12");
507 			Assert.AreEqual ("application/ms-word", known.Value, "#B13");
508 
509 			UnknownResponseHeader unknown;
510 
511 			Assert.AreEqual (3, f.UnknownResponseHeaders.Count, "#C1");
512 
513 			unknown = (UnknownResponseHeader) f.UnknownResponseHeaders ["X-AspNet-Version"];
514 			Assert.AreEqual ("X-AspNet-Version", unknown.Name, "#C2");
515 			Assert.AreEqual (Environment.Version.ToString (3), unknown.Value, "#C3");
516 
517 			unknown = (UnknownResponseHeader) f.UnknownResponseHeaders ["Content-Disposition"];
518 			Assert.AreEqual ("Content-Disposition", unknown.Name, "#C4");
519 			Assert.AreEqual ("inline", unknown.Value, "#C5");
520 
521 			ArrayList al = f.UnknownResponseHeaders ["My-Custom-Header"] as ArrayList;
522 			Assert.AreEqual (2, al.Count, "#C6");
523 
524 			unknown = (UnknownResponseHeader) al [0];
525 			Assert.AreEqual ("My-Custom-Header", unknown.Name, "#C7");
526 			Assert.AreEqual ("never", unknown.Value, "#C8");
527 
528 			unknown = (UnknownResponseHeader) al [1];
529 			Assert.AreEqual ("My-Custom-Header", unknown.Name, "#C9");
530 			Assert.AreEqual ("always", unknown.Value, "#C10");
531 		}
532 
533 		[Test] // pull #866
WriteHeadersNoCharset()534 		public void WriteHeadersNoCharset ()
535 		{
536 			FakeHttpWorkerRequest2 f;
537 			HttpContext c = Cook (2, out f);
538 
539 			HttpResponse resp = c.Response;
540 			resp.ContentType = "text/plain";
541 
542 			Assert.AreEqual ("text/plain", resp.ContentType, "#A1");
543 
544 			resp.Flush ();
545 
546 			KnownResponseHeader known;
547 
548 			AssertHelper.LessOrEqual (1, f.KnownResponseHeaders.Count, "#B1");
549 
550 			known = (KnownResponseHeader)f.KnownResponseHeaders ["Content-Type"];
551 			Assert.AreEqual (HttpWorkerRequest.HeaderContentType, known.Index, "#B2");
552 			Assert.AreEqual ("text/plain", known.Value, "#B3");
553 		}
554 
555 		[Test] // pull #866
WriteHeadersHasCharset()556 		public void WriteHeadersHasCharset ()
557 		{
558 			FakeHttpWorkerRequest2 f;
559 			HttpContext c = Cook (2, out f);
560 
561 			HttpResponse resp = c.Response;
562 			resp.ContentType = "text/plain";
563 			resp.Charset = "big5";
564 
565 			Assert.AreEqual ("text/plain", resp.ContentType, "#A1");
566 			Assert.AreEqual ("big5", resp.Charset, "#A2");
567 
568 			resp.Flush ();
569 
570 			KnownResponseHeader known;
571 
572 			AssertHelper.LessOrEqual (1, f.KnownResponseHeaders.Count, "#B1");
573 
574 			known = (KnownResponseHeader)f.KnownResponseHeaders ["Content-Type"];
575 			Assert.AreEqual (HttpWorkerRequest.HeaderContentType, known.Index, "#B2");
576 			Assert.AreEqual ("text/plain; charset=big5", known.Value, "#B3");
577 		}
578 
579 		[Test] // bug #485557
580 		[Category ("NotWorking")] // bug #488702
ClearHeaders()581 		public void ClearHeaders ()
582 		{
583 			FakeHttpWorkerRequest2 f;
584 			HttpContext c = Cook (2, out f);
585 
586 			HttpResponse resp = c.Response;
587 			resp.CacheControl = "public";
588 			resp.Cache.SetCacheability (HttpCacheability.NoCache);
589 			resp.ContentType = "text/xml";
590 			resp.AppendHeader ("Content-Disposition", "inline");
591 			resp.AppendHeader ("Content-Type", "application/ms-word");
592 			resp.AppendHeader ("Content-Length", "40");
593 			resp.AppendHeader ("Transfer-Encoding", "compress");
594 			resp.AppendHeader ("My-Custom-Header", "never");
595 			resp.AppendHeader ("My-Custom-Header", "always");
596 			resp.ClearHeaders ();
597 
598 			Assert.AreEqual ("private", resp.CacheControl, "#A1");
599 			Assert.AreEqual ("text/html", resp.ContentType, "#A2");
600 			Assert.AreEqual (0, f.KnownResponseHeaders.Count, "#A3");
601 			Assert.AreEqual (0, f.UnknownResponseHeaders.Count, "#A4");
602 
603 			resp.Flush ();
604 
605 			KnownResponseHeader known;
606 
607 			Assert.AreEqual (3, f.KnownResponseHeaders.Count, "#B1");
608 
609 			known = (KnownResponseHeader) f.KnownResponseHeaders ["Transfer-Encoding"];
610 			Assert.AreEqual (HttpWorkerRequest.HeaderTransferEncoding, known.Index, "#B2");
611 			Assert.AreEqual ("chunked", known.Value, "#B3");
612 
613 			known = (KnownResponseHeader) f.KnownResponseHeaders ["Cache-Control"];
614 			Assert.AreEqual (HttpWorkerRequest.HeaderCacheControl, known.Index, "#B4");
615 			Assert.AreEqual ("private", known.Value, "#B5");
616 
617 			known = (KnownResponseHeader) f.KnownResponseHeaders ["Content-Type"];
618 			Assert.AreEqual (HttpWorkerRequest.HeaderContentType, known.Index, "#B6");
619 			Assert.AreEqual ("text/html", known.Value, "#B7");
620 
621 			Assert.AreEqual (1, f.UnknownResponseHeaders.Count, "#C1");
622 			UnknownResponseHeader unknown = (UnknownResponseHeader) f.UnknownResponseHeaders ["X-AspNet-Version"];
623 			Assert.AreEqual ("X-AspNet-Version", unknown.Name, "#C2");
624 			Assert.AreEqual (Environment.Version.ToString (3), unknown.Value, "#C3");
625 		}
626 
627 		[Test]
Constructor()628 		public void Constructor ()
629 		{
630 			var resp = new HttpResponse (null);
631 			Assert.IsNull (resp.Output, "#A1");
632 		}
633 		[Test]
RedirectPermanent()634 		public void RedirectPermanent ()
635 		{
636 			FakeHttpWorkerRequest2 request;
637 			HttpContext context = Cook (1, out request);
638 			Assert.Throws<ArgumentNullException> (() => {
639 				context.Response.RedirectPermanent (null);
640 			}, "#A1");
641 
642 			Assert.Throws<ArgumentException> (() => {
643 				context.Response.RedirectPermanent ("http://invalid\nurl.com");
644 			}, "#A2");
645 
646 			Assert.Throws<ArgumentNullException> (() => {
647 				context.Response.RedirectPermanent (null, true);
648 			}, "#A3");
649 
650 			Assert.Throws<ArgumentException> (() => {
651 				context.Response.RedirectPermanent ("http://invalid\nurl.com", true);
652 			}, "#A4");
653 		}
654 
655 		[Test]
RedirectToRoute()656 		public void RedirectToRoute ()
657 		{
658 			var resp = new HttpResponse (new StringWriter ());
659 			// Ho, ho, ho!
660 			Assert.Throws<NullReferenceException> (() => {
661 				resp.RedirectToRoute ("SomeRoute");
662 			}, "#A1");
663 
664 			FakeHttpWorkerRequest2 request;
665 			HttpContext context = Cook (1, out request);
666 
667 			// From RouteCollection.GetVirtualPath
668 			Assert.Throws<ArgumentException> (() => {
669 				context.Response.RedirectToRoute ("SomeRoute");
670 			}, "#A2");
671 
672 			Assert.Throws<InvalidOperationException> (() => {
673 				context.Response.RedirectToRoute (new { productId = "1", category = "widgets" });
674 			}, "#A3");
675 		}
676 
677 		[Test]
RemoveOutputCacheItem()678 		public void RemoveOutputCacheItem ()
679 		{
680 			Assert.Throws<ArgumentNullException> (() => {
681 				HttpResponse.RemoveOutputCacheItem (null, "MyProvider");
682 			}, "#A1");
683 
684 			Assert.Throws<ArgumentException> (() => {
685 				HttpResponse.RemoveOutputCacheItem ("badPath", null);
686 			}, "#A2");
687 
688 			Assert.IsNull (OutputCache.Providers, "#A3");
689 			HttpResponse.RemoveOutputCacheItem ("/Path", null);
690 
691 			Assert.Throws<ProviderException> (() => {
692 				HttpResponse.RemoveOutputCacheItem ("/Path", String.Empty);
693 			}, "#A3");
694 
695 			Assert.Throws<ProviderException> (() => {
696 				HttpResponse.RemoveOutputCacheItem ("/Path", "MyProvider");
697 			}, "#A4");
698 		}
699 
700 		[Test]
OutputSetter()701 		public void OutputSetter ()
702 		{
703 			FakeHttpWorkerRequest2 request;
704 			HttpContext context = Cook (1, out request);
705 
706 			Assert.IsNotNull (context.Response.Output, "#A1");
707 			context.Response.Output = null;
708 			Assert.IsNull (context.Response.Output, "#A2");
709 
710 			// Classy...
711 			Assert.Throws<NullReferenceException> (() => {
712 				context.Response.Write ('t');
713 			}, "#A3-1");
714 
715 			Assert.Throws<NullReferenceException> (() => {
716 				context.Response.Write ((object) 5);
717 			}, "#A3-2");
718 
719 			Assert.Throws<NullReferenceException> (() => {
720 				context.Response.Write ("string");
721 			}, "#A3-3");
722 
723 			Assert.Throws<NullReferenceException> (() => {
724 				context.Response.Write (new char [] { '1' }, 0, 1);
725 			}, "#A3-4");
726 
727 			Assert.Throws<NullReferenceException> (() => {
728 				context.Response.Write ((object) null);
729 			}, "#A3-5");
730 		}
731 	}
732 
733 	[TestFixture]
734 	public class HttpResponseOutputStreamTest
735 	{
736 		FakeHttpWorkerRequest2 worker;
737 		HttpContext context;
738 		HttpResponse response;
739 		Stream out_stream;
740 
741 		[SetUp]
Setup()742 		public void Setup ()
743 		{
744 			context = Cook (2, out worker);
745 			response = context.Response;
746 			out_stream = response.OutputStream;
747 		}
748 
749 		[TearDown]
TearDown()750 		public void TearDown ()
751 		{
752 			if (response != null)
753 				response.Close ();
754 		}
755 
756 		[Test]
CanRead()757 		public void CanRead ()
758 		{
759 			Assert.IsFalse (out_stream.CanRead, "#1");
760 			out_stream.Close ();
761 			Assert.IsFalse (out_stream.CanRead, "#2");
762 		}
763 
764 		[Test]
CanSeek()765 		public void CanSeek ()
766 		{
767 			Assert.IsFalse (out_stream.CanSeek, "#1");
768 			out_stream.Close ();
769 			Assert.IsFalse (out_stream.CanSeek, "#2");
770 		}
771 
772 		[Test]
CanWrite()773 		public void CanWrite ()
774 		{
775 			Assert.IsTrue (out_stream.CanWrite, "#1");
776 			out_stream.Close ();
777 			Assert.IsTrue (out_stream.CanWrite, "#2");
778 		}
779 
780 		[Test]
Flush()781 		public void Flush ()
782 		{
783 			byte [] buffer = Encoding.UTF8.GetBytes ("mono");
784 			out_stream.Write (buffer, 0, buffer.Length);
785 			out_stream.Flush ();
786 			Assert.AreEqual (0, worker.data_len);
787 		}
788 
789 		[Test]
Length()790 		public void Length ()
791 		{
792 			try {
793 				long len = out_stream.Length;
794 				Assert.Fail ("#1:" + len);
795 			} catch (NotSupportedException ex) {
796 				Assert.AreEqual (typeof (NotSupportedException), ex.GetType (), "#2");
797 				Assert.IsNull (ex.InnerException, "#3");
798 				Assert.IsNotNull (ex.Message, "#4");
799 			}
800 		}
801 
802 		[Test]
Position()803 		public void Position ()
804 		{
805 			try {
806 				long pos = out_stream.Position;
807 				Assert.Fail ("#A1:" + pos);
808 			} catch (NotSupportedException ex) {
809 				Assert.AreEqual (typeof (NotSupportedException), ex.GetType (), "#A2");
810 				Assert.IsNull (ex.InnerException, "#A3");
811 				Assert.IsNotNull (ex.Message, "#A4");
812 			}
813 
814 			try {
815 				out_stream.Position = 0;
816 				Assert.Fail ("#B1");
817 			} catch (NotSupportedException ex) {
818 				Assert.AreEqual (typeof (NotSupportedException), ex.GetType (), "#B2");
819 				Assert.IsNull (ex.InnerException, "#B3");
820 				Assert.IsNotNull (ex.Message, "#B4");
821 			}
822 		}
823 
824 		[Test]
Read()825 		public void Read ()
826 		{
827 			byte [] buffer = new byte [5];
828 
829 			try {
830 				out_stream.Read (buffer, 0, buffer.Length);
831 				Assert.Fail ("#1");
832 			} catch (NotSupportedException ex) {
833 				Assert.AreEqual (typeof (NotSupportedException), ex.GetType (), "#2");
834 				Assert.IsNull (ex.InnerException, "#3");
835 				Assert.IsNotNull (ex.Message, "#4");
836 			}
837 		}
838 
839 		[Test]
Seek()840 		public void Seek ()
841 		{
842 			try {
843 				out_stream.Seek (5, SeekOrigin.Begin);
844 				Assert.Fail ("#1");
845 			} catch (NotSupportedException ex) {
846 				Assert.AreEqual (typeof (NotSupportedException), ex.GetType (), "#2");
847 				Assert.IsNull (ex.InnerException, "#3");
848 				Assert.IsNotNull (ex.Message, "#4");
849 			}
850 		}
851 
852 		[Test]
SetLength()853 		public void SetLength ()
854 		{
855 			try {
856 				out_stream.SetLength (5L);
857 				Assert.Fail ("#1");
858 			} catch (NotSupportedException ex) {
859 				Assert.AreEqual (typeof (NotSupportedException), ex.GetType (), "#2");
860 				Assert.IsNull (ex.InnerException, "#3");
861 				Assert.IsNotNull (ex.Message, "#4");
862 			}
863 		}
864 
865 		[Test]
Write()866 		public void Write ()
867 		{
868 			byte [] buffer;
869 
870 			buffer = Encoding.UTF8.GetBytes ("mono");
871 			out_stream.Write (buffer, 0, buffer.Length);
872 			buffer = Encoding.UTF8.GetBytes ("just rocks!!");
873 			out_stream.Write (buffer, 5, 6);
874 			out_stream.Write (buffer, 0, 4);
875 			Assert.IsFalse (worker.OutputProduced, "#1");
876 			response.Flush ();
877 			Assert.IsTrue (worker.OutputProduced, "#2");
878 
879 			string output = Encoding.UTF8.GetString (worker.data);
880 			Assert.AreEqual ("e\r\nmonorocks!just\r\n", output);
881 		}
882 
883 		[Test]
Write_Buffer_Null()884 		public void Write_Buffer_Null ()
885 		{
886 			try {
887 				out_stream.Write ((byte []) null, 0, 0);
888 				Assert.Fail ("#1");
889 			} catch (ArgumentNullException ex) {
890 				Assert.AreEqual (typeof (ArgumentNullException), ex.GetType (), "#2");
891 				Assert.IsNull (ex.InnerException, "#3");
892 				Assert.IsNotNull (ex.Message, "#4");
893 				Assert.AreEqual ("buffer", ex.ParamName, "#5");
894 			}
895 		}
896 
897 		[Test]
Write_Count_Negative()898 		public void Write_Count_Negative ()
899 		{
900 			byte [] buffer = new byte [] { 0x0a, 0x1f, 0x2d };
901 
902 			// offset < 0
903 			try {
904 				out_stream.Write (buffer, 1, -1);
905 				Assert.Fail ("#1");
906 			} catch (ArgumentOutOfRangeException ex) {
907 				Assert.AreEqual (typeof (ArgumentOutOfRangeException), ex.GetType (), "#2");
908 				Assert.IsNull (ex.InnerException, "#3");
909 				Assert.IsNotNull (ex.Message, "#4");
910 				Assert.AreEqual ("count", ex.ParamName, "#5");
911 			}
912 		}
913 
914 		[Test]
Write_Count_Overflow()915 		public void Write_Count_Overflow ()
916 		{
917 			byte [] buffer;
918 
919 			buffer = Encoding.UTF8.GetBytes ("Mono");
920 			out_stream.Write (buffer, 0, buffer.Length + 5);
921 			buffer = Encoding.UTF8.GetBytes ("Just Rocks!!");
922 			out_stream.Write (buffer, 5, buffer.Length - 2);
923 			response.Flush ();
924 
925 			string output = Encoding.UTF8.GetString (worker.data);
926 			Assert.AreEqual ("b\r\nMonoRocks!!\r\n", output);
927 		}
928 
929 		[Test]
Write_Offset_Negative()930 		public void Write_Offset_Negative ()
931 		{
932 			byte [] buffer = new byte [] { 0x0a, 0x1f, 0x2d };
933 
934 			// offset < 0
935 			try {
936 				out_stream.Write (buffer, -1, 0);
937 				Assert.Fail ("#1");
938 			} catch (ArgumentOutOfRangeException ex) {
939 				Assert.AreEqual (typeof (ArgumentOutOfRangeException), ex.GetType (), "#2");
940 				Assert.IsNull (ex.InnerException, "#3");
941 				Assert.IsNotNull (ex.Message, "#4");
942 				Assert.AreEqual ("offset", ex.ParamName, "#5");
943 			}
944 		}
945 
946 		[Test]
Write_Offset_Overflow()947 		public void Write_Offset_Overflow ()
948 		{
949 			byte [] buffer = new byte [] { 0x0a, 0x1f, 0x2d };
950 
951 			// offset == buffer length
952 			try {
953 				out_stream.Write (buffer, buffer.Length, 0);
954 				Assert.Fail ("#A1");
955 			} catch (ArgumentOutOfRangeException ex) {
956 				Assert.AreEqual (typeof (ArgumentOutOfRangeException), ex.GetType (), "#A2");
957 				Assert.IsNull (ex.InnerException, "#A3");
958 				Assert.IsNotNull (ex.Message, "#A4");
959 				Assert.AreEqual ("offset", ex.ParamName, "#A5");
960 			}
961 
962 			// offset > buffer length
963 			try {
964 				out_stream.Write (buffer, buffer.Length + 1, 0);
965 				Assert.Fail ("#B1");
966 			} catch (ArgumentOutOfRangeException ex) {
967 				Assert.AreEqual (typeof (ArgumentOutOfRangeException), ex.GetType (), "#B2");
968 				Assert.IsNull (ex.InnerException, "#B3");
969 				Assert.IsNotNull (ex.Message, "#B4");
970 				Assert.AreEqual ("offset", ex.ParamName, "#B5");
971 			}
972 
973 			response.Flush ();
974 			Assert.AreEqual (0, worker.data_len);
975 		}
976 
977 		[Test]
Write_Stream_Closed()978 		public void Write_Stream_Closed ()
979 		{
980 			byte [] buffer = Encoding.UTF8.GetBytes ("mono");
981 			out_stream.Close ();
982 			out_stream.Write (buffer, 0, buffer.Length);
983 			response.Flush ();
984 
985 			string output = Encoding.UTF8.GetString (worker.data);
986 			Assert.AreEqual ("4\r\nmono\r\n", output);
987 		}
988 
Cook(int re, out FakeHttpWorkerRequest2 f)989 		HttpContext Cook (int re, out FakeHttpWorkerRequest2 f)
990 		{
991 			f = new FakeHttpWorkerRequest2 (re);
992 			return new HttpContext (f);
993 		}
994 	}
995 }
996