[sgen] Clear the card table in the finishing pause
[mono.git] / mcs / class / System / Test / System.Net / HttpListenerTest.cs
1 //
2 // HttpListenerTest.cs
3 //      - Unit tests for System.Net.HttpListener
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 using System;
30 using System.IO;
31 using System.Net;
32 using System.Net.Sockets;
33 using System.Threading;
34 using System.Threading.Tasks;
35 using NUnit.Framework;
36 using MonoTests.Helpers;
37
38 namespace MonoTests.System.Net {
39         [TestFixture]
40         public class HttpListenerTest {
41
42                 int port;
43
44                 [SetUp]
45                 public void SetUp () {
46                         port = NetworkHelpers.FindFreePort ();
47                 }
48
49                 [Test]
50                 public void DefaultProperties ()
51                 {
52                         HttpListener listener = new HttpListener ();
53                         Assert.AreEqual (AuthenticationSchemes.Anonymous, listener.AuthenticationSchemes, "#01");
54                         Assert.AreEqual (null, listener.AuthenticationSchemeSelectorDelegate, "#02");
55                         Assert.AreEqual (false, listener.IgnoreWriteExceptions, "#03");
56                         Assert.AreEqual (false, listener.IsListening, "#03");
57                         Assert.AreEqual (0, listener.Prefixes.Count, "#04");
58                         Assert.AreEqual (null, listener.Realm, "#05");
59                         Assert.AreEqual (false, listener.UnsafeConnectionNtlmAuthentication, "#06");
60                 }
61
62                 [Test]
63                 public void Start1 ()
64                 {
65                         HttpListener listener = new HttpListener ();
66                         listener.Start ();
67                 }
68
69                 [Test]
70                 public void Stop1 ()
71                 {
72                         HttpListener listener = new HttpListener ();
73                         listener.Stop ();
74                 }
75
76                 [Test]
77                 [ExpectedException (typeof (InvalidOperationException))]
78                 public void GetContext1 ()
79                 {
80                         HttpListener listener = new HttpListener ();
81                         // "Please call Start () before calling this method"
82                         listener.GetContext ();
83                 }
84
85                 [Test]
86                 [ExpectedException (typeof (InvalidOperationException))]
87                 public void GetContext2 ()
88                 {
89                         HttpListener listener = new HttpListener ();
90                         listener.Start ();
91                         // "Please call AddPrefix () before calling this method"
92                         listener.GetContext ();
93                 }
94
95                 [Test]
96                 [ExpectedException (typeof (InvalidOperationException))]
97                 public void BeginGetContext1 ()
98                 {
99                         HttpListener listener = new HttpListener ();
100                         // "Please call Start () before calling this method"
101                         listener.BeginGetContext (null, null);
102                 }
103
104                 [Test]
105                 public void BeginGetContext2 ()
106                 {
107                         HttpListener listener = new HttpListener ();
108                         listener.Start ();
109                         // One would expect this to fail as BeginGetContext1 does not fail and
110                         // calling EndGetContext will wait forever.
111                         // Lame. They should check that we have no prefixes.
112                         IAsyncResult ares = listener.BeginGetContext (null, null);
113                         Assert.IsFalse (ares.IsCompleted);
114                 }
115
116                 private bool CanOpenPort(int port)
117                 {
118                         try
119                         {
120                                 using(Socket socket = new Socket (AddressFamily.InterNetwork,
121                                         SocketType.Stream,
122                                         ProtocolType.Tcp))
123                                 {
124                                         socket.Bind (new IPEndPoint (IPAddress.Loopback, port));
125                                         socket.Listen(1);
126                                 }
127                         }
128                         catch(Exception) {
129                                 //Can be AccessDeniedException(ports 80/443 need root access) or
130                                 //SocketException because other application is listening
131                                 return false;
132                         }
133                         return true;
134                 }
135
136                 [Test]
137                 public void DefaultHttpPort ()
138                 {
139                         if (!CanOpenPort (80))
140                                 Assert.Ignore ("Can not open port 80 skipping test.");
141                         using(HttpListener listener = new HttpListener ())
142                         {
143                                 listener.Prefixes.Add ("http://127.0.0.1/");
144                                 listener.Start ();
145                                 Assert.IsFalse (CanOpenPort (80), "HttpListener is not listening on port 80.");
146                         }
147                 }
148
149                 [Test]
150                 public void DefaultHttpsPort ()
151                 {
152                         if (!CanOpenPort (443))
153                                 Assert.Ignore ("Can not open port 443 skipping test.");
154                         using(HttpListener listener = new HttpListener ())
155                         {
156                                 listener.Prefixes.Add ("https://127.0.0.1/");
157                                 listener.Start ();
158                                 Assert.IsFalse (CanOpenPort (443), "HttpListener is not listening on port 443.");
159                         }
160                 }
161
162                 [Test]
163                 public void TwoListeners_SameAddress ()
164                 {
165                         if (!CanOpenPort (port))
166                                 Assert.Ignore ("port");
167                         HttpListener listener1 = new HttpListener ();
168                         listener1.Prefixes.Add ("http://127.0.0.1:" + port + "/");
169                         HttpListener listener2 = new HttpListener ();
170                         listener2.Prefixes.Add ("http://127.0.0.1:" + port + "/hola/");
171                         listener1.Start ();
172                         listener2.Start ();
173                 }
174
175                 [Test]
176                 [ExpectedException (typeof (HttpListenerException))]
177                 public void TwoListeners_SameURL ()
178                 {
179                         if (!CanOpenPort (port))
180                                 Assert.Ignore ("port");
181                         HttpListener listener1 = new HttpListener ();
182                         listener1.Prefixes.Add ("http://127.0.0.1:" + port + "/hola/");
183                         HttpListener listener2 = new HttpListener ();
184                         listener2.Prefixes.Add ("http://127.0.0.1:" + port + "/hola/");
185                         listener1.Start ();
186                         listener2.Start ();
187                 }
188
189                 [Test]
190                 [ExpectedException (typeof (HttpListenerException))]
191                 public void MultipleSlashes ()
192                 {
193                         if (!CanOpenPort (port))
194                                 Assert.Ignore ("port");
195                         HttpListener listener = new HttpListener ();
196                         listener.Prefixes.Add ("http://localhost:" + port + "/hola////");
197                         // this one throws on Start(), not when adding it.
198                         listener.Start ();
199                 }
200
201                 [Test]
202                 [ExpectedException (typeof (HttpListenerException))]
203                 public void PercentSign ()
204                 {
205                         if (!CanOpenPort (port))
206                                 Assert.Ignore ("port");
207                         HttpListener listener = new HttpListener ();
208                         listener.Prefixes.Add ("http://localhost:" + port + "/hola%3E/");
209                         // this one throws on Start(), not when adding it.
210                         listener.Start ();
211                 }
212
213                 [Test]
214                 public void CloseBeforeStart ()
215                 {
216                         HttpListener listener = new HttpListener ();
217                         listener.Close ();
218                 }
219
220                 [Test]
221                 public void CloseTwice ()
222                 {
223                         if (!CanOpenPort (port))
224                                 Assert.Ignore ("port");
225                         HttpListener listener = new HttpListener ();
226                         listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
227                         listener.Start ();
228                         listener.Close ();
229                         listener.Close ();
230                 }
231
232                 [Test]
233                 public void StartStopStart ()
234                 {
235                         if (!CanOpenPort (port))
236                                 Assert.Ignore ("port");
237                         HttpListener listener = new HttpListener ();
238                         listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
239                         listener.Start ();
240                         listener.Stop ();
241                         listener.Start ();
242                         listener.Close ();
243                 }
244
245                 [Test]
246                 public void StartStopDispose ()
247                 {
248                         if (!CanOpenPort (port))
249                                 Assert.Ignore ("port");
250                         using (HttpListener listener = new HttpListener ()){
251                                 listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
252                                 listener.Start ();
253                                 listener.Stop ();
254                         }
255                 }
256                 
257                 [Test]
258                 public void AbortBeforeStart ()
259                 {
260                         HttpListener listener = new HttpListener ();
261                         listener.Abort ();
262                 }
263
264                 [Test]
265                 public void AbortTwice ()
266                 {
267                         if (!CanOpenPort (port))
268                                 Assert.Ignore ("port");
269                         HttpListener listener = new HttpListener ();
270                         listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
271                         listener.Start ();
272                         listener.Abort ();
273                         listener.Abort ();
274                 }
275
276                 [Test]
277                 public void PropertiesWhenClosed1 ()
278                 {
279                         HttpListener listener = new HttpListener ();
280                         listener.Close ();
281                         Assert.AreEqual (AuthenticationSchemes.Anonymous, listener.AuthenticationSchemes, "#01");
282                         Assert.AreEqual (null, listener.AuthenticationSchemeSelectorDelegate, "#02");
283                         Assert.AreEqual (false, listener.IgnoreWriteExceptions, "#03");
284                         Assert.AreEqual (false, listener.IsListening, "#03");
285                         Assert.AreEqual (null, listener.Realm, "#05");
286                         Assert.AreEqual (false, listener.UnsafeConnectionNtlmAuthentication, "#06");
287                 }
288
289                 [Test]
290                 [ExpectedException (typeof (ObjectDisposedException))]
291                 public void PropertiesWhenClosed2 ()
292                 {
293                         HttpListener listener = new HttpListener ();
294                         listener.Close ();
295                         HttpListenerPrefixCollection p = listener.Prefixes;
296                 }
297
298                 [Test]
299                 [ExpectedException (typeof (ObjectDisposedException))]
300                 public void PropertiesWhenClosedSet1 ()
301                 {
302                         HttpListener listener = new HttpListener ();
303                         listener.Close ();
304                         listener.AuthenticationSchemes = AuthenticationSchemes.None;
305                 }
306
307                 [Test]
308                 [ExpectedException (typeof (ObjectDisposedException))]
309                 public void PropertiesWhenClosedSet2 ()
310                 {
311                         HttpListener listener = new HttpListener ();
312                         listener.Close ();
313                         listener.AuthenticationSchemeSelectorDelegate = null;
314                 }
315
316                 [Test]
317                 [ExpectedException (typeof (ObjectDisposedException))]
318                 public void PropertiesWhenClosedSet3 ()
319                 {
320                         HttpListener listener = new HttpListener ();
321                         listener.Close ();
322                         listener.IgnoreWriteExceptions = true;
323                 }
324
325                 [Test]
326                 [ExpectedException (typeof (ObjectDisposedException))]
327                 public void PropertiesWhenClosedSet4 ()
328                 {
329                         HttpListener listener = new HttpListener ();
330                         listener.Close ();
331                         listener.Realm = "hola";
332                 }
333
334                 [Test]
335                 [ExpectedException (typeof (ObjectDisposedException))]
336                 public void PropertiesWhenClosedSet5 ()
337                 {
338                         HttpListener listener = new HttpListener ();
339                         listener.Close ();
340                         listener.UnsafeConnectionNtlmAuthentication = true;
341                 }
342
343                 [Test]
344                 public void PropertiesWhenClosed3 ()
345                 {
346                         HttpListener listener = new HttpListener ();
347                         listener.Close ();
348                         Assert.IsFalse (listener.IsListening);
349                 }
350
351                 [Test]
352                 public void CloseWhileBegin ()
353                 {
354                         HttpListener listener = new HttpListener ();
355                         listener.Prefixes.Add ("http://127.0.0.1:9001/closewhilebegin/");
356                         listener.Start ();
357                         CallMe cm = new CallMe ();
358                         listener.BeginGetContext (cm.Callback, listener);
359                         listener.Close ();
360                         if (false == cm.Event.WaitOne (3000, false))
361                                 Assert.Fail ("This should not time out.");
362                         Assert.IsNotNull (cm.Error);
363                         Assert.AreEqual (typeof (ObjectDisposedException), cm.Error.GetType (), "Exception type");
364                         cm.Dispose ();
365                 }
366
367                 [Test]
368                 public void AbortWhileBegin ()
369                 {
370                         HttpListener listener = new HttpListener ();
371                         listener.Prefixes.Add ("http://127.0.0.1:9001/abortwhilebegin/");
372                         listener.Start ();
373                         CallMe cm = new CallMe ();
374                         listener.BeginGetContext (cm.Callback, listener);
375                         listener.Abort ();
376                         if (false == cm.Event.WaitOne (3000, false))
377                                 Assert.Fail ("This should not time out.");
378                         Assert.IsNotNull (cm.Error);
379                         Assert.AreEqual (typeof (ObjectDisposedException), cm.Error.GetType (), "Exception type");
380                         cm.Dispose ();
381                 }
382
383                 [Test]
384                 [ExpectedException (typeof (HttpListenerException))]
385                 public void CloseWhileGet ()
386                 {
387                         // "System.Net.HttpListener Exception : The I/O operation has been aborted
388                         // because of either a thread exit or an application request
389                         //   at System.Net.HttpListener.GetContext()
390                         //   at MonoTests.System.Net.HttpListenerTest.CloseWhileGet()
391
392                         HttpListener listener = new HttpListener ();
393                         listener.Prefixes.Add ("http://127.0.0.1:9001/closewhileget/");
394                         listener.Start ();
395                         RunMe rm = new RunMe (1000, new ThreadStart (listener.Close), new object [0]);
396                         rm.Start ();
397                         HttpListenerContext ctx = listener.GetContext ();
398                 }
399
400                 [Test]
401                 [ExpectedException (typeof (HttpListenerException))]
402                 public void AbortWhileGet ()
403                 {
404                         // "System.Net.HttpListener Exception : The I/O operation has been aborted
405                         // because of either a thread exit or an application request
406                         //   at System.Net.HttpListener.GetContext()
407                         //   at MonoTests.System.Net.HttpListenerTest.CloseWhileGet()
408
409                         HttpListener listener = new HttpListener ();
410                         listener.Prefixes.Add ("http://127.0.0.1:9001/abortwhileget/");
411                         listener.Start ();
412                         RunMe rm = new RunMe (1000, new ThreadStart (listener.Abort), new object [0]);
413                         rm.Start ();
414                         HttpListenerContext ctx = listener.GetContext ();
415                 }
416
417                 class RunMe {
418                         Delegate d;
419                         int delay_ms;
420                         object [] args;
421                         public object Result;
422
423                         public RunMe (int delay_ms, Delegate d, object [] args)
424                         {
425                                 this.delay_ms = delay_ms;
426                                 this.d = d;
427                                 this.args = args;
428                         }
429
430                         public void Start ()
431                         {
432                                 Thread th = new Thread (new ThreadStart (Run));
433                                 th.Start ();
434                         }
435
436                         void Run ()
437                         {
438                                 Thread.Sleep (delay_ms);
439                                 Result = d.DynamicInvoke (args);
440                         }
441                 }
442
443                 class CallMe {
444                         public ManualResetEvent Event = new ManualResetEvent (false);
445                         public bool Called;
446                         public HttpListenerContext Context;
447                         public Exception Error;
448
449                         public void Reset ()
450                         {
451                                 Called = false;
452                                 Context = null;
453                                 Error = null;
454                                 Event.Reset ();
455                         }
456
457                         public void Callback (IAsyncResult ares)
458                         {
459                                 Called = true;
460                                 if (ares == null) {
461                                         Error = new ArgumentNullException ("ares");
462                                         return;
463                                 }
464                                 
465                                 try {
466                                         HttpListener listener = (HttpListener) ares.AsyncState;
467                                         Context = listener.EndGetContext (ares);
468                                 } catch (Exception e) {
469                                         Error = e;
470                                 }
471                                 Event.Set ();
472                         }
473
474                         public void Dispose ()
475                         {
476                                 Event.Close ();
477                         }
478                 }
479
480                 [Test]
481                 public void ConnectionReuse ()
482                 {
483                         var uri = "http://localhost:" + NetworkHelpers.FindFreePort () + "/";
484
485                         HttpListener listener = new HttpListener ();
486                         listener.Prefixes.Add (uri);
487                         listener.Start ();
488
489                         IPEndPoint expectedIpEndPoint = CreateListenerRequest (listener, uri);
490
491                         Assert.AreEqual (expectedIpEndPoint, CreateListenerRequest (listener, uri), "reuse1");
492                         Assert.AreEqual (expectedIpEndPoint, CreateListenerRequest (listener, uri), "reuse2");
493                 }
494
495                 public IPEndPoint CreateListenerRequest (HttpListener listener, string uri)
496                 {
497                         IPEndPoint ipEndPoint = null;
498                         var mre = new ManualResetEventSlim ();
499                         listener.BeginGetContext (result => {
500                                 ipEndPoint = ListenerCallback (result);
501                                 mre.Set ();
502                         }, listener);
503
504                         var request = (HttpWebRequest) WebRequest.Create (uri);
505                         request.Method = "POST";
506
507                         // We need to write something
508                         request.GetRequestStream ().Write (new byte [] {(byte)'a'}, 0, 1);
509                         request.GetRequestStream ().Dispose ();
510
511                         // Send request, socket is created or reused.
512                         var response = request.GetResponse ();
513
514                         // Close response so socket can be reused.
515                         response.Close ();
516
517                         mre.Wait ();
518
519                         return ipEndPoint;
520                 }
521
522                 public static IPEndPoint ListenerCallback (IAsyncResult result)
523                 {
524                         var listener = (HttpListener) result.AsyncState;
525                         var context = listener.EndGetContext (result);
526                         var clientEndPoint = context.Request.RemoteEndPoint;
527
528                         // Disposing InputStream should not avoid socket reuse
529                         context.Request.InputStream.Dispose ();
530
531                         // Close OutputStream to send response
532                         context.Response.OutputStream.Close ();
533
534                         return clientEndPoint;
535                 }
536                 
537                 [Test]
538                 public void HttpClientIsDisconnectedCheckForWriteException()
539                 {
540                         string uri = "http://localhost:" + NetworkHelpers.FindFreePort () + "/";
541
542                         AutoResetEvent exceptionOccuredEvent = new AutoResetEvent (false);
543                         HttpListener listener = new HttpListener {
544                                 IgnoreWriteExceptions = false
545                         };
546                         listener.Prefixes.Add (uri);
547                         listener.Start ();
548                         listener.BeginGetContext (result =>
549                         {
550                                 HttpListenerContext context = listener.EndGetContext (result);
551                                 context.Response.SendChunked = true;
552                                 context.Request.InputStream.Close ();
553                                 
554                                 var bytes = new byte [1024];
555                                 using(Stream outputStream = context.Response.OutputStream) {
556                                         try {
557                                                 while (true) 
558                                                         outputStream.Write (bytes, 0, bytes.Length);
559                                         } catch {
560                                                 exceptionOccuredEvent.Set ();
561                                         }
562                                 }
563                         }, null);
564
565                         Task.Factory.StartNew (() =>
566                         {
567                                 var webRequest = (HttpWebRequest)WebRequest.Create (uri);
568                                 webRequest.Method = "POST";
569                                 webRequest.KeepAlive = false;
570                                 Stream requestStream = webRequest.GetRequestStream ();
571                                 requestStream.WriteByte (1);
572                                 requestStream.Close ();
573                                 using (WebResponse response = webRequest.GetResponse ())
574                                 using (Stream stream = response.GetResponseStream ()) {
575                                         byte[] clientBytes = new byte [1024];
576                                         Assert.IsNotNull (stream, "#01");
577                                         stream.Read (clientBytes, 0, clientBytes.Length);
578                                 }
579                         });
580
581                         Assert.IsTrue (exceptionOccuredEvent.WaitOne (15 * 1000), "#02");
582                 }
583         }
584 }
585