source: TFP-WebServer/SpaceWizards.HttpListener/src/System/Net/Managed/HttpListener.Managed.cs

Last change on this file was 377, checked in by alloc, 2 years ago

Made SpaceWizards.HttpListener compilable against .NET 4.8 with preprocessor define UNITY_NETFRAMEWORK

File size: 11.5 KB
Line 
1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3
4// ReSharper disable RedundantUsingDirective
5// ReSharper disable RedundantCast
6// ReSharper disable RedundantUnsafeContext
7
8using System;
9using System.Collections;
10using System.Collections.Generic;
11using System.IO;
12using System.Net;
13using System.Net.Security;
14
15namespace SpaceWizards.HttpListener
16{
17 public sealed unsafe partial class HttpListener
18 {
19 public static bool IsSupported => true;
20
21 private Dictionary<HttpListenerContext, HttpListenerContext> _listenerContexts = new Dictionary<HttpListenerContext, HttpListenerContext>();
22 private List<HttpListenerContext> _contextQueue = new List<HttpListenerContext>();
23 private List<ListenerAsyncResult> _asyncWaitQueue = new List<ListenerAsyncResult>();
24 private Dictionary<HttpConnection, HttpConnection> _connections = new Dictionary<HttpConnection, HttpConnection>();
25 private bool _unsafeConnectionNtlmAuthentication;
26
27 public HttpListenerTimeoutManager TimeoutManager
28 {
29 get
30 {
31 CheckDisposed();
32 return _timeoutManager;
33 }
34 }
35
36 private void AddPrefixCore(string uriPrefix) => HttpEndPointManager.AddPrefix(uriPrefix, this);
37
38 private void RemovePrefixCore(string uriPrefix) => HttpEndPointManager.RemovePrefix(uriPrefix, this);
39
40 public void Start()
41 {
42 lock (_internalLock)
43 {
44 try
45 {
46 CheckDisposed();
47 if (_state == State.Started)
48 return;
49
50 HttpEndPointManager.AddListener(this);
51
52 _state = State.Started;
53 }
54 catch (Exception exception)
55 {
56 _state = State.Closed;
57 if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Start {exception}");
58 throw;
59 }
60 }
61 }
62
63 public bool UnsafeConnectionNtlmAuthentication
64 {
65 // NTLM isn't currently supported, so this is a nop anyway and we can just roundtrip the value
66 get => _unsafeConnectionNtlmAuthentication;
67 set
68 {
69 CheckDisposed();
70 _unsafeConnectionNtlmAuthentication = value;
71 }
72 }
73
74 public void Stop()
75 {
76
77 lock (_internalLock)
78 {
79 try
80 {
81 CheckDisposed();
82 if (_state == State.Stopped)
83 {
84 return;
85 }
86
87 Close(false);
88 }
89 catch (Exception exception)
90 {
91 if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Stop {exception}");
92 throw;
93 }
94 finally
95 {
96 _state = State.Stopped;
97 }
98 }
99 }
100
101 public void Abort()
102 {
103
104 lock (_internalLock)
105 {
106 try
107 {
108 if (_state == State.Closed)
109 {
110 return;
111 }
112
113 // Just detach and free resources. Don't call Stop (which may throw).
114 if (_state == State.Started)
115 {
116 Close(true);
117 }
118 }
119 catch (Exception exception)
120 {
121 if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Abort {exception}");
122 throw;
123 }
124 finally
125 {
126 _state = State.Closed;
127 }
128 }
129 }
130
131 private void Dispose()
132 {
133
134 lock (_internalLock)
135 {
136 try
137 {
138 if (_state == State.Closed)
139 {
140 return;
141 }
142
143 Close(true);
144 }
145 catch (Exception exception)
146 {
147 if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, $"Dispose {exception}");
148 throw;
149 }
150 finally
151 {
152 _state = State.Closed;
153 }
154 }
155 }
156
157 private void Close(bool force)
158 {
159 CheckDisposed();
160 HttpEndPointManager.RemoveListener(this);
161 Cleanup(force);
162 }
163
164 internal void UnregisterContext(HttpListenerContext context)
165 {
166 lock ((_listenerContexts as ICollection).SyncRoot)
167 {
168 _listenerContexts.Remove(context);
169 }
170 lock ((_contextQueue as ICollection).SyncRoot)
171 {
172 int idx = _contextQueue.IndexOf(context);
173 if (idx >= 0)
174 _contextQueue.RemoveAt(idx);
175 }
176 }
177
178 internal void AddConnection(HttpConnection cnc)
179 {
180 lock ((_connections as ICollection).SyncRoot)
181 {
182 _connections[cnc] = cnc;
183 }
184 }
185
186 internal void RemoveConnection(HttpConnection cnc)
187 {
188 lock ((_connections as ICollection).SyncRoot)
189 {
190 _connections.Remove(cnc);
191 }
192 }
193
194 internal void RegisterContext(HttpListenerContext context)
195 {
196 lock ((_listenerContexts as ICollection).SyncRoot)
197 {
198 _listenerContexts[context] = context;
199 }
200
201 ListenerAsyncResult? ares = null;
202 lock ((_asyncWaitQueue as ICollection).SyncRoot)
203 {
204 if (_asyncWaitQueue.Count == 0)
205 {
206 lock ((_contextQueue as ICollection).SyncRoot)
207 _contextQueue.Add(context);
208 }
209 else
210 {
211 ares = _asyncWaitQueue[0];
212 _asyncWaitQueue.RemoveAt(0);
213 }
214 }
215
216 if (ares != null)
217 {
218 ares.Complete(context);
219 }
220 }
221
222 private void Cleanup(bool close_existing)
223 {
224 lock ((_listenerContexts as ICollection).SyncRoot)
225 {
226 if (close_existing)
227 {
228 // Need to copy this since closing will call UnregisterContext
229 ICollection keys = _listenerContexts.Keys;
230 var all = new HttpListenerContext[keys.Count];
231 keys.CopyTo(all, 0);
232 _listenerContexts.Clear();
233 for (int i = all.Length - 1; i >= 0; i--)
234 all[i].Connection.Close(true);
235 }
236
237 lock ((_connections as ICollection).SyncRoot)
238 {
239 ICollection keys = _connections.Keys;
240 var conns = new HttpConnection[keys.Count];
241 keys.CopyTo(conns, 0);
242 _connections.Clear();
243 for (int i = conns.Length - 1; i >= 0; i--)
244 conns[i].Close(true);
245 }
246 lock ((_contextQueue as ICollection).SyncRoot)
247 {
248 var ctxs = (HttpListenerContext[])_contextQueue.ToArray();
249 _contextQueue.Clear();
250 for (int i = ctxs.Length - 1; i >= 0; i--)
251 ctxs[i].Connection.Close(true);
252 }
253
254 lock ((_asyncWaitQueue as ICollection).SyncRoot)
255 {
256 Exception exc = new ObjectDisposedException("listener");
257 foreach (ListenerAsyncResult ares in _asyncWaitQueue)
258 {
259 ares.Complete(exc);
260 }
261 _asyncWaitQueue.Clear();
262 }
263 }
264 }
265
266 private HttpListenerContext? GetContextFromQueue()
267 {
268 lock ((_contextQueue as ICollection).SyncRoot)
269 {
270 if (_contextQueue.Count == 0)
271 {
272 return null;
273 }
274
275 HttpListenerContext context = _contextQueue[0];
276 _contextQueue.RemoveAt(0);
277
278 return context;
279 }
280 }
281
282 public IAsyncResult BeginGetContext(AsyncCallback? callback, object? state)
283 {
284 CheckDisposed();
285 if (_state != State.Started)
286 {
287 throw new InvalidOperationException(SR.Format(SR.net_listener_mustcall, "Start()"));
288 }
289
290 ListenerAsyncResult ares = new ListenerAsyncResult(this, callback, state);
291
292 // lock wait_queue early to avoid race conditions
293 lock ((_asyncWaitQueue as ICollection).SyncRoot)
294 {
295 lock ((_contextQueue as ICollection).SyncRoot)
296 {
297 HttpListenerContext? ctx = GetContextFromQueue();
298 if (ctx != null)
299 {
300 ares.Complete(ctx, true);
301 return ares;
302 }
303 }
304
305 _asyncWaitQueue.Add(ares);
306 }
307
308 return ares;
309 }
310
311 public HttpListenerContext EndGetContext(IAsyncResult asyncResult)
312 {
313 CheckDisposed();
314 if (asyncResult == null)
315 {
316 throw new ArgumentNullException(nameof(asyncResult));
317 }
318
319 ListenerAsyncResult? ares = asyncResult as ListenerAsyncResult;
320 if (ares == null || !ReferenceEquals(this, ares._parent))
321 {
322 throw new ArgumentException(SR.net_io_invalidasyncresult, nameof(asyncResult));
323 }
324 if (ares._endCalled)
325 {
326 throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, nameof(EndGetContext)));
327 }
328
329 ares._endCalled = true;
330
331 if (!ares.IsCompleted)
332 ares.AsyncWaitHandle.WaitOne();
333
334 lock ((_asyncWaitQueue as ICollection).SyncRoot)
335 {
336 int idx = _asyncWaitQueue.IndexOf(ares);
337 if (idx >= 0)
338 _asyncWaitQueue.RemoveAt(idx);
339 }
340
341 HttpListenerContext context = ares.GetContext()!;
342 context.ParseAuthentication(context.AuthenticationSchemes);
343 return context;
344 }
345
346 internal AuthenticationSchemes SelectAuthenticationScheme(HttpListenerContext context)
347 {
348 return AuthenticationSchemeSelectorDelegate != null ? AuthenticationSchemeSelectorDelegate(context.Request) : _authenticationScheme;
349 }
350
351 public HttpListenerContext GetContext()
352 {
353 CheckDisposed();
354 if (_state == State.Stopped)
355 {
356 throw new InvalidOperationException(SR.Format(SR.net_listener_mustcall, "Start()"));
357 }
358 if (_prefixes.Count == 0)
359 {
360 throw new InvalidOperationException(SR.Format(SR.net_listener_mustcall, "AddPrefix()"));
361 }
362
363 ListenerAsyncResult ares = (ListenerAsyncResult)BeginGetContext(null, null);
364 ares._inGet = true;
365
366 return EndGetContext(ares);
367 }
368 }
369}
Note: See TracBrowser for help on using the repository browser.