merged Sys.Web.Services 2.0 support in my branch:
[mono.git] / mcs / class / System / System.Net / HttpListener.cs
1 //
2 // System.Net.HttpListener
3 //
4 // Author:
5 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
6 //
7 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 #if NET_2_0 && SECURITY_DEP
30
31 using System.Collections.Generic;
32 using System.Threading;
33 //TODO: logging
34 namespace System.Net {
35         public sealed class HttpListener : IDisposable {
36                 AuthenticationSchemes auth_schemes;
37                 HttpListenerPrefixCollection prefixes;
38                 AuthenticationSchemeSelector auth_selector; 
39                 string realm;
40                 bool ignore_write_exceptions;
41                 bool unsafe_ntlm_auth;
42                 bool listening;
43                 bool disposed;
44
45                 Dictionary<HttpListenerContext,HttpListenerContext> registry;
46                 List<HttpListenerContext> ctx_queue;
47                 List<ListenerAsyncResult> wait_queue;
48
49                 public HttpListener ()
50                 {
51                         prefixes = new HttpListenerPrefixCollection (this);
52                         registry = new Dictionary<HttpListenerContext,HttpListenerContext> ();
53                         ctx_queue = new List<HttpListenerContext> ();
54                         wait_queue = new List<ListenerAsyncResult> ();
55                         auth_schemes = AuthenticationSchemes.Anonymous;
56                 }
57
58                 // TODO: Digest, NTLM and Negotiate require ControlPrincipal
59                 public AuthenticationSchemes AuthenticationSchemes {
60                         get { return auth_schemes; }
61                         set {
62                                 CheckDisposed ();
63                                 auth_schemes = value;
64                         }
65                 }
66
67                 //TODO: when is this called?
68                 public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate {
69                         get { return auth_selector; }
70                         set {
71                                 CheckDisposed ();
72                                 auth_selector = value;
73                         }
74                 }
75
76                 public bool IgnoreWriteExceptions {
77                         get { return ignore_write_exceptions; }
78                         set {
79                                 CheckDisposed ();
80                                 ignore_write_exceptions = value;
81                         }
82                 }
83
84                 public bool IsListening {
85                         get { return listening; }
86                 }
87
88                 public static bool IsSupported {
89                         get { return true; }
90                 }
91
92                 public HttpListenerPrefixCollection Prefixes {
93                         get {
94                                 CheckDisposed ();
95                                 return prefixes;
96                         }
97                 }
98
99                 // TODO: use this
100                 public string Realm {
101                         get { return realm; }
102                         set {
103                                 CheckDisposed ();
104                                 realm = value;
105                         }
106                 }
107
108                 [MonoTODO ("Support for NTLM needs some loving.")]
109                 public bool UnsafeConnectionNtlmAuthentication {
110                         get { return unsafe_ntlm_auth; }
111                         set {
112                                 CheckDisposed ();
113                                 unsafe_ntlm_auth = value;
114                         }
115                 }
116
117                 public void Abort ()
118                 {
119                         if (disposed)
120                                 return;
121
122                         if (!listening) {
123                                 disposed = true;
124                                 return;
125                         }
126
127                         Close (true);
128                 }
129
130                 public void Close ()
131                 {
132                         if (disposed)
133                                 return;
134
135                         if (!listening) {
136                                 disposed = true;
137                                 return;
138                         }
139
140                         Close (false);
141                 }
142
143                 void Close (bool force)
144                 {
145                         CheckDisposed ();
146                         EndPointManager.RemoveListener (this);
147                         Cleanup (force);
148                         disposed = true;
149                 }
150
151                 void Cleanup (bool close_existing)
152                 {
153                         lock (registry) {
154                                 if (close_existing) {
155                                         foreach (HttpListenerContext context in registry.Keys) {
156                                                 context.Connection.Close ();
157                                         }
158                                         registry.Clear (); // Just in case.
159                                 }
160
161                                 lock (ctx_queue) {
162                                         foreach (HttpListenerContext context in ctx_queue)
163                                                 context.Connection.Close ();
164
165                                         ctx_queue.Clear ();
166                                 }
167
168                                 lock (wait_queue) {
169                                         foreach (ListenerAsyncResult ares in wait_queue) {
170                                                 ares.Complete ("Listener was closed.");
171                                         }
172                                         wait_queue.Clear ();
173                                 }
174                         }
175                 }
176
177                 public IAsyncResult BeginGetContext (AsyncCallback callback, Object state)
178                 {
179                         CheckDisposed ();
180                         if (!listening)
181                                 throw new InvalidOperationException ("Please, call Start before using this method.");
182
183                         ListenerAsyncResult ares = new ListenerAsyncResult (callback, state);
184
185                         // lock wait_queue early to avoid race conditions
186                         lock (wait_queue) {
187                                 lock (ctx_queue) {
188                                         HttpListenerContext ctx = GetContextFromQueue ();
189                                         if (ctx != null) {
190                                                 ares.Complete (ctx, true);
191                                                 return ares;
192                                         }
193                                 }
194
195                                 wait_queue.Add (ares);
196                         }
197
198                         return ares;
199                 }
200
201                 public HttpListenerContext EndGetContext (IAsyncResult asyncResult)
202                 {
203                         CheckDisposed ();
204                         if (asyncResult == null)
205                                 throw new ArgumentNullException ("asyncResult");
206
207                         ListenerAsyncResult ares = asyncResult as ListenerAsyncResult;
208                         if (ares == null)
209                                 throw new ArgumentException ("Wrong IAsyncResult.", "asyncResult");
210
211                         if (!ares.IsCompleted)
212                                 ares.AsyncWaitHandle.WaitOne ();
213
214                         lock (wait_queue) {
215                                 int idx = wait_queue.IndexOf (ares);
216                                 if (idx >= 0)
217                                         wait_queue.RemoveAt (idx);
218                         }
219
220                         return ares.GetContext (); // This will throw on error.
221                 }
222
223                 public HttpListenerContext GetContext ()
224                 {
225                         // The prefixes are not checked when using the async interface!?
226                         if (prefixes.Count == 0)
227                                 throw new InvalidOperationException ("Please, call AddPrefix before using this method.");
228
229                         IAsyncResult ares = BeginGetContext (null, null);
230                         return EndGetContext (ares);
231                 }
232
233                 public void Start ()
234                 {
235                         CheckDisposed ();
236                         if (listening)
237                                 return;
238
239                         EndPointManager.AddListener (this);
240                         listening = true;
241                 }
242
243                 public void Stop ()
244                 {
245                         CheckDisposed ();
246                         listening = false;
247                         Close (false);
248                 }
249
250                 void IDisposable.Dispose ()
251                 {
252                         if (disposed)
253                                 return;
254
255                         disposed = true;
256                         Close (true); //TODO: Should we force here or not?
257                 }
258
259                 internal void CheckDisposed ()
260                 {
261                         if (disposed)
262                                 throw new ObjectDisposedException (GetType ().ToString ());
263                 }
264
265                 // Must be called with a lock on ctx_queue
266                 HttpListenerContext GetContextFromQueue ()
267                 {
268                         if (ctx_queue.Count == 0)
269                                 return null;
270
271                         HttpListenerContext context = ctx_queue [0];
272                         ctx_queue.RemoveAt (0);
273                         return context;
274                 }
275
276                 internal void RegisterContext (HttpListenerContext context)
277                 {
278                         try {
279                                 Monitor.Enter (registry);
280                                 registry [context] = context;
281                                 Monitor.Enter (wait_queue);
282                                 Monitor.Enter (ctx_queue);
283                                 if (wait_queue.Count == 0) {
284                                         ctx_queue.Add (context);
285                                 } else {
286                                         ListenerAsyncResult ares = wait_queue [0];
287                                         wait_queue.RemoveAt (0);
288                                         ares.Complete (context);
289                                 }
290                         } finally {
291                                 Monitor.Exit (ctx_queue);
292                                 Monitor.Exit (wait_queue);
293                                 Monitor.Exit (registry);
294                         }
295                 }
296
297                 internal void UnregisterContext (HttpListenerContext context)
298                 {
299                         try {
300                                 Monitor.Enter (registry);
301                                 Monitor.Enter (ctx_queue);
302                                 int idx = ctx_queue.IndexOf (context);
303                                 if (idx >= 0)
304                                         ctx_queue.RemoveAt (idx);
305                                 registry.Remove (context);
306                         } finally {
307                                 Monitor.Exit (ctx_queue);
308                                 Monitor.Exit (registry);
309                         }
310                 }
311         }
312 }
313 #endif
314