2 // HttpListener2Test.cs
3 // - Unit tests for System.Net.HttpListener - connection testing
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
31 using System.Collections.Generic;
34 using System.Net.Sockets;
36 using System.Threading;
37 using NUnit.Framework;
39 // ***************************************************************************************
40 // NOTE: when adding prefixes, make then unique per test, as MS might take 'some time' to
41 // unregister it even after explicitly closing the listener.
42 // ***************************************************************************************
43 namespace MonoTests.System.Net {
46 [Ignore ("The class HttpListener is not supported")]
48 public class HttpListener2Test {
49 public class MyNetworkStream : NetworkStream {
50 public MyNetworkStream (Socket sock) : base (sock, true)
54 public Socket GetSocket ()
60 public static HttpListener CreateAndStartListener (string prefix)
62 HttpListener listener = new HttpListener ();
63 listener.Prefixes.Add (prefix);
68 public static HttpListener CreateAndStartListener (string prefix, AuthenticationSchemes authSchemes)
70 HttpListener listener = new HttpListener ();
71 listener.AuthenticationSchemes = authSchemes;
72 listener.Prefixes.Add (prefix);
77 public static MyNetworkStream CreateNS (int port)
79 Socket sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
80 sock.Connect (new IPEndPoint (IPAddress.Loopback, port));
81 return new MyNetworkStream (sock);
84 public static void Send (Stream stream, string str)
86 byte [] bytes = Encoding.ASCII.GetBytes (str);
87 stream.Write (bytes, 0, bytes.Length);
90 public static string Receive (Stream stream, int size)
92 byte [] bytes = new byte [size];
93 int nread = stream.Read (bytes, 0, size);
94 return Encoding.ASCII.GetString (bytes, 0, nread);
97 public static string ReceiveWithTimeout (Stream stream, int size, int timeout, out bool timed_out)
99 byte [] bytes = new byte [size];
100 IAsyncResult ares = stream.BeginRead (bytes, 0, size, null, null);
101 timed_out = !ares.AsyncWaitHandle.WaitOne (timeout, false);
104 int nread = stream.EndRead (ares);
105 return Encoding.ASCII.GetString (bytes, 0, nread);
108 public static HttpListenerContext GetContextWithTimeout (HttpListener listener, int timeout, out bool timed_out)
110 IAsyncResult ares = listener.BeginGetContext (null, null);
111 timed_out = !ares.AsyncWaitHandle.WaitOne (timeout, false);
114 return listener.EndGetContext (ares);
120 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test1/");
121 NetworkStream ns = CreateNS (9000);
122 Send (ns, "GET / HTTP/1.1\r\n\r\n"); // No host
123 string response = Receive (ns, 512);
126 Assert.IsTrue (response.StartsWith ("HTTP/1.1 400"));
132 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test2/");
133 NetworkStream ns = CreateNS (9000);
134 Send (ns, "GET / HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n"); // no prefix
135 string response = Receive (ns, 512);
138 Assert.IsTrue (response.StartsWith ("HTTP/1.1 400"));
144 StringBuilder bad = new StringBuilder ();
145 for (int i = 0; i < 33; i++){
147 bad.Append ((char) i);
167 foreach (char b in bad.ToString ()){
168 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test3/");
169 NetworkStream ns = CreateNS (9000);
170 Send (ns, String.Format ("MA{0} / HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n", b)); // bad method
172 string response = Receive (ns, 512);
175 Assert.AreEqual (true, response.StartsWith ("HTTP/1.1 400"), String.Format ("Failed on {0}", (int) b));
182 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test4/");
183 NetworkStream ns = CreateNS (9000);
184 Send (ns, "POST /test4/ HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n"); // length required
185 string response = Receive (ns, 512);
188 Assert.IsTrue (response.StartsWith ("HTTP/1.1 411"));
194 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test5/");
195 NetworkStream ns = CreateNS (9000);
196 Send (ns, "POST / HTTP/1.1\r\nHost: 127.0.0.1\r\nTransfer-Encoding: pepe\r\n\r\n"); // not implemented
197 string response = Receive (ns, 512);
200 Assert.IsTrue (response.StartsWith ("HTTP/1.1 501"));
206 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test6/");
207 NetworkStream ns = CreateNS (9000);
208 // not implemented! This is against the RFC. Should be a bad request/length required
209 Send (ns, "POST /test6/ HTTP/1.1\r\nHost: 127.0.0.1\r\nTransfer-Encoding: identity\r\n\r\n");
210 string response = Receive (ns, 512);
213 Assert.IsTrue (response.StartsWith ("HTTP/1.1 501"));
219 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test7/");
220 NetworkStream ns = CreateNS (9000);
221 Send (ns, "POST /test7/ HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: 3\r\n\r\n123");
222 HttpListenerContext ctx = listener.GetContext ();
223 Send (ctx.Response.OutputStream, "%%%OK%%%");
224 string response = Receive (ns, 1024);
227 Assert.IsTrue (response.StartsWith ("HTTP/1.1 200"));
228 Assert.IsTrue (-1 != response.IndexOf ("Transfer-Encoding: chunked"));
234 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test8/");
235 NetworkStream ns = CreateNS (9000);
236 // Just like Test7, but 1.0
237 Send (ns, "POST /test8/ HTTP/1.0\r\nHost: 127.0.0.1\r\nContent-Length: 3\r\n\r\n123");
238 HttpListenerContext ctx = listener.GetContext ();
239 Send (ctx.Response.OutputStream, "%%%OK%%%");
240 string response = Receive (ns, 512);
243 Assert.IsTrue (response.StartsWith ("HTTP/1.1 200"));
244 Assert.IsTrue (-1 == response.IndexOf ("Transfer-Encoding: chunked"));
250 // 1.0 + "Transfer-Encoding: chunked"
251 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test9/");
252 NetworkStream ns = CreateNS (9000);
253 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");
255 string response = ReceiveWithTimeout (ns, 512, 1000, out timeout);
256 Assert.IsFalse (timeout);
259 Assert.IsTrue (response.StartsWith ("HTTP/1.1 411"));
263 public void Test10 ()
265 // Same as Test9, but now we shutdown the socket for sending.
266 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test10/");
267 MyNetworkStream ns = CreateNS (9000);
268 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");
269 ns.GetSocket ().Shutdown (SocketShutdown.Send);
271 string response = ReceiveWithTimeout (ns, 512, 1000, out timeout);
272 Assert.IsFalse (timeout);
275 Assert.IsTrue (response.StartsWith ("HTTP/1.1 411"));
279 public void Test11 ()
282 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test11/");
283 MyNetworkStream ns = CreateNS (9000);
284 Send (ns, "POST /test11/ HTTP/0.9\r\nHost: 127.0.0.1\r\n\r\n123");
285 ns.GetSocket ().Shutdown (SocketShutdown.Send);
286 string input = Receive (ns, 512);
289 Assert.IsTrue (input.StartsWith ("HTTP/1.1 400"));
293 public void Test12 ()
296 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test12/");
297 MyNetworkStream ns = CreateNS (9000);
298 Send (ns, "POST /test12/ HTTP/0.9\r\nHost: 127.0.0.1\r\nContent-Length: 3\r\n\r\n123");
299 ns.GetSocket ().Shutdown (SocketShutdown.Send);
300 string input = Receive (ns, 512);
303 Assert.IsTrue (input.StartsWith ("HTTP/1.1 400"));
307 public void Test13 ()
310 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test13/");
311 MyNetworkStream ns = CreateNS (9000);
312 Send (ns, "GEt /test13/ HTTP/0.9\r\nHost: 127.0.0.1\r\n\r\n");
313 ns.GetSocket ().Shutdown (SocketShutdown.Send);
314 string input = Receive (ns, 512);
317 Assert.IsTrue (input.StartsWith ("HTTP/1.1 400"));
320 HttpListenerRequest test14_request;
321 ManualResetEvent test_evt;
324 public void Test14 ()
326 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test14/");
327 MyNetworkStream ns = CreateNS (9000);
328 Send (ns, "POST /test14/ HTTP/1.0\r\nHost: 127.0.0.1\r\nContent-Length: 3\r\n\r\n123");
329 HttpListenerContext c = listener.GetContext ();
330 test14_request = c.Request;
331 test_evt = new ManualResetEvent (false);
332 Thread thread = new Thread (ReadToEnd);
334 if (test_evt.WaitOne (3000, false) == false) {
337 Assert.IsTrue (false, "Timed out");
340 Assert.AreEqual ("123", read_to_end, "Did not get the expected input.");
348 using (StreamReader r = new StreamReader (test14_request.InputStream)) {
349 read_to_end = r.ReadToEnd ();
355 public void Test15 ()
357 // 2 separate writes -> 2 packets. Body size > 8kB
358 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test15/");
359 MyNetworkStream ns = CreateNS (9000);
360 Send (ns, "POST /test15/ HTTP/1.0\r\nHost: 127.0.0.1\r\nContent-Length: 8888\r\n\r\n");
362 string data = new string ('a', 8888);
364 HttpListenerContext c = listener.GetContext ();
365 HttpListenerRequest req = c.Request;
366 using (StreamReader r = new StreamReader (req.InputStream)) {
367 read_to_end = r.ReadToEnd ();
369 Assert.AreEqual (read_to_end.Length, data.Length, "Wrong length");
370 Assert.IsTrue (data == read_to_end, "Wrong data");
376 public void Test16 ()
378 // 1 single write with headers + body (size > 8kB)
379 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test16/");
380 MyNetworkStream ns = CreateNS (9000);
381 StringBuilder sb = new StringBuilder ();
382 sb.Append ("POST /test16/ HTTP/1.0\r\nHost: 127.0.0.1\r\nContent-Length: 8888\r\n\r\n");
383 string eights = new string ('b', 8888);
385 string data = sb.ToString ();
387 HttpListenerContext c = listener.GetContext ();
388 HttpListenerRequest req = c.Request;
389 using (StreamReader r = new StreamReader (req.InputStream)) {
390 read_to_end = r.ReadToEnd ();
392 Assert.AreEqual (read_to_end.Length, read_to_end.Length, "Wrong length");
393 Assert.IsTrue (eights == read_to_end, "Wrong data");
399 public void Test17 ()
401 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/test17/");
402 NetworkStream ns = CreateNS (9000);
403 Send (ns, "RANDOM /test17/ HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: 3\r\n\r\n123");
404 HttpListenerContext ctx = listener.GetContext ();
405 Send (ctx.Response.OutputStream, "%%%OK%%%");
406 string response = Receive (ns, 1024);
409 Assert.IsTrue (response.StartsWith ("HTTP/1.1 200"));
410 Assert.IsTrue (-1 != response.IndexOf ("Transfer-Encoding: chunked"));
415 NetworkStream ns = CreateNS (9000);
416 Send (ns, "GET /SendCookie/ HTTP/1.1\r\nHost: 127.0.0.1\r\n"+
417 "Cookie:$Version=\"1\"; "+
418 "Cookie1=Value1; $Path=\"/\"; "+
419 "CookieM=ValueM; $Path=\"/p2\"; $Domain=\"test\"; $Port=\"99\";"+
420 "Cookie2=Value2; $Path=\"/foo\";"+
429 public void ReceiveCookiesFromClient ()
431 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/SendCookie/");
432 Thread clientThread = new Thread (new ThreadStart (SendCookie));
433 clientThread.Start ();
435 HttpListenerContext context = listener.GetContext();
436 HttpListenerRequest request = context.Request;
438 Assert.AreEqual (3, request.Cookies.Count, "#1");
439 foreach (Cookie c in request.Cookies) {
440 if (c.Name == "Cookie1") {
441 Assert.AreEqual ("Value1", c.Value, "#2");
442 Assert.AreEqual ("\"/\"", c.Path, "#3");
443 Assert.AreEqual (0, c.Port.Length, "#4");
444 Assert.AreEqual (0, c.Domain.Length, "#5");
445 } else if (c.Name == "CookieM") {
446 Assert.AreEqual ("ValueM", c.Value, "#6");
447 Assert.AreEqual ("\"/p2\"", c.Path, "#7");
448 Assert.AreEqual ("\"99\"", c.Port, "#8");
449 Assert.AreEqual ("\"test\"", c.Domain, "#9");
450 } else if (c.Name == "Cookie2") {
451 Assert.AreEqual ("Value2", c.Value, "#10");
452 Assert.AreEqual ("\"/foo\"", c.Path, "#11");
453 Assert.AreEqual (0, c.Port.Length, "#12");
454 Assert.AreEqual (0, c.Domain.Length, "#13");
456 Assert.Fail ("Invalid cookie name " + c.Name);
462 private object _lock = new Object();
463 private string cookieResponse;
465 void ReceiveCookie () {
467 NetworkStream ns = CreateNS (9000);
468 Send (ns, "GET /ReceiveCookie/ HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n");
469 cookieResponse = Receive (ns, 512);
474 public void SendCookiestoClient ()
476 HttpListener listener = CreateAndStartListener ("http://127.0.0.1:9000/ReceiveCookie/");
477 Thread clientThread = new Thread (new ThreadStart (ReceiveCookie));
478 clientThread.Start ();
480 HttpListenerContext context = listener.GetContext();
481 HttpListenerRequest request = context.Request;
482 HttpListenerResponse response = context.Response;
484 Cookie cookie = new Cookie ();
485 cookie.Name = "Name0";
486 cookie.Value = "Value0";
487 cookie.Domain = "blue";
488 cookie.Path = "/path/";
489 cookie.Port = "\"80\"";
491 response.Cookies.Add (cookie);
493 string responseString = "<HTML><BODY>----</BODY></HTML>";
494 byte[] buffer = Encoding.UTF8.GetBytes(responseString);
495 response.ContentLength64 = buffer.Length;
496 Stream output = response.OutputStream;
497 output.Write(buffer, 0, buffer.Length);
502 bool foundCookie = false;
503 foreach (String str in cookieResponse.Split ('\n')) {
504 if (!str.StartsWith ("Set-Cookie2"))
506 Dictionary<string, String> dic = new Dictionary<string, String>();
507 foreach (String p in str.Substring (str.IndexOf (":") + 1).Split (';')) {
508 String[] parts = p.Split('=');
509 dic.Add (parts [0].Trim (), parts [1].Trim ());
511 Assert.AreEqual ("Value0", dic ["Name0"], "#1");
512 Assert.AreEqual ("blue", dic ["Domain"], "#2");
513 Assert.AreEqual ("\"/path/\"", dic ["Path"], "#3");
514 Assert.AreEqual ("\"80\"", dic ["Port"], "#4");
515 Assert.AreEqual ("1", dic ["Version"], "#5");
519 Assert.IsTrue (foundCookie, "#6");
527 public class HttpListenerBugs {
529 public void TestNonChunkedAsync ()
531 HttpListener listener = HttpListener2Test.CreateAndStartListener ("http://127.0.0.1:9123/");
533 listener.BeginGetContext (callback, listener);
535 HttpListener2Test.MyNetworkStream ns = HttpListener2Test.CreateNS (9123);
536 string message = "<script>\n"+
537 " <!-- register the blueprint for our show-headers service -->\n"+
538 " <action verb=\"POST\" path=\"/host/register\">\n" +
540 " <assembly>dream.tutorial.show-headers</assembly>\n" +
541 " <class>MindTouch.Dream.Tutorial.ShowHeadersService</class>\n" +
545 " <!-- instantiate it -->\n" +
546 " <action verb=\"POST\" path=\"/host/start\">\n" +
548 " <path>show-headers</path>\n" +
549 " <class>MindTouch.Dream.Tutorial.ShowHeadersService</class>\n" +
553 string s = String.Format ("POST / HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Length: {0}\r\n\r\n{1}",
554 message.Length, message);
555 HttpListener2Test.Send (ns, s);
557 string response = HttpListener2Test.ReceiveWithTimeout (ns, 1024, 3000, out timedout);
559 Assert.IsFalse (timedout);
562 void callback (IAsyncResult ar)
564 HttpListener l = (HttpListener) ar.AsyncState;
566 HttpListenerContext c = l.EndGetContext (ar);
567 HttpListenerRequest request = c.Request;
569 StreamReader r = new StreamReader (request.InputStream);
570 string sr =r.ReadToEnd ();
571 HttpListener2Test.Send (c.Response.OutputStream, "Miguel is love");