23c055c7deb147f85fd686b701685622dd29a5e9
[mono.git] / mcs / class / System.Web / Test / System.Web / HttpRequestTest.cs
1 //
2 // System.Web.HttpRequestTest.cs - Unit tests for System.Web.HttpRequest
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //      Miguel de Icaza    <miguel@novell.com>
7 //      Gonzalo Paniagua Javier <gonzalo@novell.com>
8 //
9 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 using System;
31 using System.Text;
32 using System.Web;
33 using System.Collections.Specialized;
34 using NUnit.Framework;
35 using System.Diagnostics;
36
37 namespace MonoTests.System.Web {
38
39         [TestFixture]
40         public class HttpRequestTest {
41
42 #if NET_1_1
43                 [Test]
44                 [ExpectedException (typeof (HttpRequestValidationException))]
45                 public void ValidateInput_XSS ()
46                 {
47                         string problem = "http://server.com/attack2.aspx?test=<script>alert('vulnerability')</script>";
48                         string decoded = HttpUtility.UrlDecode (problem);
49                         int n = decoded.IndexOf ('?');
50                         HttpRequest request = new HttpRequest (null, decoded.Substring (0,n), decoded.Substring (n+1));
51                         request.ValidateInput ();
52                         // the next statement throws
53                         Assert.AreEqual ("<script>alert('vulnerability')</script>", request.QueryString ["test"], "QueryString");
54                 }
55
56                 // Notes:
57                 // * this is to avoid a regression that would cause Mono to 
58                 //   fail again on item #2 of the XSS vulnerabilities listed at:
59                 //   http://it-project.ru/andir/docs/aspxvuln/aspxvuln.en.xml
60                 // * The author notes that Microsoft has decided not to fix 
61                 //   this issue (hence the NotDotNet category).
62
63                 [Test]
64                 [Category ("NotDotNet")]
65                 [ExpectedException (typeof (HttpRequestValidationException))]
66                 public void ValidateInput_XSS_Unicode ()
67                 {
68                         string problem = "http://server.com/attack2.aspx?test=%uff1cscript%uff1ealert('vulnerability')%uff1c/script%uff1e";
69                         string decoded = HttpUtility.UrlDecode (problem);
70                         int n = decoded.IndexOf ('?');
71                         HttpRequest request = new HttpRequest (null, decoded.Substring (0,n), decoded.Substring (n+1));
72                         request.ValidateInput ();
73                         // the next statement throws
74                         Assert.AreEqual ("\xff1cscript\xff1ealert('vulnerability')\xff1c/script\xff1e", request.QueryString ["test"], "QueryString");
75                 }
76
77                 // This has affected ASP.NET 1.1 but it seems fixed now
78                 // http://secunia.com/advisories/9716/
79                 // http://weblogs.asp.net/kaevans/archive/2003/11/12/37169.aspx
80                 [Test]
81                 [ExpectedException (typeof (HttpRequestValidationException))]
82                 public void ValidateInput_XSS_Null ()
83                 {
84                         string problem = "http://secunia.com/?test=<%00SCRIPT>alert(document.cookie)</SCRIPT>";
85                         string decoded = HttpUtility.UrlDecode (problem);
86                         int n = decoded.IndexOf ('?');
87                         HttpRequest request = new HttpRequest (null, decoded.Substring (0,n), decoded.Substring (n+1));
88                         request.ValidateInput ();
89                         // the next statement throws
90                         Assert.AreEqual ("<SCRIPT>alert(document.cookie)</SCRIPT>", request.QueryString ["test"], "QueryString");
91                 }
92 #endif
93                 //
94                 // Tests the properties from the simple constructor.
95                 [Test]
96                 public void Test_PropertiesSimpleConstructor ()
97                 {
98                         string url = "http://www.gnome.org/";
99                         string qs = "key=value&key2=value%32second";
100                         
101                         HttpRequest r = new HttpRequest ("file", url, qs);
102
103                         Assert.AreEqual ("/?" + qs, r.RawUrl, "U1");
104                         Assert.AreEqual (url, r.Url.ToString (), "U2");
105
106                         r = new HttpRequest ("file", "http://www.gnome.org", qs);
107                         Assert.AreEqual (url, r.Url.ToString (), "U3");
108
109                         qs = "a&b=1&c=d&e&b=2&d=";
110                         r = new HttpRequest ("file", url, qs);
111                         NameValueCollection nvc = r.QueryString;
112
113                         Assert.AreEqual ("a,e", nvc [null], "U4");
114                         Assert.AreEqual ("1,2", nvc ["b"], "U5");
115                         Assert.AreEqual ("d", nvc ["c"], "U5");
116                         Assert.AreEqual ("", nvc ["d"], "U6");
117                         Assert.AreEqual (4, nvc.Count, "U6");
118
119                         Assert.AreEqual (null, r.ApplicationPath, "U7");
120                 }
121
122                 [Test]
123                 [ExpectedException(typeof(ArgumentNullException))]
124                 public void Test_AccessToVars ()
125                 {
126                         string url = "http://www.gnome.org/";
127                         string qs = "key=value&key2=value%32second";
128                         
129                         HttpRequest r = new HttpRequest ("file", url, qs);
130                         string s = r.PhysicalApplicationPath;
131                 }
132         }
133
134         [TestFixture]
135         public class Test_HttpFakeRequest {
136                 class FakeHttpWorkerRequest : HttpWorkerRequest {
137                         public int return_kind;
138
139                         [Conditional ("REQUEST_TEST_VERY_VERBOSE")]
140                         void WhereAmI ()
141                         {
142                                 Console.WriteLine (Environment.StackTrace);
143                         }
144                         
145
146                         public FakeHttpWorkerRequest (int re)
147                         {
148                                 return_kind = re;
149                         }
150                         
151                         public override string GetUriPath()
152                         {
153                                 WhereAmI ();
154                                 return "/uri.aspx";
155                         }
156         
157                         public override string GetQueryString()
158                         {
159                                 WhereAmI ();
160
161                                 switch (return_kind) {
162                                 case 20:
163                                         return null;
164                                 case 16:
165                                         return "key1=value1&key2=value2";
166                                 case 25: // HEAD
167                                 case 30: // POST
168                                         return "mapa.x=10&mapa.y=20";
169                                 case 26: // HEAD
170                                 case 31: // POST
171                                         return "mapa.x=10&mapa=20";
172                                 case 27: // GET
173                                         return "mapa.x=10&mapa.y=20";
174                                 case 28: // GET
175                                         return "mapa.x=10";
176                                 case 29: // GET
177                                         return "mapa=10";
178                                 case 32: // GET
179                                         return "mapa.x=pi&mapa.y=20";
180                                 case 50:
181                                         return "PlainString";
182                                 case 51:
183                                         return "Plain&Arg=1";
184                                 default:
185                                         return "GetQueryString";
186                                 }
187                         }
188         
189                         public override string GetRawUrl()
190                         {
191                                 WhereAmI ();
192                                 return "/bb.aspx";
193                         }
194         
195                         public override string GetHttpVerbName()
196                         {
197                                 WhereAmI ();
198                                 if (return_kind == 25 || return_kind == 26)
199                                         return "HEAD";
200                                 if (return_kind == 30 || return_kind == 31)
201                                         return "POST";
202                                 return "GET";
203                         }
204         
205                         public override string GetHttpVersion()
206                         {
207                                 WhereAmI ();
208                                 return "HTTP/1.1";
209                         }
210         
211                         public override byte [] GetPreloadedEntityBody ()
212                         {
213                                 if (return_kind != 30 && return_kind != 31)
214                                         return base.GetPreloadedEntityBody ();
215
216                                 return Encoding.UTF8.GetBytes (GetQueryString ());
217                         }
218
219                         public override bool IsEntireEntityBodyIsPreloaded ()
220                         {
221                                 if (return_kind != 30 && return_kind != 31)
222                                         return base.IsEntireEntityBodyIsPreloaded ();
223
224                                 return true;
225                         }
226
227                         public override int GetRemotePort()
228                         {
229                                 return 1010;
230                         }
231         
232                         public override string GetLocalAddress()
233                         {
234                                 return "localhost";
235                         }
236
237                         public override string GetAppPath ()
238                         {
239                                 return "AppPath";
240                         }
241
242                         public override string GetRemoteName ()
243                         {
244                                 return "RemoteName";
245                         }
246
247                         public override string GetRemoteAddress ()
248                         {
249                                 return "RemoteAddress";
250                         }
251
252                         public override string GetServerName ()
253                         {
254                                 return "localhost";
255                         }
256                         
257                         public override int GetLocalPort()
258                         {
259                                 return 2020;
260                         }
261         
262                         public override void SendStatus(int s, string x)
263                         {
264                         }
265         
266                         public override void SendKnownResponseHeader(int x, string j)
267                         {
268                         }
269         
270                         public override void SendUnknownResponseHeader(string a, string b)
271                         {
272                         }
273                 
274                         public override void SendResponseFromMemory(byte[] arr, int x)
275                         {
276                         }
277         
278                         public override void SendResponseFromFile(string a, long b , long c)
279                         {
280                                 WhereAmI ();
281                         }
282         
283                         public override void SendResponseFromFile (IntPtr a, long b, long c)
284                         {
285                         }
286         
287                         public override void FlushResponse(bool x)
288                         {
289                         }
290         
291                         public override void EndOfRequest() {
292                         }
293
294                         public override string GetKnownRequestHeader (int index)
295                         {
296                                 switch (index){
297                                 case HttpWorkerRequest.HeaderContentType: 
298                                         switch (return_kind){
299                                         case 1: return "text/plain";
300                                         case 2: return "text/plain; charset=latin1";
301                                         case 3: return "text/plain; charset=iso-8859-1";
302                                         case 4: return "text/plain; charset=\"iso-8859-1\"";    
303                                         case 5: return "text/plain; charset=\"iso-8859-1\" ; other";
304                                         case 30:
305                                         case 31:
306                                                 return "application/x-www-form-urlencoded";
307                                         }
308                                         break;
309                                         
310                                 case HttpWorkerRequest.HeaderContentLength:
311                                         switch (return_kind){
312                                         case 0:  return "1024";
313                                         case 1:  return "-1024";
314                                         case 30:
315                                         case 31:
316                                                 return GetQueryString ().Length.ToString ();
317                                         case -1: return "Blah";
318                                         case -2: return "";
319                                         }
320                                                 break;
321
322                                 case HttpWorkerRequest.HeaderCookie:
323                                         switch (return_kind){
324                                         case 10: return "Key=Value";
325                                         case 11: return "Key=<value>";
326                                         case 12: return "Key=>";
327                                         case 13: return "Key=\xff1c";
328                                         case 14: return "Key=\xff1e";
329                                         }
330                                         break;
331                                 case HttpWorkerRequest.HeaderReferer:
332                                         switch (return_kind){
333                                         case 1: return null;
334                                         case 2: return "http://www.mono-project.com/test.aspx";
335                                         case 15: return "http://www.mono-project.com";
336                                         }
337                                         break;
338                                 case HttpWorkerRequest.HeaderUserAgent:
339                                         switch (return_kind){
340                                         case 15: return "Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20040913 Firefox/0.10";
341                                         }
342                                         break;
343                                 case HttpWorkerRequest.HeaderAccept:
344                                         switch (return_kind){
345                                         case 21: return "text/xml,application/xml, application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
346                                         }
347                                         break;
348                                 case HttpWorkerRequest.HeaderAcceptLanguage:
349                                         switch (return_kind){
350                                         case 21: return "en-us, en;q=0.5";
351                                         }
352                                         break;
353                                 }
354                                 return "";
355                         }
356
357                         public override string [][] GetUnknownRequestHeaders ()
358                         {
359                                 if (return_kind == 0)
360                                         return new string [0][];
361
362                                 if (return_kind == 3){
363                                         string [][] x = new string [4][];
364                                         x [0] = new string [] { "k1", "v1" };
365                                         x [1] = new string [] { "k2", "v2" };
366                                         x [2] = new string [] { "k3", "v3" };
367                                         x [3] = new string [] { "k4", "v4" };
368
369                                         return x;
370                                 }
371
372                                 if (return_kind == 4){
373                                         //
374                                         // This tests the bad values and the extra row with an error
375                                         //
376                                         string [][] x = new string [3][];
377                                         x [0] = new string [] { "k1", "" };
378                                         x [1] = new string [] { "k2", null };
379                                         x [2] = new string [] { "k3", "   " };
380
381                                         return x;
382                                 }
383
384                                 if (return_kind == 2){
385                                         string [][] x = new string [2][];
386                                         x [0] = new string [] { "k1", "" };
387
388                                         // Returns an empty row.
389                                         return x;
390                                 }
391
392                                 return null;
393                         }
394                 }
395
396                 HttpContext Cook (int re)
397                 {
398                         FakeHttpWorkerRequest f = new FakeHttpWorkerRequest (re);
399                         HttpContext c = new HttpContext (f);
400
401                         return c;
402                 }
403                 
404                 [Test] public void Test_BrokenContentLength ()
405                 {
406                         HttpContext c = Cook (-1);
407                         Assert.AreEqual (0, c.Request.ContentLength, "C1");
408
409                         c = Cook (-2);
410                         Assert.AreEqual (0, c.Request.ContentLength, "C2");
411
412                 }
413
414                 [Test][ExpectedException(typeof(NullReferenceException))]
415                 public void Test_EmptyUnknownRow ()
416                 {
417                         HttpContext c = Cook (2);
418                         NameValueCollection x = c.Request.Headers;
419                 }
420                 
421                 [Test] public void Test_RequestFields ()
422                 {
423                         HttpContext c = Cook (1);
424
425 #if NET_2_0
426                         Assert.IsNull (c.Request.ApplicationPath, "A1");
427 #else
428                         Assert.AreEqual ("AppPath", c.Request.ApplicationPath, "A1");
429 #endif
430                         Assert.AreEqual ("text/plain", c.Request.ContentType, "A2");
431
432                         c = Cook (0);
433                         Assert.AreEqual (1024, c.Request.ContentLength, "A3");
434                         
435                         c = Cook (3);
436                         Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A4");
437                         NameValueCollection x = c.Request.Headers;
438                         
439                         Assert.AreEqual ("v1", x ["k1"], "K1");
440                         Assert.AreEqual ("v2", x ["k2"], "K2");
441                         Assert.AreEqual ("v3", x ["k3"], "K3");
442                         Assert.AreEqual ("v4", x ["k4"], "K4");
443                         Assert.AreEqual ("text/plain; charset=iso-8859-1", x ["Content-Type"], "K4");
444                         Assert.AreEqual (5, x.Count, "K5");
445
446                         c = Cook (2);
447                         Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A5");
448                         Assert.AreEqual ("text/plain; charset=latin1", c.Request.ContentType, "A5-1");
449
450                         c = Cook (4);
451                         Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A6");
452                         x = c.Request.Headers;
453                         Assert.AreEqual ("", x ["k1"], "K6");
454                         Assert.AreEqual (null, x ["k2"], "K7");
455                         Assert.AreEqual ("   ", x ["k3"], "K8");
456                         Assert.AreEqual (4, x.Count, "K9");
457
458                         c = Cook (5);
459                         Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A7");
460
461                         Assert.AreEqual ("RemoteName", c.Request.UserHostName, "A8");
462                         Assert.AreEqual ("RemoteAddress", c.Request.UserHostAddress, "A9");
463
464                         // Difference between Url property and RawUrl one: one is resolved, the other is not
465                         Assert.AreEqual ("/bb.aspx", c.Request.RawUrl, "A10");
466                         Assert.AreEqual ("http://localhost:2020/uri.aspx?GetQueryString", c.Request.Url.ToString (), "A11");
467                 }
468                 
469                 [Test] public void Test_Cookies ()
470                 {
471                         HttpContext c;
472
473                         c = Cook (10);
474                         c.Request.ValidateInput ();
475                         Assert.AreEqual ("Value", c.Request.Cookies ["Key"].Value, "cookie1");
476                 }
477
478                 [Test]
479                 [ExpectedException(typeof (HttpRequestValidationException))]
480                 public void Test_DangerousCookie ()
481                 {
482                         HttpContext c;
483
484                         c = Cook (11);
485                         c.Request.ValidateInput ();
486                         object a = c.Request.Cookies;
487                 }
488                 
489                 [Test]
490                 [ExpectedException(typeof (HttpRequestValidationException))]
491                 [Category ("NotDotNet")] // doesn't work on 1.1 SP1 and 2.0 RC
492                 public void Test_DangerousCookie2 ()
493                 {
494                         HttpContext c;
495
496                         c = Cook (12);
497                         c.Request.ValidateInput ();
498                         object a = c.Request.Cookies;
499                 }
500                 
501                 [Test]
502                 [ExpectedException(typeof (HttpRequestValidationException))]
503                 [Category ("NotDotNet")] // doesn't work on 1.1 SP1 and 2.0 RC
504                 public void Test_DangerousCookie3 ()
505                 {
506                         HttpContext c;
507
508                         c = Cook (13);
509                         c.Request.ValidateInput ();
510                         object a = c.Request.Cookies;
511                 }
512                 
513                 [Test]
514                 [ExpectedException(typeof (HttpRequestValidationException))]
515                 [Category ("NotDotNet")] // doesn't work on 1.1 SP1 and 2.0 RC
516                 public void Test_DangerousCookie4 ()
517                 {
518                         HttpContext c;
519
520                         c = Cook (14);
521                         c.Request.ValidateInput ();
522                         object a = c.Request.Cookies;
523                 }
524
525                 [Test]
526                 public void Test_MiscHeaders ()
527                 {
528                         HttpContext c = Cook (15);
529
530                         // The uri ToString contains the trailing slash.
531                         Assert.AreEqual ("http://www.mono-project.com/", c.Request.UrlReferrer.ToString (), "ref1");
532                         
533                         Assert.AreEqual ("Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20040913 Firefox/0.10", c.Request.UserAgent, "ref2");
534
535                         // All the AcceptTypes and UserLanguages tests below here pass under MS
536                         c = Cook (20);
537                         string [] at = c.Request.AcceptTypes;
538                         string [] ul = c.Request.UserLanguages;
539                         Assert.IsNull (at, "AT1");
540                         Assert.IsNull (ul, "UL1");
541                         c = Cook (21);
542                         at = c.Request.AcceptTypes;
543                         Assert.IsNotNull (at, "AT2");
544                         string [] expected = { "text/xml", "application/xml", "application/xhtml+xml", "text/html;q=0.9",
545                                                 "text/plain;q=0.8", "image/png", "*/*;q=0.5" };
546
547                         Assert.AreEqual (expected.Length, at.Length, "AT3");
548                         for (int i = expected.Length - 1; i >= 0; i--)
549                                 Assert.AreEqual (expected [i], at [i], "AT" + (3 + i));
550
551                         ul = c.Request.UserLanguages;
552                         Assert.IsNotNull (ul, "UL2");
553                         expected = new string [] { "en-us", "en;q=0.5" };
554
555                         Assert.AreEqual (expected.Length, ul.Length, "UL3");
556                         for (int i = expected.Length - 1; i >= 0; i--)
557                                 Assert.AreEqual (expected [i], ul [i], "UL" + (3 + i));
558
559                 }
560
561                 [Test]
562                 public void Empty_WorkerRequest_QueryString ()
563                 {
564                         HttpContext c = Cook (20);
565
566                         //
567                         // Checks that the following line does not throw an exception if
568                         // the querystring returned by the HttpWorkerRequest is null.
569                         //
570                         NameValueCollection nvc = c.Request.QueryString;
571                 }
572
573                 [Test]
574                 public void Test_QueryString_ToString ()
575                 {
576                         HttpContext c = Cook (50);
577
578                         Assert.AreEqual (c.Request.QueryString.ToString (), "PlainString", "QTS#1");
579
580                         c = Cook (51);
581                         Assert.AreEqual (c.Request.QueryString.ToString (), "Plain&Arg=1", "QTS#2");
582                 }
583                 
584                 [Test]
585                 public void Leading_qm_in_QueryString ()
586                 {
587                         HttpContext c = Cook (16);
588                         NameValueCollection nvc = c.Request.QueryString;
589                         foreach (string id in nvc.AllKeys) {
590                                 if (id.StartsWith ("?"))
591                                         Assert.Fail (id);
592                         }
593                 }
594
595                 [Test]
596                 public void TestPath ()
597                 {
598                         HttpContext c = Cook (16);
599
600                         // This used to crash, ifolder exposed this
601                         string x = c.Request.Path;
602                 }
603                 
604                 [Test]
605                 public void MapImageCoordinatesHEAD ()
606                 {
607                         HttpContext c = Cook (25);
608                         int [] coords = c.Request.MapImageCoordinates ("mapa");
609                         Assert.IsNotNull (coords, "A1");
610                         Assert.AreEqual (10, coords [0], "X");
611                         Assert.AreEqual (20, coords [1], "Y");
612
613                         c = Cook (26);
614                         coords = c.Request.MapImageCoordinates ("mapa");
615                         Assert.AreEqual (null, coords, "coords");
616                 }
617
618                 [Test]
619                 public void MapImageCoordinatesGET ()
620                 {
621                         HttpContext c = Cook (27);
622                         int [] coords = c.Request.MapImageCoordinates ("mapa");
623                         Assert.AreEqual (10, coords [0], "X");
624                         Assert.AreEqual (20, coords [1], "Y");
625
626                         coords = c.Request.MapImageCoordinates ("m");
627                         Assert.AreEqual (null, coords, "coords1");
628
629                         c = Cook (28);
630                         coords = c.Request.MapImageCoordinates ("mapa");
631                         Assert.AreEqual (null, coords, "coords2");
632                         c = Cook (29);
633                         coords = c.Request.MapImageCoordinates ("mapa");
634                         Assert.AreEqual (null, coords, "coords3");
635                         c = Cook (32);
636                         coords = c.Request.MapImageCoordinates ("mapa");
637                         Assert.AreEqual (null, coords, "coords4");
638                 }
639
640                 [Test]
641                 public void MapImageCoordinatesPOST ()
642                 {
643                         HttpContext c = Cook (30);
644                         int [] coords = c.Request.MapImageCoordinates ("mapa");
645                         Assert.IsNotNull (coords, "A1");
646                         Assert.AreEqual (10, coords [0], "X");
647                         Assert.AreEqual (20, coords [1], "Y");
648
649                         c = Cook (31);
650                         coords = c.Request.MapImageCoordinates ("mapa");
651                         Assert.AreEqual (null, coords, "coords2");
652                 }
653
654                 [Test]
655                 public void TestReferer ()
656                 {
657                         HttpContext c = Cook (1);
658
659                         Assert.AreEqual (null, c.Request.UrlReferrer, "REF1");
660
661                         c = Cook (2);
662                         Assert.AreEqual ("http://www.mono-project.com/test.aspx", c.Request.UrlReferrer.ToString (), "REF1");                   
663                 }
664                 
665
666                 [Test]
667                 public void NegativeContentLength ()
668                 {
669                         HttpContext c = Cook (1);
670                         HttpRequest req = c.Request;
671                         Assert.AreEqual (0, req.ContentLength, "#01");
672                 }
673         }
674
675         // This class is defined here to make it easy to create fake
676         // HttpWorkerRequest-derived classes by only overriding the methods
677         // necessary for testing.
678         class BaseFakeHttpWorkerRequest : HttpWorkerRequest
679         {
680                 public override void EndOfRequest()
681                 {
682                 }
683
684                 public override void FlushResponse(bool finalFlush)
685                 {
686                 }
687
688                 public override string GetHttpVerbName()
689                 {
690                         return "GET";
691                 }
692
693                 public override string GetHttpVersion()
694                 {
695                         return "HTTP/1.1";
696                 }
697
698                 public override string GetLocalAddress()
699                 {
700                         return "localhost";
701                 }
702
703                 public override int GetLocalPort()
704                 {
705                         return 8080;
706                 }
707
708                 public override string GetQueryString()
709                 {
710                         return String.Empty;
711                 }
712
713                 public override string GetRawUrl()
714                 {
715                         string rawUrl = GetUriPath();
716                         string queryString = GetQueryString();
717                         if (queryString != null && queryString.Length > 0)
718                         {
719                                 rawUrl += "?" + queryString;
720                         }
721                         return rawUrl;
722                 }
723
724                 public override string GetRemoteAddress()
725                 {
726                         return "remotehost";
727                 }
728
729                 public override int GetRemotePort()
730                 {
731                         return 8080;
732                 }
733
734                 public override string GetUriPath()
735                 {
736                         return "default.aspx";
737                 }
738
739                 public override void SendKnownResponseHeader(int index, string value)
740                 {
741                 }
742
743                 public override void SendResponseFromFile(IntPtr handle, long offset, long length)
744                 {
745                 }
746
747                 public override void SendResponseFromFile(string filename, long offset, long length)
748                 {
749                 }
750
751                 public override void SendResponseFromMemory(byte[] data, int length)
752                 {
753                 }
754
755                 public override void SendStatus(int statusCode, string statusDescription)
756                 {
757                 }
758
759                 public override void SendUnknownResponseHeader(string name, string value)
760                 {
761                 }
762         }
763
764         // This test ensures accessing the Form property does not throw an
765         // exception when the length of data in the request exceeds the length
766         // as reported by the Content-Length header. This bug was discovered
767         // with an AJAX application using XMLHttpRequest to POST back to the
768         // server. The Content-Length header was two bytes less than the length
769         // of the buffer returned from GetPreloadedEntityBody. This was causing
770         // an exception to be thrown by Mono because it was trying to allocate
771         // a buffer that was -2 bytes in length.
772         [TestFixture]
773         public class Test_UrlEncodedBodyWithExtraCRLF
774         {
775                 class FakeHttpWorkerRequest : BaseFakeHttpWorkerRequest
776                 {
777                         // This string is 9 bytes in length. That's 2 more than
778                         // the Content-Length header says it should be.
779                         string data = "foo=bar\r\n";
780
781                         public override string GetKnownRequestHeader(int index)
782                         {
783                                 switch (index)
784                                 {
785                                         case HttpWorkerRequest.HeaderContentLength:
786                                                 return (data.Length - 2).ToString();
787                                         case HttpWorkerRequest.HeaderContentType:
788                                                 return "application/x-www-form-urlencoded";
789                                 }
790                                 return String.Empty;
791                         }
792
793                         public override byte[] GetPreloadedEntityBody()
794                         {
795                                 return Encoding.ASCII.GetBytes(data);
796                         }
797                 }
798
799                 HttpContext context = null;
800
801                 [SetUp]
802                 public void SetUp()
803                 {
804                         HttpWorkerRequest workerRequest = new FakeHttpWorkerRequest();
805                         context = new HttpContext(workerRequest);
806                 }
807
808                 [Test]
809                 public void ContentLength()
810                 {
811                         Assert.AreEqual(7, context.Request.ContentLength);
812                 }
813
814                 [Test]
815                 public void Form_Count()
816                 {
817                         Assert.AreEqual(1, context.Request.Form.Count);
818                 }
819
820                 [Test]
821                 [Category ("NotDotNet")]
822                 public void Form_Item()
823                 {
824                         // I would have expected the extra two characters to be stripped
825                         // but Microsoft's CLR keeps them so Mono should, too.
826                         //Assert.AreEqual("bar\r\n", context.Request.Form["foo"]);
827                         Assert.AreEqual("bar", context.Request.Form["foo"]);
828                 }
829         }
830
831         // This test ensures the HttpRequet object's Form property gets
832         // properly constructed and populated when the Content-Type header
833         // includes a charset parameter and that the charset parameter is
834         // respected.
835         [TestFixture]
836         public class Test_UrlEncodedBodyWithUtf8CharsetParameter
837         {
838                 class FakeHttpWorkerRequest : BaseFakeHttpWorkerRequest
839                 {
840                         // The two funny-looking characters are really a single
841                         // accented "a" character encoded in UTF-8.
842                         string data = "foo=b%C3%A1r";
843
844                         public override string GetKnownRequestHeader(int index)
845                         {
846                                 switch (index)
847                                 {
848                                         case HttpWorkerRequest.HeaderContentLength:
849                                                 return data.Length.ToString();
850                                         case HttpWorkerRequest.HeaderContentType:
851                                                 return "application/x-www-form-urlencoded; charset=utf-8";
852                                 }
853                                 return String.Empty;
854                         }
855
856                         public override byte[] GetPreloadedEntityBody()
857                         {
858                                 return Encoding.ASCII.GetBytes(data);
859                         }
860                 }
861
862                 HttpContext context = null;
863
864                 [SetUp]
865                 public void SetUp()
866                 {
867                         HttpWorkerRequest workerRequest = new FakeHttpWorkerRequest();
868                         context = new HttpContext(workerRequest);
869                 }
870
871                 [Test]
872                 public void Form_Count()
873                 {
874                         Assert.AreEqual(1, context.Request.Form.Count);
875                 }
876
877                 [Test]
878                 public void Form_Item()
879                 {
880                         Assert.AreEqual("b\xE1r", context.Request.Form["foo"]);
881                 }
882         }
883 }
884