3 // - Unit tests for System.Net.HttpListener
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.
32 using System.Net.Sockets;
33 using System.Threading;
34 using System.Threading.Tasks;
35 using NUnit.Framework;
36 using MonoTests.Helpers;
38 namespace MonoTests.System.Net {
40 [Category ("RequiresBSDSockets")]
41 public class HttpListenerTest {
46 public void SetUp () {
47 port = NetworkHelpers.FindFreePort ();
51 public void DefaultProperties ()
53 HttpListener listener = new HttpListener ();
54 Assert.AreEqual (AuthenticationSchemes.Anonymous, listener.AuthenticationSchemes, "#01");
55 Assert.AreEqual (null, listener.AuthenticationSchemeSelectorDelegate, "#02");
56 Assert.AreEqual (false, listener.IgnoreWriteExceptions, "#03");
57 Assert.AreEqual (false, listener.IsListening, "#03");
58 Assert.AreEqual (0, listener.Prefixes.Count, "#04");
59 Assert.AreEqual (null, listener.Realm, "#05");
60 Assert.AreEqual (false, listener.UnsafeConnectionNtlmAuthentication, "#06");
66 HttpListener listener = new HttpListener ();
73 HttpListener listener = new HttpListener ();
78 [ExpectedException (typeof (InvalidOperationException))]
79 public void GetContext1 ()
81 HttpListener listener = new HttpListener ();
82 // "Please call Start () before calling this method"
83 listener.GetContext ();
87 [ExpectedException (typeof (InvalidOperationException))]
88 public void GetContext2 ()
90 HttpListener listener = new HttpListener ();
92 // "Please call AddPrefix () before calling this method"
93 listener.GetContext ();
97 [ExpectedException (typeof (InvalidOperationException))]
98 public void BeginGetContext1 ()
100 HttpListener listener = new HttpListener ();
101 // "Please call Start () before calling this method"
102 listener.BeginGetContext (null, null);
106 public void BeginGetContext2 ()
108 HttpListener listener = new HttpListener ();
110 // One would expect this to fail as BeginGetContext1 does not fail and
111 // calling EndGetContext will wait forever.
112 // Lame. They should check that we have no prefixes.
113 IAsyncResult ares = listener.BeginGetContext (null, null);
114 Assert.IsFalse (ares.IsCompleted);
117 private bool CanOpenPort(int port)
121 using(Socket socket = new Socket (AddressFamily.InterNetwork,
125 socket.Bind (new IPEndPoint (IPAddress.Loopback, port));
130 //Can be AccessDeniedException(ports 80/443 need root access) or
131 //SocketException because other application is listening
138 public void DefaultHttpPort ()
140 if (!CanOpenPort (80))
141 Assert.Ignore ("Can not open port 80 skipping test.");
142 using(HttpListener listener = new HttpListener ())
144 listener.Prefixes.Add ("http://127.0.0.1/");
146 Assert.IsFalse (CanOpenPort (80), "HttpListener is not listening on port 80.");
151 public void DefaultHttpsPort ()
153 if (!CanOpenPort (443))
154 Assert.Ignore ("Can not open port 443 skipping test.");
155 using(HttpListener listener = new HttpListener ())
157 listener.Prefixes.Add ("https://127.0.0.1/");
159 Assert.IsFalse (CanOpenPort (443), "HttpListener is not listening on port 443.");
164 public void TwoListeners_SameAddress ()
166 if (!CanOpenPort (port))
167 Assert.Ignore ("port");
168 HttpListener listener1 = new HttpListener ();
169 listener1.Prefixes.Add ("http://127.0.0.1:" + port + "/");
170 HttpListener listener2 = new HttpListener ();
171 listener2.Prefixes.Add ("http://127.0.0.1:" + port + "/hola/");
177 [ExpectedException (typeof (HttpListenerException))]
178 public void TwoListeners_SameURL ()
180 if (!CanOpenPort (port))
181 Assert.Ignore ("port");
182 HttpListener listener1 = new HttpListener ();
183 listener1.Prefixes.Add ("http://127.0.0.1:" + port + "/hola/");
184 HttpListener listener2 = new HttpListener ();
185 listener2.Prefixes.Add ("http://127.0.0.1:" + port + "/hola/");
191 [ExpectedException (typeof (HttpListenerException))]
192 public void MultipleSlashes ()
194 if (!CanOpenPort (port))
195 Assert.Ignore ("port");
196 HttpListener listener = new HttpListener ();
197 listener.Prefixes.Add ("http://localhost:" + port + "/hola////");
198 // this one throws on Start(), not when adding it.
203 [ExpectedException (typeof (HttpListenerException))]
204 public void PercentSign ()
206 if (!CanOpenPort (port))
207 Assert.Ignore ("port");
208 HttpListener listener = new HttpListener ();
209 listener.Prefixes.Add ("http://localhost:" + port + "/hola%3E/");
210 // this one throws on Start(), not when adding it.
215 public void CloseBeforeStart ()
217 HttpListener listener = new HttpListener ();
222 public void CloseTwice ()
224 if (!CanOpenPort (port))
225 Assert.Ignore ("port");
226 HttpListener listener = new HttpListener ();
227 listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
234 public void StartStopStart ()
236 if (!CanOpenPort (port))
237 Assert.Ignore ("port");
238 HttpListener listener = new HttpListener ();
239 listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
247 public void StartStopDispose ()
249 if (!CanOpenPort (port))
250 Assert.Ignore ("port");
251 using (HttpListener listener = new HttpListener ()){
252 listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
259 public void AbortBeforeStart ()
261 HttpListener listener = new HttpListener ();
266 public void AbortTwice ()
268 if (!CanOpenPort (port))
269 Assert.Ignore ("port");
270 HttpListener listener = new HttpListener ();
271 listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
278 public void PropertiesWhenClosed1 ()
280 HttpListener listener = new HttpListener ();
282 Assert.AreEqual (AuthenticationSchemes.Anonymous, listener.AuthenticationSchemes, "#01");
283 Assert.AreEqual (null, listener.AuthenticationSchemeSelectorDelegate, "#02");
284 Assert.AreEqual (false, listener.IgnoreWriteExceptions, "#03");
285 Assert.AreEqual (false, listener.IsListening, "#03");
286 Assert.AreEqual (null, listener.Realm, "#05");
287 Assert.AreEqual (false, listener.UnsafeConnectionNtlmAuthentication, "#06");
291 [ExpectedException (typeof (ObjectDisposedException))]
292 public void PropertiesWhenClosed2 ()
294 HttpListener listener = new HttpListener ();
296 HttpListenerPrefixCollection p = listener.Prefixes;
300 [ExpectedException (typeof (ObjectDisposedException))]
301 public void PropertiesWhenClosedSet1 ()
303 HttpListener listener = new HttpListener ();
305 listener.AuthenticationSchemes = AuthenticationSchemes.None;
309 [ExpectedException (typeof (ObjectDisposedException))]
310 public void PropertiesWhenClosedSet2 ()
312 HttpListener listener = new HttpListener ();
314 listener.AuthenticationSchemeSelectorDelegate = null;
318 [ExpectedException (typeof (ObjectDisposedException))]
319 public void PropertiesWhenClosedSet3 ()
321 HttpListener listener = new HttpListener ();
323 listener.IgnoreWriteExceptions = true;
327 [ExpectedException (typeof (ObjectDisposedException))]
328 public void PropertiesWhenClosedSet4 ()
330 HttpListener listener = new HttpListener ();
332 listener.Realm = "hola";
336 [ExpectedException (typeof (ObjectDisposedException))]
337 public void PropertiesWhenClosedSet5 ()
339 HttpListener listener = new HttpListener ();
341 listener.UnsafeConnectionNtlmAuthentication = true;
345 public void PropertiesWhenClosed3 ()
347 HttpListener listener = new HttpListener ();
349 Assert.IsFalse (listener.IsListening);
353 public void CloseWhileBegin ()
355 HttpListener listener = new HttpListener ();
356 listener.Prefixes.Add ("http://127.0.0.1:" + NetworkHelpers.FindFreePort () + "/closewhilebegin/");
358 CallMe cm = new CallMe ();
359 listener.BeginGetContext (cm.Callback, listener);
361 if (false == cm.Event.WaitOne (3000, false))
362 Assert.Fail ("This should not time out.");
363 Assert.IsNotNull (cm.Error);
364 Assert.AreEqual (typeof (ObjectDisposedException), cm.Error.GetType (), "Exception type");
369 public void AbortWhileBegin ()
371 HttpListener listener = new HttpListener ();
372 listener.Prefixes.Add ("http://127.0.0.1:" + NetworkHelpers.FindFreePort () + "/abortwhilebegin/");
374 CallMe cm = new CallMe ();
375 listener.BeginGetContext (cm.Callback, listener);
377 if (false == cm.Event.WaitOne (3000, false))
378 Assert.Fail ("This should not time out.");
379 Assert.IsNotNull (cm.Error);
380 Assert.AreEqual (typeof (ObjectDisposedException), cm.Error.GetType (), "Exception type");
385 [ExpectedException (typeof (HttpListenerException))]
386 public void CloseWhileGet ()
388 // "System.Net.HttpListener Exception : The I/O operation has been aborted
389 // because of either a thread exit or an application request
390 // at System.Net.HttpListener.GetContext()
391 // at MonoTests.System.Net.HttpListenerTest.CloseWhileGet()
393 HttpListener listener = new HttpListener ();
394 listener.Prefixes.Add ("http://127.0.0.1:" + NetworkHelpers.FindFreePort () + "/closewhileget/");
396 RunMe rm = new RunMe (1000, new ThreadStart (listener.Close), new object [0]);
398 HttpListenerContext ctx = listener.GetContext ();
402 [ExpectedException (typeof (HttpListenerException))]
403 public void AbortWhileGet ()
405 // "System.Net.HttpListener Exception : The I/O operation has been aborted
406 // because of either a thread exit or an application request
407 // at System.Net.HttpListener.GetContext()
408 // at MonoTests.System.Net.HttpListenerTest.CloseWhileGet()
410 HttpListener listener = new HttpListener ();
411 listener.Prefixes.Add ("http://127.0.0.1:" + NetworkHelpers.FindFreePort () + "/abortwhileget/");
413 RunMe rm = new RunMe (1000, new ThreadStart (listener.Abort), new object [0]);
415 HttpListenerContext ctx = listener.GetContext ();
422 public object Result;
424 public RunMe (int delay_ms, Delegate d, object [] args)
426 this.delay_ms = delay_ms;
433 Thread th = new Thread (new ThreadStart (Run));
439 Thread.Sleep (delay_ms);
440 Result = d.DynamicInvoke (args);
445 public ManualResetEvent Event = new ManualResetEvent (false);
447 public HttpListenerContext Context;
448 public Exception Error;
458 public void Callback (IAsyncResult ares)
462 Error = new ArgumentNullException ("ares");
467 HttpListener listener = (HttpListener) ares.AsyncState;
468 Context = listener.EndGetContext (ares);
469 } catch (Exception e) {
475 public void Dispose ()
482 public void ConnectionReuse ()
484 var uri = "http://localhost:" + NetworkHelpers.FindFreePort () + "/";
486 HttpListener listener = new HttpListener ();
487 listener.Prefixes.Add (uri);
490 IPEndPoint expectedIpEndPoint = CreateListenerRequest (listener, uri);
492 Assert.AreEqual (expectedIpEndPoint, CreateListenerRequest (listener, uri), "reuse1");
493 Assert.AreEqual (expectedIpEndPoint, CreateListenerRequest (listener, uri), "reuse2");
496 public IPEndPoint CreateListenerRequest (HttpListener listener, string uri)
498 IPEndPoint ipEndPoint = null;
499 var mre = new ManualResetEventSlim ();
500 listener.BeginGetContext (result => {
501 ipEndPoint = ListenerCallback (result);
505 var request = (HttpWebRequest) WebRequest.Create (uri);
506 request.Method = "POST";
508 // We need to write something
509 request.GetRequestStream ().Write (new byte [] {(byte)'a'}, 0, 1);
510 request.GetRequestStream ().Dispose ();
512 // Send request, socket is created or reused.
513 var response = request.GetResponse ();
515 // Close response so socket can be reused.
523 public static IPEndPoint ListenerCallback (IAsyncResult result)
525 var listener = (HttpListener) result.AsyncState;
526 var context = listener.EndGetContext (result);
527 var clientEndPoint = context.Request.RemoteEndPoint;
529 // Disposing InputStream should not avoid socket reuse
530 context.Request.InputStream.Dispose ();
532 // Close OutputStream to send response
533 context.Response.OutputStream.Close ();
535 return clientEndPoint;
539 public void HttpClientIsDisconnectedCheckForWriteException()
541 string uri = "http://localhost:" + NetworkHelpers.FindFreePort () + "/";
543 AutoResetEvent exceptionOccuredEvent = new AutoResetEvent (false);
544 HttpListener listener = new HttpListener {
545 IgnoreWriteExceptions = false
547 listener.Prefixes.Add (uri);
549 listener.BeginGetContext (result =>
551 HttpListenerContext context = listener.EndGetContext (result);
552 context.Response.SendChunked = true;
553 context.Request.InputStream.Close ();
555 var bytes = new byte [1024];
556 using(Stream outputStream = context.Response.OutputStream) {
559 outputStream.Write (bytes, 0, bytes.Length);
561 exceptionOccuredEvent.Set ();
566 Task.Factory.StartNew (() =>
568 var webRequest = (HttpWebRequest)WebRequest.Create (uri);
569 webRequest.Method = "POST";
570 webRequest.KeepAlive = false;
571 Stream requestStream = webRequest.GetRequestStream ();
572 requestStream.WriteByte (1);
573 requestStream.Close ();
574 using (WebResponse response = webRequest.GetResponse ())
575 using (Stream stream = response.GetResponseStream ()) {
576 byte[] clientBytes = new byte [1024];
577 Assert.IsNotNull (stream, "#01");
578 stream.Read (clientBytes, 0, clientBytes.Length);
582 Assert.IsTrue (exceptionOccuredEvent.WaitOne (15 * 1000), "#02");