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 public class HttpListenerTest {
45 public void SetUp () {
46 port = NetworkHelpers.FindFreePort ();
50 public void DefaultProperties ()
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");
65 HttpListener listener = new HttpListener ();
72 HttpListener listener = new HttpListener ();
77 [ExpectedException (typeof (InvalidOperationException))]
78 public void GetContext1 ()
80 HttpListener listener = new HttpListener ();
81 // "Please call Start () before calling this method"
82 listener.GetContext ();
86 [ExpectedException (typeof (InvalidOperationException))]
87 public void GetContext2 ()
89 HttpListener listener = new HttpListener ();
91 // "Please call AddPrefix () before calling this method"
92 listener.GetContext ();
96 [ExpectedException (typeof (InvalidOperationException))]
97 public void BeginGetContext1 ()
99 HttpListener listener = new HttpListener ();
100 // "Please call Start () before calling this method"
101 listener.BeginGetContext (null, null);
105 public void BeginGetContext2 ()
107 HttpListener listener = new HttpListener ();
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);
116 private bool CanOpenPort(int port)
120 using(Socket socket = new Socket (AddressFamily.InterNetwork,
124 socket.Bind (new IPEndPoint (IPAddress.Loopback, port));
129 //Can be AccessDeniedException(ports 80/443 need root access) or
130 //SocketException because other application is listening
137 public void DefaultHttpPort ()
139 if (!CanOpenPort (80))
140 Assert.Ignore ("Can not open port 80 skipping test.");
141 using(HttpListener listener = new HttpListener ())
143 listener.Prefixes.Add ("http://127.0.0.1/");
145 Assert.IsFalse (CanOpenPort (80), "HttpListener is not listening on port 80.");
150 public void DefaultHttpsPort ()
152 if (!CanOpenPort (443))
153 Assert.Ignore ("Can not open port 443 skipping test.");
154 using(HttpListener listener = new HttpListener ())
156 listener.Prefixes.Add ("https://127.0.0.1/");
158 Assert.IsFalse (CanOpenPort (443), "HttpListener is not listening on port 443.");
163 public void TwoListeners_SameAddress ()
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/");
176 [ExpectedException (typeof (HttpListenerException))]
177 public void TwoListeners_SameURL ()
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/");
190 [ExpectedException (typeof (HttpListenerException))]
191 public void MultipleSlashes ()
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.
202 [ExpectedException (typeof (HttpListenerException))]
203 public void PercentSign ()
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.
214 public void CloseBeforeStart ()
216 HttpListener listener = new HttpListener ();
221 public void CloseTwice ()
223 if (!CanOpenPort (port))
224 Assert.Ignore ("port");
225 HttpListener listener = new HttpListener ();
226 listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
233 public void StartStopStart ()
235 if (!CanOpenPort (port))
236 Assert.Ignore ("port");
237 HttpListener listener = new HttpListener ();
238 listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
246 public void StartStopDispose ()
248 if (!CanOpenPort (port))
249 Assert.Ignore ("port");
250 using (HttpListener listener = new HttpListener ()){
251 listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
258 public void AbortBeforeStart ()
260 HttpListener listener = new HttpListener ();
265 public void AbortTwice ()
267 if (!CanOpenPort (port))
268 Assert.Ignore ("port");
269 HttpListener listener = new HttpListener ();
270 listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
277 public void PropertiesWhenClosed1 ()
279 HttpListener listener = new HttpListener ();
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");
290 [ExpectedException (typeof (ObjectDisposedException))]
291 public void PropertiesWhenClosed2 ()
293 HttpListener listener = new HttpListener ();
295 HttpListenerPrefixCollection p = listener.Prefixes;
299 [ExpectedException (typeof (ObjectDisposedException))]
300 public void PropertiesWhenClosedSet1 ()
302 HttpListener listener = new HttpListener ();
304 listener.AuthenticationSchemes = AuthenticationSchemes.None;
308 [ExpectedException (typeof (ObjectDisposedException))]
309 public void PropertiesWhenClosedSet2 ()
311 HttpListener listener = new HttpListener ();
313 listener.AuthenticationSchemeSelectorDelegate = null;
317 [ExpectedException (typeof (ObjectDisposedException))]
318 public void PropertiesWhenClosedSet3 ()
320 HttpListener listener = new HttpListener ();
322 listener.IgnoreWriteExceptions = true;
326 [ExpectedException (typeof (ObjectDisposedException))]
327 public void PropertiesWhenClosedSet4 ()
329 HttpListener listener = new HttpListener ();
331 listener.Realm = "hola";
335 [ExpectedException (typeof (ObjectDisposedException))]
336 public void PropertiesWhenClosedSet5 ()
338 HttpListener listener = new HttpListener ();
340 listener.UnsafeConnectionNtlmAuthentication = true;
344 public void PropertiesWhenClosed3 ()
346 HttpListener listener = new HttpListener ();
348 Assert.IsFalse (listener.IsListening);
352 public void CloseWhileBegin ()
354 HttpListener listener = new HttpListener ();
355 listener.Prefixes.Add ("http://127.0.0.1:9001/closewhilebegin/");
357 CallMe cm = new CallMe ();
358 listener.BeginGetContext (cm.Callback, listener);
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");
368 public void AbortWhileBegin ()
370 HttpListener listener = new HttpListener ();
371 listener.Prefixes.Add ("http://127.0.0.1:9001/abortwhilebegin/");
373 CallMe cm = new CallMe ();
374 listener.BeginGetContext (cm.Callback, listener);
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");
384 [ExpectedException (typeof (HttpListenerException))]
385 public void CloseWhileGet ()
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()
392 HttpListener listener = new HttpListener ();
393 listener.Prefixes.Add ("http://127.0.0.1:9001/closewhileget/");
395 RunMe rm = new RunMe (1000, new ThreadStart (listener.Close), new object [0]);
397 HttpListenerContext ctx = listener.GetContext ();
401 [ExpectedException (typeof (HttpListenerException))]
402 public void AbortWhileGet ()
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()
409 HttpListener listener = new HttpListener ();
410 listener.Prefixes.Add ("http://127.0.0.1:9001/abortwhileget/");
412 RunMe rm = new RunMe (1000, new ThreadStart (listener.Abort), new object [0]);
414 HttpListenerContext ctx = listener.GetContext ();
421 public object Result;
423 public RunMe (int delay_ms, Delegate d, object [] args)
425 this.delay_ms = delay_ms;
432 Thread th = new Thread (new ThreadStart (Run));
438 Thread.Sleep (delay_ms);
439 Result = d.DynamicInvoke (args);
444 public ManualResetEvent Event = new ManualResetEvent (false);
446 public HttpListenerContext Context;
447 public Exception Error;
457 public void Callback (IAsyncResult ares)
461 Error = new ArgumentNullException ("ares");
466 HttpListener listener = (HttpListener) ares.AsyncState;
467 Context = listener.EndGetContext (ares);
468 } catch (Exception e) {
474 public void Dispose ()
481 public void ConnectionReuse ()
483 var uri = "http://localhost:" + NetworkHelpers.FindFreePort () + "/";
485 HttpListener listener = new HttpListener ();
486 listener.Prefixes.Add (uri);
489 IPEndPoint expectedIpEndPoint = CreateListenerRequest (listener, uri);
491 Assert.AreEqual (expectedIpEndPoint, CreateListenerRequest (listener, uri), "reuse1");
492 Assert.AreEqual (expectedIpEndPoint, CreateListenerRequest (listener, uri), "reuse2");
495 public IPEndPoint CreateListenerRequest (HttpListener listener, string uri)
497 IPEndPoint ipEndPoint = null;
498 var mre = new ManualResetEventSlim ();
499 listener.BeginGetContext (result => {
500 ipEndPoint = ListenerCallback (result);
504 var request = (HttpWebRequest) WebRequest.Create (uri);
505 request.Method = "POST";
507 // We need to write something
508 request.GetRequestStream ().Write (new byte [] {(byte)'a'}, 0, 1);
509 request.GetRequestStream ().Dispose ();
511 // Send request, socket is created or reused.
512 var response = request.GetResponse ();
514 // Close response so socket can be reused.
522 public static IPEndPoint ListenerCallback (IAsyncResult result)
524 var listener = (HttpListener) result.AsyncState;
525 var context = listener.EndGetContext (result);
526 var clientEndPoint = context.Request.RemoteEndPoint;
528 // Disposing InputStream should not avoid socket reuse
529 context.Request.InputStream.Dispose ();
531 // Close OutputStream to send response
532 context.Response.OutputStream.Close ();
534 return clientEndPoint;
538 public void HttpClientIsDisconnectedCheckForWriteException()
540 string uri = "http://localhost:" + NetworkHelpers.FindFreePort () + "/";
542 AutoResetEvent exceptionOccuredEvent = new AutoResetEvent (false);
543 HttpListener listener = new HttpListener {
544 IgnoreWriteExceptions = false
546 listener.Prefixes.Add (uri);
548 listener.BeginGetContext (result =>
550 HttpListenerContext context = listener.EndGetContext (result);
551 context.Response.SendChunked = true;
552 context.Request.InputStream.Close ();
554 var bytes = new byte [1024];
555 using(Stream outputStream = context.Response.OutputStream) {
558 outputStream.Write (bytes, 0, bytes.Length);
560 exceptionOccuredEvent.Set ();
565 Task.Factory.StartNew (() =>
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);
581 Assert.IsTrue (exceptionOccuredEvent.WaitOne (15 * 1000), "#02");