Merge pull request #2124 from mhutch/mono-api-info-forwarders
[mono.git] / mcs / class / System / Test / System.Net / HttpListener2Test.cs
1 //
2 // HttpListener2Test.cs
3 //      - Unit tests for System.Net.HttpListener - connection testing
4 //
5 // Author:
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System;
31 using System.Collections.Generic;
32 using System.Globalization;
33 using System.IO;
34 using System.Net;
35 using System.Net.Sockets;
36 using System.Text;
37 using System.Threading;
38
39 using NUnit.Framework;
40
41 // ***************************************************************************************
42 // NOTE: when adding prefixes, make then unique per test, as MS might take 'some time' to
43 // unregister it even after explicitly closing the listener.
44 // ***************************************************************************************
45 namespace MonoTests.System.Net {
46         
47         [TestFixture]
48         public class HttpListener2Test {
49                 
50                 private HttpListener _listener = null;
51                 
52                 public class MyNetworkStream : NetworkStream {
53                         public MyNetworkStream (Socket sock) : base (sock, true)
54                         {
55                         }
56
57                         public Socket GetSocket ()
58                         {
59                                 return Socket;
60                         }
61                 }
62
63                 public static HttpListener CreateAndStartListener (string prefix)
64                 {
65                         HttpListener listener = new HttpListener ();
66                         listener.Prefixes.Add (prefix);
67                         listener.Start ();
68                         return listener;
69                 }
70
71                 public static HttpListener CreateAndStartListener (string prefix, AuthenticationSchemes authSchemes)
72                 {
73                         HttpListener listener = new HttpListener ();
74                         listener.AuthenticationSchemes = authSchemes;
75                         listener.Prefixes.Add (prefix);
76                         listener.Start ();
77                         return listener;
78                 }
79
80                 public static MyNetworkStream CreateNS (int port)
81                 {
82                         return CreateNS (port, 5000);
83                 }
84
85                 public static MyNetworkStream CreateNS (int port, int timeout_ms)
86                 {
87                         Socket sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
88                         sock.Connect (new IPEndPoint (IPAddress.Loopback, port));
89                         sock.SendTimeout = timeout_ms;
90                         sock.ReceiveTimeout = timeout_ms;
91                         return new MyNetworkStream (sock);
92                 }
93
94                 public static void Send (Stream stream, string str)
95                 {
96                         byte [] bytes = Encoding.ASCII.GetBytes (str);
97                         stream.Write (bytes, 0, bytes.Length);
98                 }
99
100                 public static string Receive (Stream stream, int size)
101                 {
102                         byte [] bytes = new byte [size];
103                         int nread = stream.Read (bytes, 0, size);
104                         return Encoding.ASCII.GetString (bytes, 0, nread);
105                 }
106
107                 public static string ReceiveWithTimeout (Stream stream, int size, int timeout, out bool timed_out)
108                 {
109                         byte [] bytes = new byte [size];
110                         IAsyncResult ares = stream.BeginRead (bytes, 0, size, null, null);
111                         timed_out = !ares.AsyncWaitHandle.WaitOne (timeout, false);
112                         if (timed_out)
113                                 return null;
114                         int nread = stream.EndRead (ares);
115                         return Encoding.ASCII.GetString (bytes, 0, nread);
116                 }
117
118                 public static HttpListenerContext GetContextWithTimeout (HttpListener listener, int timeout, out bool timed_out)
119                 {
120                         IAsyncResult ares = listener.BeginGetContext (null, null);
121                         timed_out = !ares.AsyncWaitHandle.WaitOne (timeout, false);
122                         if (timed_out) 
123                                 return null;
124                         return listener.EndGetContext (ares);
125                 }
126                 
127                 [TearDown] 
128                 public void Dispose()
129                 {
130                         if (_listener != null) {
131                                 _listener.Close();
132                                 _listener = null;
133                         }
134                 }
135
136                 [Test]
137                 public void Test1 ()
138                 {
139                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test1/");
140                         NetworkStream ns = CreateNS (9000);
141                         Send (ns, "GET / HTTP/1.1\r\n\r\n"); // No host
142                         string response = Receive (ns, 512);
143                         ns.Close ();
144                         StringAssert.StartsWith ("HTTP/1.1 400", response);
145                 }
146
147                 [Test]
148                 public void Test2 ()
149                 {
150                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test2/");
151                         NetworkStream ns = CreateNS (9000);
152                         Send (ns, "GET / HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n"); // no prefix
153                         string response = Receive (ns, 512);
154                         ns.Close ();
155                         StringAssert.StartsWith ("HTTP/1.1 400", response);
156                 }
157
158                 [Test]
159                 public void Test3 ()
160                 {
161                         StringBuilder bad = new StringBuilder ();
162                         for (int i = 0; i < 33; i++){
163                                 if (i != 13)
164                                         bad.Append ((char) i);
165                         }
166                         bad.Append ('(');
167                         bad.Append (')');
168                         bad.Append ('[');
169                         bad.Append (']');
170                         bad.Append ('<');
171                         bad.Append ('>');
172                         bad.Append ('@');
173                         bad.Append (',');
174                         bad.Append (';');
175                         bad.Append (':');
176                         bad.Append ('\\');
177                         bad.Append ('"');
178                         bad.Append ('/');
179                         bad.Append ('?');
180                         bad.Append ('=');
181                         bad.Append ('{');
182                         bad.Append ('}');
183
184                         foreach (char b in bad.ToString ()){
185                                 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test3/");
186                                 NetworkStream ns = CreateNS (9000);
187                                 Send (ns, String.Format ("MA{0} / HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n", b)); // bad method
188                                 
189                                 string response = Receive (ns, 512);
190                                 ns.Close ();
191                                 listener.Close ();
192                                 StringAssert.StartsWith ("HTTP/1.1 400", response, String.Format ("Failed on {0}", (int) b));
193                         }
194                 }
195
196                 [Test]
197                 public void Test4 ()
198                 {
199                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test4/");
200                         NetworkStream ns = CreateNS (9000);
201                         Send (ns, "POST /test4/ HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n"); // length required
202                         string response = Receive (ns, 512);
203                         ns.Close ();
204                         StringAssert.StartsWith ("HTTP/1.1 411", response);
205                 }
206
207                 [Test]
208                 public void Test5 ()
209                 {
210                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test5/");
211                         NetworkStream ns = CreateNS (9000);
212                         Send (ns, "POST / HTTP/1.1\r\nHost: 127.0.0.1\r\nTransfer-Encoding: pepe\r\n\r\n"); // not implemented
213                         string response = Receive (ns, 512);
214                         ns.Close ();
215                         StringAssert.StartsWith ("HTTP/1.1 501", response);
216                 }
217
218                 [Test]
219                 public void Test6 ()
220                 {
221                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test6/");
222                         NetworkStream ns = CreateNS (9000);
223                          // not implemented! This is against the RFC. Should be a bad request/length required
224                         Send (ns, "POST /test6/ HTTP/1.1\r\nHost: 127.0.0.1\r\nTransfer-Encoding: identity\r\n\r\n");
225                         string response = Receive (ns, 512);
226                         ns.Close ();
227                         StringAssert.StartsWith ("HTTP/1.1 501", response);
228                 }
229
230                 [Test]
231                 public void Test7 ()
232                 {
233                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test7/");
234                         NetworkStream ns = CreateNS (9000);
235                         Send (ns, "POST /test7/ HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: 3\r\n\r\n123");
236                         HttpListenerContext ctx = _listener.GetContext ();
237                         Send (ctx.Response.OutputStream, "%%%OK%%%");
238                         ctx.Response.Close ();
239                         string response = Receive (ns, 1024);
240                         ns.Close ();
241                         StringAssert.StartsWith ("HTTP/1.1 200", response);
242                         StringAssert.Contains ("Transfer-Encoding: chunked", response);
243                 }
244
245                 [Test]
246                 public void Test8 ()
247                 {
248                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test8/");
249                         NetworkStream ns = CreateNS (9000);
250                         // Just like Test7, but 1.0
251                         Send (ns, "POST /test8/ HTTP/1.0\r\nHost: 127.0.0.1\r\nContent-Length: 3\r\n\r\n123");
252                         HttpListenerContext ctx = _listener.GetContext ();
253                         Send (ctx.Response.OutputStream, "%%%OK%%%");
254                         ctx.Response.Close ();
255                         string response = Receive (ns, 512);
256                         ns.Close ();
257                         StringAssert.StartsWith ("HTTP/1.1 200", response);
258                         Assert.IsTrue (-1 == response.IndexOf ("Transfer-Encoding: chunked"));
259                 }
260
261                 [Test]
262                 public void Test9 ()
263                 {
264                         // 1.0 + "Transfer-Encoding: chunked"
265                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test9/");
266                         NetworkStream ns = CreateNS (9000);
267                         Send (ns, "POST /test9/ HTTP/1.0\r\nHost: 127.0.0.1\r\nTransfer-Encoding: chunked\r\n\r\n3\r\n123\r\n0\r\n\r\n");
268                         bool timeout;
269                         string response = ReceiveWithTimeout (ns, 512, 1000, out timeout);
270                         ns.Close ();
271                         Assert.IsFalse (timeout);
272                         StringAssert.StartsWith ("HTTP/1.1 411", response);
273                 }
274
275                 [Test]
276                 public void Test10 ()
277                 {
278                         // Same as Test9, but now we shutdown the socket for sending.
279                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test10/");
280                         MyNetworkStream ns = CreateNS (9000);
281                         Send (ns, "POST /test10/ HTTP/1.0\r\nHost: 127.0.0.1\r\nTransfer-Encoding: chunked\r\n\r\n3\r\n123\r\n0\r\n\r\n");
282                         ns.GetSocket ().Shutdown (SocketShutdown.Send);
283                         bool timeout;
284                         string response = ReceiveWithTimeout (ns, 512, 1000, out timeout);
285                         ns.Close ();
286                         Assert.IsFalse (timeout);
287                         StringAssert.StartsWith ("HTTP/1.1 411", response);
288                 }
289
290                 [Test]
291                 public void Test11 ()
292                 {
293                         // 0.9
294                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test11/");
295                         MyNetworkStream ns = CreateNS (9000);
296                         Send (ns, "POST /test11/ HTTP/0.9\r\nHost: 127.0.0.1\r\n\r\n123");
297                         ns.GetSocket ().Shutdown (SocketShutdown.Send);
298                         string input = Receive (ns, 512);
299                         ns.Close ();
300                         StringAssert.StartsWith ("HTTP/1.1 400", input);
301                 }
302
303                 [Test]
304                 public void Test12 ()
305                 {
306                         // 0.9
307                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test12/");
308                         MyNetworkStream ns = CreateNS (9000);
309                         Send (ns, "POST /test12/ HTTP/0.9\r\nHost: 127.0.0.1\r\nContent-Length: 3\r\n\r\n123");
310                         ns.GetSocket ().Shutdown (SocketShutdown.Send);
311                         string input = Receive (ns, 512);
312                         ns.Close ();
313                         StringAssert.StartsWith ("HTTP/1.1 400", input);
314                 }
315
316                 [Test]
317                 public void Test13 ()
318                 {
319                         // 0.9
320                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test13/");
321                         MyNetworkStream ns = CreateNS (9000);
322                         Send (ns, "GEt /test13/ HTTP/0.9\r\nHost: 127.0.0.1\r\n\r\n");
323                         ns.GetSocket ().Shutdown (SocketShutdown.Send);
324                         string input = Receive (ns, 512);
325                         ns.Close ();
326                         StringAssert.StartsWith ("HTTP/1.1 400", input);
327                 }
328
329                 HttpListenerRequest test14_request;
330                 ManualResetEvent test_evt;
331                 bool test14_error;
332                 [Test]
333                 public void Test14 ()
334                 {
335                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test14/");
336                         MyNetworkStream ns = CreateNS (9000);
337                         Send (ns, "POST /test14/ HTTP/1.0\r\nHost: 127.0.0.1\r\nContent-Length: 3\r\n\r\n123");
338                         HttpListenerContext c = _listener.GetContext ();
339                         test14_request = c.Request;
340                         test_evt = new ManualResetEvent (false);
341                         Thread thread = new Thread (ReadToEnd);
342                         thread.Start ();
343                         if (test_evt.WaitOne (3000, false) == false) {
344                                 thread.Abort ();
345                                 test_evt.Close ();
346                                 Assert.IsTrue (false, "Timed out");
347                         }
348                         test_evt.Close ();
349                         c.Response.Close ();
350                         ns.Close ();
351                         Assert.AreEqual ("123", read_to_end, "Did not get the expected input.");
352                 }
353
354                 string read_to_end;
355                 void ReadToEnd ()
356                 {
357                         using (StreamReader r = new StreamReader (test14_request.InputStream)) {
358                                 read_to_end = r.ReadToEnd ();
359                                 test_evt.Set ();
360                         }
361                 }
362
363                 [Test]
364                 public void Test15 ()
365                 {
366                         // 2 separate writes -> 2 packets. Body size > 8kB
367                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test15/");
368                         MyNetworkStream ns = CreateNS (9000);
369                         Send (ns, "POST /test15/ HTTP/1.0\r\nHost: 127.0.0.1\r\nContent-Length: 8888\r\n\r\n");
370                         Thread.Sleep (800);
371                         string data = new string ('a', 8888);
372                         Send (ns, data);
373                         HttpListenerContext c = _listener.GetContext ();
374                         HttpListenerRequest req = c.Request;
375                         using (StreamReader r = new StreamReader (req.InputStream)) {
376                                 read_to_end = r.ReadToEnd ();
377                         }
378                         Assert.AreEqual (read_to_end.Length, data.Length, "Wrong length");
379                         Assert.IsTrue (data == read_to_end, "Wrong data");
380                         c.Response.Close ();
381                         ns.Close ();
382                 }
383
384                 [Test]
385                 public void Test16 ()
386                 {
387                         // 1 single write with headers + body (size > 8kB)
388                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test16/");
389                         MyNetworkStream ns = CreateNS (9000);
390                         StringBuilder sb = new StringBuilder ();
391                         sb.Append ("POST /test16/ HTTP/1.0\r\nHost: 127.0.0.1\r\nContent-Length: 8888\r\n\r\n");
392                         string eights = new string ('b', 8888);
393                         sb.Append (eights);
394                         string data = sb.ToString ();
395                         Send (ns, data);
396                         HttpListenerContext c = _listener.GetContext ();
397                         HttpListenerRequest req = c.Request;
398                         using (StreamReader r = new StreamReader (req.InputStream)) {
399                                 read_to_end = r.ReadToEnd ();
400                         }
401                         Assert.AreEqual (read_to_end.Length, read_to_end.Length, "Wrong length");
402                         Assert.IsTrue (eights == read_to_end, "Wrong data");
403                         c.Response.Close ();
404                         ns.Close ();
405                 }
406
407                 [Test]
408                 public void Test17 ()
409                 {
410                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/test17/");
411                         NetworkStream ns = CreateNS (9000);
412                         Send (ns, "RANDOM /test17/ HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: 3\r\n\r\n123");
413                         HttpListenerContext ctx = _listener.GetContext ();
414                         Send (ctx.Response.OutputStream, "%%%OK%%%");
415                         ctx.Response.Close ();
416                         string response = Receive (ns, 1024);
417                         ns.Close ();
418                         StringAssert.StartsWith ("HTTP/1.1 200", response);
419                         StringAssert.Contains ("Transfer-Encoding: chunked", response);
420                 }
421
422                 [Test]
423                 public void Test_MultipleClosesOnOuputStreamAllowed ()
424                 {
425                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/MultipleCloses/");
426                         NetworkStream ns = CreateNS (9000);
427                         Send (ns, "GET /MultipleCloses/ HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n");
428
429                         HttpListenerContext ctx = _listener.GetContext ();
430                         ctx.Response.OutputStream.Close ();
431                         ctx.Response.OutputStream.Close ();
432                         ctx.Response.OutputStream.Close ();
433                         ctx.Response.Close ();
434                 }
435         
436                 void SendCookie ()
437                 {
438                         NetworkStream ns = CreateNS (9000);
439                         Send (ns, "GET /SendCookie/ HTTP/1.1\r\nHost: 127.0.0.1\r\n"+
440                                 "Cookie:$Version=\"1\"; "+
441                                 "Cookie1=Value1; $Path=\"/\"; "+
442                                 "CookieM=ValueM; $Path=\"/p2\"; $Domain=\"test\"; $Port=\"99\";"+
443                                 "Cookie2=Value2; $Path=\"/foo\";"+
444                                 "\r\n"+
445                                 "\r\n");
446                         ns.Flush ();
447                         Thread.Sleep (200);
448                         ns.Close();
449                 }
450
451                 [Test]
452                 public void ReceiveCookiesFromClient ()
453                 {
454                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/SendCookie/");
455                         Thread clientThread = new Thread (new ThreadStart (SendCookie));
456                         clientThread.Start ();
457
458                         HttpListenerContext context = _listener.GetContext();
459                         HttpListenerRequest request = context.Request;
460
461                         Assert.AreEqual (3, request.Cookies.Count, "#1");
462                         foreach (Cookie c in request.Cookies) {
463                                 if (c.Name == "Cookie1") {
464                                         Assert.AreEqual ("Value1", c.Value, "#2");
465                                         Assert.AreEqual ("\"/\"", c.Path, "#3");
466                                         Assert.AreEqual (0, c.Port.Length, "#4");
467                                         Assert.AreEqual (0, c.Domain.Length, "#5");
468                                 } else if (c.Name == "CookieM") {
469                                         Assert.AreEqual ("ValueM", c.Value, "#6");
470                                         Assert.AreEqual ("\"/p2\"", c.Path, "#7");
471                                         Assert.AreEqual ("\"99\"", c.Port, "#8");
472                                         Assert.AreEqual ("\"test\"", c.Domain, "#9");
473                                 } else if (c.Name == "Cookie2") {
474                                         Assert.AreEqual ("Value2", c.Value, "#10");
475                                         Assert.AreEqual ("\"/foo\"", c.Path, "#11");
476                                         Assert.AreEqual (0, c.Port.Length, "#12");
477                                         Assert.AreEqual (0, c.Domain.Length, "#13");
478                                 } else
479                                         Assert.Fail ("Invalid cookie name " + c.Name);
480                         }
481                 }
482
483                 private object _lock = new Object();
484                 private string cookieResponse;
485
486                 void ReceiveCookie () {
487                         lock (_lock) {
488                                 NetworkStream ns = CreateNS (9000);
489                                 Send (ns, "GET /ReceiveCookie/ HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n");
490                                 cookieResponse = Receive (ns, 512);
491                         }
492                 }
493
494                 [Test]
495                 public void SendCookiestoClient ()
496                 {
497                         _listener = CreateAndStartListener ("http://127.0.0.1:9000/ReceiveCookie/");
498                         Thread clientThread = new Thread (new ThreadStart (ReceiveCookie));
499                         clientThread.Start ();
500
501                         HttpListenerContext context = _listener.GetContext();
502                         HttpListenerRequest request = context.Request;
503                         HttpListenerResponse response = context.Response;
504
505                         Cookie cookie = new Cookie ();
506                         cookie.Name = "Name0";
507                         cookie.Value = "Value0";
508                         cookie.Domain = "blue";
509                         cookie.Path = "/path/";
510                         cookie.Port = "\"80\"";
511                         cookie.Version = 1;
512                         response.Cookies.Add (cookie);
513
514                         string responseString = "<HTML><BODY>----</BODY></HTML>";
515                         byte[] buffer = Encoding.UTF8.GetBytes(responseString);
516                         response.ContentLength64 = buffer.Length;
517                         Stream output = response.OutputStream;
518                         output.Write(buffer, 0, buffer.Length);
519                         output.Flush ();
520                         response.Close();
521                         
522                         lock (_lock) {
523                                 bool foundCookie = false;
524                                 foreach (String str in cookieResponse.Split ('\n')) {
525                                         if (!str.StartsWith ("Set-Cookie"))
526                                                 continue;
527                                         Dictionary<string, String> dic = new Dictionary<string, String>();
528                                         foreach (String p in str.Substring (str.IndexOf (":") + 1).Split (';')) {
529                                                 String[] parts = p.Split('=');
530                                                 dic.Add (parts [0].Trim (), parts [1].Trim ());
531                                         }
532                                         Assert.AreEqual ("Value0", dic ["Name0"], "#1");
533                                         Assert.AreEqual ("blue", dic ["Domain"], "#2");
534                                         Assert.AreEqual ("\"/path/\"", dic ["Path"], "#3");
535                                         Assert.AreEqual ("\"80\"", dic ["Port"], "#4");
536                                         Assert.AreEqual ("1", dic ["Version"], "#5");
537                                         foundCookie = true;
538                                         break;
539                                 }
540                                 Assert.IsTrue (foundCookie, "#6");
541                         }
542                 }
543
544                 [Test]
545                 public void MultiResponses ()
546                 {
547                         Thread srv = new Thread (new ThreadStart (EchoServer));
548                         srv.Start ();
549                         Thread.Sleep (200);
550
551                         for (int i = 0; i < 10; i++) {
552                                 string payload = string.Format (CultureInfo.InvariantCulture,
553                                         "Client{0}", i);
554
555                                 HttpWebRequest req = (HttpWebRequest) WebRequest.Create (
556                                         "http://localhost:8888/foobar/");
557                                 req.ServicePoint.Expect100Continue = false;
558                                 req.ServicePoint.UseNagleAlgorithm = false;
559                                 req.Method = "POST";
560                                 StreamWriter w = new StreamWriter (req.GetRequestStream ());
561                                 w.WriteLine (payload);
562                                 w.Close ();
563
564                                 HttpWebResponse resp = (HttpWebResponse) req.GetResponse ();
565                                 StreamReader r = new StreamReader (resp.GetResponseStream ());
566                                 Assert.AreEqual ("Hello, " + payload + "!", r.ReadToEnd ().Trim ());
567                                 r.Close ();
568                         }
569
570                         manualReset.Set ();
571                         srv.Join ();
572                 }
573
574                 void EchoServer ()
575                 {
576                         _listener = new HttpListener ();
577                         _listener.Prefixes.Add ("http://*:8888/foobar/");
578                         _listener.Start ();
579
580                         manualReset = new ManualResetEvent (false);
581
582                         IAsyncResult result = _listener.BeginGetContext (
583                                 new AsyncCallback (EchoCallback), _listener);
584                         manualReset.WaitOne ();
585                 }
586
587                 void EchoCallback (IAsyncResult result)
588                 {
589                         HttpListener listener = (HttpListener) result.AsyncState;
590                         HttpListenerContext context = listener.EndGetContext (result);
591                         HttpListenerRequest req = context.Request;
592                         StreamReader r = new StreamReader (req.InputStream);
593                         string reqBody = r.ReadToEnd ().Trim ();
594
595                         HttpListenerResponse resp = context.Response;
596                         StreamWriter o = new StreamWriter (resp.OutputStream);
597                         o.WriteLine ("Hello, " + reqBody + "!");
598                         o.Close ();
599
600                         listener.BeginGetContext (new AsyncCallback (EchoCallback), listener);
601                 }
602
603                 private ManualResetEvent manualReset;
604
605         }
606
607         [TestFixture]
608         public class HttpListenerBugs {
609                 [Test]
610                 public void TestNonChunkedAsync ()
611                 {
612                         HttpListener listener = HttpListener2Test.CreateAndStartListener ("http://127.0.0.1:9123/");
613
614                         listener.BeginGetContext (callback, listener);
615                         
616                         HttpListener2Test.MyNetworkStream ns = HttpListener2Test.CreateNS (9123);
617                         string message = "<script>\n"+
618                                 " <!-- register the blueprint for our show-headers service -->\n"+
619                                 " <action verb=\"POST\" path=\"/host/register\">\n" +
620                                 "    <blueprint>\n" +
621                                 "      <assembly>dream.tutorial.show-headers</assembly>\n" +
622                                 "      <class>MindTouch.Dream.Tutorial.ShowHeadersService</class>\n" +
623                                 "    </blueprint>\n" +
624                                 "  </action>\n" +
625                                 "\n" +
626                                 "  <!-- instantiate it -->\n" +
627                                 "  <action verb=\"POST\" path=\"/host/start\">\n" +
628                                 "    <config>\n" +
629                                 "      <path>show-headers</path>\n" +
630                                 "      <class>MindTouch.Dream.Tutorial.ShowHeadersService</class>\n" +
631                                 "    </config>\n" +
632                                 "  </action>\n" +
633                                 "</script>";
634                         string s = String.Format ("POST / HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: {0}\r\n\r\n{1}",
635                                                 message.Length, message);  
636                         HttpListener2Test.Send (ns, s);
637                         bool timedout;
638                         string response = HttpListener2Test.ReceiveWithTimeout (ns, 1024, 3000, out timedout);
639                         ns.Close ();
640                         listener.Close ();
641                         Assert.IsFalse (timedout);
642                 }
643
644                 void callback (IAsyncResult ar)
645                 {
646                         HttpListener l = (HttpListener) ar.AsyncState;
647
648                         HttpListenerContext c = l.EndGetContext (ar);
649                         HttpListenerRequest request = c.Request;
650
651                         StreamReader r = new StreamReader (request.InputStream);
652                         string sr =r.ReadToEnd ();
653                         HttpListener2Test.Send (c.Response.OutputStream, "Miguel is love");
654                         c.Response.Close ();
655                 }
656
657                 //
658                 // As it turns out, when we closed the OutputStream,
659                 // we were not shutting down the connection, which was
660                 // a documented pattern to close the connection
661                 // 
662                 [Test]
663                 public void Test_MultipleConnections ()
664                 {
665                         HttpListener listener = HttpListener2Test.CreateAndStartListener ("http://127.0.0.1:9000/multiple/");
666
667                         // First one
668                         NetworkStream ns = HttpListener2Test.CreateNS (9000);
669                         HttpListener2Test.Send (ns, "POST /multiple/ HTTP/1.0\r\nHost: 127.0.0.1\r\nContent-Length: 3\r\n\r\n123");
670                         HttpListenerContext ctx = listener.GetContext ();
671                         HttpListener2Test.Send (ctx.Response.OutputStream, "%%%OK%%%");
672                         ctx.Response.OutputStream.Close ();
673                         string response = HttpListener2Test.Receive (ns, 1024);
674                         ns.Close ();
675
676                         // Second one
677                         ns = HttpListener2Test.CreateNS (9000);
678                         HttpListener2Test.Send (ns, "POST /multiple/ HTTP/1.0\r\nHost: 127.0.0.1\r\nContent-Length: 3\r\n\r\n123");
679                         ctx = listener.GetContext ();
680                         HttpListener2Test.Send (ctx.Response.OutputStream, "%%%OK%%%");
681                         ctx.Response.OutputStream.Close ();
682                         response = HttpListener2Test.Receive (ns, 1024);
683                         ns.Close ();
684                         
685                         listener.Close ();
686                 }
687
688                 //
689                 // Test case for bug 341443, an pretty old bug, filed on November of 2007.
690                 //
691                 [Test]
692                 public void Test_HostInUri ()
693                 {
694                         var wait = new ManualResetEvent (false);
695                         var wait2 = new ManualResetEvent (false);
696                         
697                         Thread t = new Thread (delegate (object a) {
698                                 wait.WaitOne ();
699
700                                 NetworkStream ns = HttpListener2Test.CreateNS (9145);
701                                 HttpListener2Test.Send (ns, "GET http://www.google.com/ HTTP/1.1\r\nHost: www.google.com\r\nContent-Length: 3\r\n\r\n123456");
702
703                                 wait2.WaitOne ();
704                                 ns.Close ();
705                         });
706                         t.Start ();
707                                 
708                         HttpListener listener = HttpListener2Test.CreateAndStartListener ("http://*:9145/");
709                         wait.Set ();
710                         HttpListenerContext ctx = listener.GetContext ();
711                         
712                         Assert.AreEqual ("http://www.google.com:9145/", ctx.Request.Url.ToString ());
713                         Assert.AreEqual ("http://www.google.com/", ctx.Request.RawUrl);
714                         wait2.Set ();
715
716                         listener.Close ();
717                 }
718
719                 [Test] // bug #513849
720                 public void ClosePort ()
721                 {
722                         var h = new HttpListener ();
723                         h.Prefixes.Add ("http://127.0.0.1:30158/");
724                         h.Start ();
725                         h.BeginGetContext (null, null);
726                         h.Stop ();
727                         TcpListener t = new TcpListener (IPAddress.Parse ("127.0.0.1"), 30158);
728                         t.Start ();
729                         t.Stop ();
730                 }
731
732                 //
733                 // Bugs: #17204, #10818
734                 //
735                 // Sadly, on Unix, if there are different calls to bind
736                 // like *:port and host:port that is not an error,
737                 // it would only be an error if host:port is done twice, so
738                 // the best we can hope for is that listening on a specific interface
739                 // does not also listen to another interface.
740                 //
741                 [Test]
742                 public void BindToSingleInterface ()
743                 {
744                         IPAddress [] machineAddress = null;
745
746                         try {
747                                 machineAddress = Dns.GetHostAddresses (Dns.GetHostName ());
748                         } catch (SocketException){
749                                 // The build hosts sometimes can not resolve the hostname
750                                 Assert.Ignore ("Hostname couldn't be resolved.");
751                         }
752                         
753                         int port = 61234;
754                         var h = new HttpListener ();
755                         h.Prefixes.Add ("http://" + machineAddress [0] + ":" + port + "/");
756                         h.Start ();
757
758                         try {
759                                 var c = new TcpClient ("localhost", port);
760                                 Assert.Fail ("The TcpClient should have failed to connect since HttpListener is not listening on localhost");
761                         } catch (SocketException){
762                                 // Pass
763                         }
764                         h.Stop ();
765                 }
766
767                 [Test]
768                 public void BindToAllInterfaces ()
769                 {
770                         var h = new HttpListener ();
771                         int port = 62234;
772                         h.Prefixes.Add ("http://*:" + port + "/");
773                         h.Start ();
774                         var c = new TcpClient ("localhost", port);
775                         h.Stop ();
776                 }
777
778                 // Test case for bug #31209
779                 [Test]
780                 public void Test_EmptyLineAtStart ()
781                 {
782                         var listener = HttpListener2Test.CreateAndStartListener ("http://127.0.0.1:9124/");
783                         var ns = HttpListener2Test.CreateNS (9124);
784
785                         HttpListener2Test.Send (ns, "\r\nGET / HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n");
786
787                         bool timedout;
788                         HttpListener2Test.GetContextWithTimeout (listener, 1000, out timedout);
789
790                         Assert.IsFalse (timedout, "timed out");
791
792                         ns.Close ();
793                         listener.Close ();
794                 }
795         }
796 }