Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 7a48e11

Browse files
hughbestephentoub
authored andcommitted
Add HttpListenerPrefixCollection tests and align managed implementation with Windows (#19217)
* Clean up some HttpListener code to make way for prefix code sharing * Share HttpListenerPrefixCollection between managed and Windows implementations * Align managed and Windows implementation of adding and removing prefixes Move to a platform abstraction layer situation * Add tests for HttpListenerPrefixCollection and things related to prefixes and HttpListener * Fix some managed implementation test failures Expects HttpListenerException thrown, actually SocketException was thrown. Fixes Add_InvalidPrefixNotStarted_ThrowsHttpListenerExceptionOnStart Fixes Add_InvalidPrefixAlreadyStarted_ThrowsHttpListenerExceptionOnAdd * Fix no exception thrown listening to an already registered host/port in managed Fixes Add_PrefixAlreadyRegisteredAndStarted_ThrowsHttpListenerException * Temporarily disable potentially bad tests Should these be removed in PR feedback? * Add explicit NRE throwing in HttpListenerPrefixCollection.CopyTo * Fix NRE in HttpListenerPrefixCollection.CopyTo(null, 0) * Improve managed implementation validation of hosts And add a bunch of other tests for prefixes * Address PR feedback
1 parent e411611 commit 7a48e11

11 files changed

+738
-485
lines changed

src/System.Net.HttpListener/src/System.Net.HttpListener.csproj

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
<Reference Include="System.Threading.Timer" />
4848
</ItemGroup>
4949
<ItemGroup>
50+
<Compile Include="System\Net\HttpListenerPrefixCollection.cs" />
5051
<Compile Include="System\Net\HttpRequestStream.cs" />
5152
<Compile Include="System\Net\AuthenticationTypes.cs" />
5253
<Compile Include="System\Net\HttpResponseStream.cs" />
@@ -65,6 +66,9 @@
6566
<Compile Include="$(CommonPath)\System\Net\Logging\NetEventSource.Common.cs">
6667
<Link>Common\System\Net\Logging\NetEventSource.Common.cs</Link>
6768
</Compile>
69+
<Compile Include="$(CommonPath)\System\Net\CaseInsensitiveAscii.cs">
70+
<Link>Common\System\Net\CaseInsensitiveAscii.cs</Link>
71+
</Compile>
6872
<Compile Include="$(CommonPath)\System\Net\HttpStatusDescription.cs">
6973
<Link>Common\System\Net\HttpStatusDescription.cs</Link>
7074
</Compile>
@@ -79,7 +83,6 @@
7983
</Compile>
8084
</ItemGroup>
8185
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
82-
<Compile Include="System\Net\Windows\HttpListenerPrefixCollection.Windows.cs" />
8386
<Compile Include="System\Net\Windows\HttpListener.Windows.cs" />
8487
<Compile Include="System\Net\Windows\HttpListenerContext.Windows.cs" />
8588
<Compile Include="System\Net\Windows\HttpListenerRequest.Windows.cs" />
@@ -98,9 +101,6 @@
98101
<Compile Include="System\Net\Windows\RequestContextBase.cs" />
99102
<Compile Include="System\Net\Windows\SyncRequestContext.cs" />
100103
<Compile Include="System\Net\Windows\ListenerAsyncResult.Windows.cs" />
101-
<Compile Include="$(CommonPath)\System\Net\CaseInsensitiveAscii.cs">
102-
<Link>Common\System\Net\CaseInsensitiveAscii.cs</Link>
103-
</Compile>
104104
<Compile Include="$(CommonPath)\System\Net\DebugSafeHandle.cs">
105105
<Link>Common\System\Net\DebugSafeHandle.cs</Link>
106106
</Compile>
@@ -336,7 +336,6 @@
336336
<Compile Include="System\Net\Managed\HttpListenerTimeoutManager.Managed.cs" />
337337
<Compile Include="System\Net\Managed\ListenerAsyncResult.Managed.cs" />
338338
<Compile Include="System\Net\Managed\HttpHeaderStrings.cs" />
339-
<Compile Include="System\Net\Managed\HttpListenerPrefixCollection.Managed.cs" />
340339
<Compile Include="System\Net\Managed\ChunkedInputStream.cs" />
341340
<Compile Include="System\Net\Managed\ChunkStream.cs" />
342341
<Compile Include="System\Net\Managed\HttpResponseStream.Managed.cs" />

src/System.Net.HttpListener/src/System/Net/HttpListener.cs

Lines changed: 163 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public sealed unsafe partial class HttpListener : IDisposable
1414

1515
private readonly object _internalLock;
1616
private volatile State _state; // _state is set only within lock blocks, but often read outside locks.
17-
private HttpListenerPrefixCollection _prefixes;
17+
private readonly HttpListenerPrefixCollection _prefixes;
1818
internal Hashtable _uriPrefixes = new Hashtable();
1919
private bool _ignoreWriteExceptions;
2020
private ServiceNameStore _defaultServiceNames;
@@ -47,10 +47,7 @@ public HttpListener()
4747

4848
public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate
4949
{
50-
get
51-
{
52-
return _authenticationDelegate;
53-
}
50+
get => _authenticationDelegate;
5451
set
5552
{
5653
CheckDisposed();
@@ -60,10 +57,7 @@ public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate
6057

6158
public ExtendedProtectionSelector ExtendedProtectionSelectorDelegate
6259
{
63-
get
64-
{
65-
return _extendedProtectionSelectorDelegate;
66-
}
60+
get => _extendedProtectionSelectorDelegate;
6761
set
6862
{
6963
CheckDisposed();
@@ -78,10 +72,7 @@ public ExtendedProtectionSelector ExtendedProtectionSelectorDelegate
7872

7973
public AuthenticationSchemes AuthenticationSchemes
8074
{
81-
get
82-
{
83-
return _authenticationScheme;
84-
}
75+
get => _authenticationScheme;
8576
set
8677
{
8778
CheckDisposed();
@@ -91,10 +82,7 @@ public AuthenticationSchemes AuthenticationSchemes
9182

9283
public ExtendedProtectionPolicy ExtendedProtectionPolicy
9384
{
94-
get
95-
{
96-
return _extendedProtectionPolicy;
97-
}
85+
get => _extendedProtectionPolicy;
9886
set
9987
{
10088
CheckDisposed();
@@ -111,20 +99,171 @@ public ExtendedProtectionPolicy ExtendedProtectionPolicy
11199
}
112100
}
113101

114-
public ServiceNameCollection DefaultServiceNames
102+
public ServiceNameCollection DefaultServiceNames => _defaultServiceNames.ServiceNames;
103+
104+
public HttpListenerPrefixCollection Prefixes
115105
{
116106
get
117107
{
118-
return _defaultServiceNames.ServiceNames;
108+
if (NetEventSource.IsEnabled) NetEventSource.Enter(this);
109+
CheckDisposed();
110+
if (NetEventSource.IsEnabled) NetEventSource.Enter(this);
111+
return _prefixes;
119112
}
120113
}
121114

122-
public string Realm
115+
internal void AddPrefix(string uriPrefix)
123116
{
124-
get
117+
if (NetEventSource.IsEnabled) NetEventSource.Enter(this, $"uriPrefix:{uriPrefix}");
118+
string registeredPrefix = null;
119+
try
120+
{
121+
if (uriPrefix == null)
122+
{
123+
throw new ArgumentNullException(nameof(uriPrefix));
124+
}
125+
CheckDisposed();
126+
int i;
127+
if (string.Compare(uriPrefix, 0, "http://", 0, 7, StringComparison.OrdinalIgnoreCase) == 0)
128+
{
129+
i = 7;
130+
}
131+
else if (string.Compare(uriPrefix, 0, "https://", 0, 8, StringComparison.OrdinalIgnoreCase) == 0)
132+
{
133+
i = 8;
134+
}
135+
else
136+
{
137+
throw new ArgumentException(SR.net_listener_scheme, nameof(uriPrefix));
138+
}
139+
bool inSquareBrakets = false;
140+
int j = i;
141+
while (j < uriPrefix.Length && uriPrefix[j] != '/' && (uriPrefix[j] != ':' || inSquareBrakets))
142+
{
143+
if (uriPrefix[j] == '[')
144+
{
145+
if (inSquareBrakets)
146+
{
147+
j = i;
148+
break;
149+
}
150+
inSquareBrakets = true;
151+
}
152+
if (inSquareBrakets && uriPrefix[j] == ']')
153+
{
154+
inSquareBrakets = false;
155+
}
156+
j++;
157+
}
158+
if (i == j)
159+
{
160+
throw new ArgumentException(SR.net_listener_host, nameof(uriPrefix));
161+
}
162+
if (uriPrefix[uriPrefix.Length - 1] != '/')
163+
{
164+
throw new ArgumentException(SR.net_listener_slash, nameof(uriPrefix));
165+
}
166+
registeredPrefix = uriPrefix[j] == ':' ? String.Copy(uriPrefix) : uriPrefix.Substring(0, j) + (i == 7 ? ":80" : ":443") + uriPrefix.Substring(j);
167+
fixed (char* pChar = registeredPrefix)
168+
{
169+
i = 0;
170+
while (pChar[i] != ':')
171+
{
172+
pChar[i] = (char)CaseInsensitiveAscii.AsciiToLower[(byte)pChar[i]];
173+
i++;
174+
}
175+
}
176+
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"mapped uriPrefix: {uriPrefix} to registeredPrefix: {registeredPrefix}");
177+
if (_state == State.Started)
178+
{
179+
AddPrefixCore(registeredPrefix);
180+
}
181+
_uriPrefixes[uriPrefix] = registeredPrefix;
182+
_defaultServiceNames.Add(uriPrefix);
183+
}
184+
catch (Exception exception)
185+
{
186+
if (NetEventSource.IsEnabled) NetEventSource.Error(this, exception);
187+
throw;
188+
}
189+
finally
190+
{
191+
if (NetEventSource.IsEnabled) NetEventSource.Exit(this, $"prefix: {registeredPrefix}");
192+
}
193+
}
194+
195+
internal bool ContainsPrefix(string uriPrefix) => _uriPrefixes.Contains(uriPrefix);
196+
197+
internal bool RemovePrefix(string uriPrefix)
198+
{
199+
if (NetEventSource.IsEnabled) NetEventSource.Enter(this, $"uriPrefix: {uriPrefix}");
200+
try
125201
{
126-
return _realm;
202+
CheckDisposed();
203+
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"uriPrefix: {uriPrefix}");
204+
if (uriPrefix == null)
205+
{
206+
throw new ArgumentNullException(nameof(uriPrefix));
207+
}
208+
209+
if (!_uriPrefixes.Contains(uriPrefix))
210+
{
211+
return false;
212+
}
213+
214+
if (_state == State.Started)
215+
{
216+
RemovePrefixCore((string)_uriPrefixes[uriPrefix]);
217+
}
218+
219+
_uriPrefixes.Remove(uriPrefix);
220+
_defaultServiceNames.Remove(uriPrefix);
221+
}
222+
catch (Exception exception)
223+
{
224+
if (NetEventSource.IsEnabled) NetEventSource.Error(this, exception);
225+
throw;
127226
}
227+
finally
228+
{
229+
if (NetEventSource.IsEnabled) NetEventSource.Exit(this, $"uriPrefix: {uriPrefix}");
230+
}
231+
return true;
232+
}
233+
234+
internal void RemoveAll(bool clear)
235+
{
236+
if (NetEventSource.IsEnabled) NetEventSource.Enter(this);
237+
try
238+
{
239+
CheckDisposed();
240+
// go through the uri list and unregister for each one of them
241+
if (_uriPrefixes.Count > 0)
242+
{
243+
if (_state == State.Started)
244+
{
245+
foreach (string registeredPrefix in _uriPrefixes.Values)
246+
{
247+
RemovePrefixCore(registeredPrefix);
248+
}
249+
}
250+
251+
if (clear)
252+
{
253+
_uriPrefixes.Clear();
254+
_defaultServiceNames.Clear();
255+
}
256+
}
257+
}
258+
finally
259+
{
260+
if (NetEventSource.IsEnabled) NetEventSource.Exit(this);
261+
}
262+
}
263+
264+
public string Realm
265+
{
266+
get => _realm;
128267
set
129268
{
130269
CheckDisposed();
@@ -136,10 +275,7 @@ public string Realm
136275

137276
public bool IgnoreWriteExceptions
138277
{
139-
get
140-
{
141-
return _ignoreWriteExceptions;
142-
}
278+
get => _ignoreWriteExceptions;
143279
set
144280
{
145281
CheckDisposed();
@@ -189,9 +325,6 @@ private enum State
189325
Closed,
190326
}
191327

192-
void IDisposable.Dispose()
193-
{
194-
Dispose();
195-
}
328+
void IDisposable.Dispose() => Dispose();
196329
}
197330
}

0 commit comments

Comments
 (0)