parent
15344d8d7a
commit
46a11be2ed
|
|
@ -106,7 +106,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
_cookedUrlQuery = Marshal.PtrToStringUni((IntPtr)cookedUrl.pQueryString, cookedUrl.QueryStringLength / 2);
|
_cookedUrlQuery = Marshal.PtrToStringUni((IntPtr)cookedUrl.pQueryString, cookedUrl.QueryStringLength / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
UrlPrefix prefix = httpContext.Server.UrlPrefixes[(int)_contextId];
|
UrlPrefix prefix = httpContext.Server.UrlPrefixes.GetPrefix((int)_contextId);
|
||||||
string orriginalPath = RequestPath;
|
string orriginalPath = RequestPath;
|
||||||
|
|
||||||
// These paths are both unescaped already.
|
// These paths are both unescaped already.
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
return string.Equals(Whole, obj as string, StringComparison.OrdinalIgnoreCase);
|
return string.Equals(Whole, Convert.ToString(obj), StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.Net.Http.Server
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A collection or URL prefixes
|
||||||
|
/// </summary>
|
||||||
|
public class UrlPrefixCollection : ICollection<UrlPrefix>
|
||||||
|
{
|
||||||
|
private readonly WebListener _webListener;
|
||||||
|
private readonly IDictionary<int, UrlPrefix> _prefixes = new Dictionary<int, UrlPrefix>(1);
|
||||||
|
private int _nextId = 1;
|
||||||
|
|
||||||
|
internal UrlPrefixCollection(WebListener webListener)
|
||||||
|
{
|
||||||
|
_webListener = webListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get { return _prefixes.Count; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly
|
||||||
|
{
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(string prefix)
|
||||||
|
{
|
||||||
|
Add(UrlPrefix.Create(prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(UrlPrefix item)
|
||||||
|
{
|
||||||
|
var id = _nextId++;
|
||||||
|
if (_webListener.IsListening)
|
||||||
|
{
|
||||||
|
RegisterPrefix(item.Whole, id);
|
||||||
|
}
|
||||||
|
_prefixes.Add(id, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal UrlPrefix GetPrefix(int id)
|
||||||
|
{
|
||||||
|
return _prefixes[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
if (_webListener.IsListening)
|
||||||
|
{
|
||||||
|
UnregisterAllPrefixes();
|
||||||
|
}
|
||||||
|
_prefixes.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(UrlPrefix item)
|
||||||
|
{
|
||||||
|
return _prefixes.Values.Contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(UrlPrefix[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
_prefixes.Values.CopyTo(array, arrayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(string prefix)
|
||||||
|
{
|
||||||
|
return Remove(UrlPrefix.Create(prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(UrlPrefix item)
|
||||||
|
{
|
||||||
|
int? id = null;
|
||||||
|
foreach (var pair in _prefixes)
|
||||||
|
{
|
||||||
|
if (pair.Value.Equals(item))
|
||||||
|
{
|
||||||
|
id = pair.Key;
|
||||||
|
if (_webListener.IsListening)
|
||||||
|
{
|
||||||
|
UnregisterPrefix(pair.Value.Whole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (id.HasValue)
|
||||||
|
{
|
||||||
|
_prefixes.Remove(id.Value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<UrlPrefix> GetEnumerator()
|
||||||
|
{
|
||||||
|
return _prefixes.Values.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void RegisterAllPrefixes()
|
||||||
|
{
|
||||||
|
// go through the uri list and register for each one of them
|
||||||
|
foreach (var pair in _prefixes)
|
||||||
|
{
|
||||||
|
// We'll get this index back on each request and use it to look up the prefix to calculate PathBase.
|
||||||
|
RegisterPrefix(pair.Value.Whole, pair.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void UnregisterAllPrefixes()
|
||||||
|
{
|
||||||
|
// go through the uri list and unregister for each one of them
|
||||||
|
foreach (var prefix in _prefixes.Values)
|
||||||
|
{
|
||||||
|
// ignore possible failures
|
||||||
|
UnregisterPrefix(prefix.Whole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterPrefix(string uriPrefix, int contextId)
|
||||||
|
{
|
||||||
|
uint statusCode = 0;
|
||||||
|
|
||||||
|
statusCode =
|
||||||
|
UnsafeNclNativeMethods.HttpApi.HttpAddUrlToUrlGroup(
|
||||||
|
_webListener.UrlGroupId,
|
||||||
|
uriPrefix,
|
||||||
|
(ulong)contextId,
|
||||||
|
0);
|
||||||
|
|
||||||
|
if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_ALREADY_EXISTS)
|
||||||
|
{
|
||||||
|
throw new WebListenerException((int)statusCode, String.Format(Resources.Exception_PrefixAlreadyRegistered, uriPrefix));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new WebListenerException((int)statusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool UnregisterPrefix(string uriPrefix)
|
||||||
|
{
|
||||||
|
uint statusCode = 0;
|
||||||
|
|
||||||
|
statusCode =
|
||||||
|
UnsafeNclNativeMethods.HttpApi.HttpRemoveUrlFromUrlGroup(
|
||||||
|
_webListener.UrlGroupId,
|
||||||
|
uriPrefix,
|
||||||
|
0);
|
||||||
|
|
||||||
|
if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -85,7 +85,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
|
|
||||||
private object _internalLock;
|
private object _internalLock;
|
||||||
|
|
||||||
private List<UrlPrefix> _urlPrefixes = new List<UrlPrefix>();
|
private UrlPrefixCollection _urlPrefixes;
|
||||||
|
|
||||||
// The native request queue
|
// The native request queue
|
||||||
private long? _requestQueueLength;
|
private long? _requestQueueLength;
|
||||||
|
|
@ -103,6 +103,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
_state = State.Stopped;
|
_state = State.Stopped;
|
||||||
_internalLock = new object();
|
_internalLock = new object();
|
||||||
|
|
||||||
|
_urlPrefixes = new UrlPrefixCollection(this);
|
||||||
_timeoutManager = new TimeoutManager(this);
|
_timeoutManager = new TimeoutManager(this);
|
||||||
_authManager = new AuthenticationManager(this);
|
_authManager = new AuthenticationManager(this);
|
||||||
_connectionCancellationTokens = new ConcurrentDictionary<ulong, ConnectionCancellation>();
|
_connectionCancellationTokens = new ConcurrentDictionary<ulong, ConnectionCancellation>();
|
||||||
|
|
@ -120,7 +121,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
get { return _logger; }
|
get { return _logger; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<UrlPrefix> UrlPrefixes
|
public UrlPrefixCollection UrlPrefixes
|
||||||
{
|
{
|
||||||
get { return _urlPrefixes; }
|
get { return _urlPrefixes; }
|
||||||
}
|
}
|
||||||
|
|
@ -133,6 +134,11 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal ulong UrlGroupId
|
||||||
|
{
|
||||||
|
get { return _urlGroupId; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exposes the Http.Sys timeout configurations. These may also be configured in the registry.
|
/// Exposes the Http.Sys timeout configurations. These may also be configured in the registry.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -258,29 +264,6 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveAll(bool clear)
|
|
||||||
{
|
|
||||||
CheckDisposed();
|
|
||||||
// go through the uri list and unregister for each one of them
|
|
||||||
if (_urlPrefixes.Count > 0)
|
|
||||||
{
|
|
||||||
LogHelper.LogInfo(_logger, "RemoveAll");
|
|
||||||
if (_state == State.Started)
|
|
||||||
{
|
|
||||||
foreach (UrlPrefix registeredPrefix in _urlPrefixes)
|
|
||||||
{
|
|
||||||
// ignore possible failures
|
|
||||||
InternalRemovePrefix(registeredPrefix.Whole);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clear)
|
|
||||||
{
|
|
||||||
_urlPrefixes.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IntPtr DangerousGetHandle()
|
private IntPtr DangerousGetHandle()
|
||||||
{
|
{
|
||||||
return _requestQueueHandle.DangerousGetHandle();
|
return _requestQueueHandle.DangerousGetHandle();
|
||||||
|
|
@ -379,7 +362,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
// All resources are set up correctly. Now add all prefixes.
|
// All resources are set up correctly. Now add all prefixes.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
AddAllPrefixes();
|
_urlPrefixes.RegisterAllPrefixes();
|
||||||
}
|
}
|
||||||
catch (WebListenerException)
|
catch (WebListenerException)
|
||||||
{
|
{
|
||||||
|
|
@ -489,7 +472,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
LogHelper.LogInfo(_logger, "Stop");
|
LogHelper.LogInfo(_logger, "Stop");
|
||||||
|
|
||||||
RemoveAll(false);
|
_urlPrefixes.UnregisterAllPrefixes();
|
||||||
|
|
||||||
_state = State.Stopped;
|
_state = State.Stopped;
|
||||||
|
|
||||||
|
|
@ -586,62 +569,6 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private uint InternalAddPrefix(string uriPrefix, int contextId)
|
|
||||||
{
|
|
||||||
uint statusCode = 0;
|
|
||||||
|
|
||||||
statusCode =
|
|
||||||
UnsafeNclNativeMethods.HttpApi.HttpAddUrlToUrlGroup(
|
|
||||||
_urlGroupId,
|
|
||||||
uriPrefix,
|
|
||||||
(ulong)contextId,
|
|
||||||
0);
|
|
||||||
|
|
||||||
return statusCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool InternalRemovePrefix(string uriPrefix)
|
|
||||||
{
|
|
||||||
uint statusCode = 0;
|
|
||||||
|
|
||||||
statusCode =
|
|
||||||
UnsafeNclNativeMethods.HttpApi.HttpRemoveUrlFromUrlGroup(
|
|
||||||
_urlGroupId,
|
|
||||||
uriPrefix,
|
|
||||||
0);
|
|
||||||
|
|
||||||
if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddAllPrefixes()
|
|
||||||
{
|
|
||||||
// go through the uri list and register for each one of them
|
|
||||||
if (_urlPrefixes.Count > 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _urlPrefixes.Count; i++)
|
|
||||||
{
|
|
||||||
// We'll get this index back on each request and use it to look up the prefix to calculate PathBase.
|
|
||||||
UrlPrefix registeredPrefix = _urlPrefixes[i];
|
|
||||||
uint statusCode = InternalAddPrefix(registeredPrefix.Whole, i);
|
|
||||||
if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_ALREADY_EXISTS)
|
|
||||||
{
|
|
||||||
throw new WebListenerException((int)statusCode, String.Format(Resources.Exception_PrefixAlreadyRegistered, registeredPrefix.Whole));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new WebListenerException((int)statusCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal unsafe bool ValidateRequest(NativeRequestContext requestMemory)
|
internal unsafe bool ValidateRequest(NativeRequestContext requestMemory)
|
||||||
{
|
{
|
||||||
// Block potential DOS attacks
|
// Block potential DOS attacks
|
||||||
|
|
@ -713,7 +640,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
|
|
||||||
private CancellationToken GetConnectionCancellation(ulong connectionId)
|
private CancellationToken GetConnectionCancellation(ulong connectionId)
|
||||||
{
|
{
|
||||||
// Read case is performance senstive
|
// Read case is performance sensitive
|
||||||
ConnectionCancellation cancellation;
|
ConnectionCancellation cancellation;
|
||||||
if (!_connectionCancellationTokens.TryGetValue(connectionId, out cancellation))
|
if (!_connectionCancellationTokens.TryGetValue(connectionId, out cancellation))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,69 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Server_HotAddPrefix_Success()
|
||||||
|
{
|
||||||
|
string address;
|
||||||
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
|
{
|
||||||
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
|
var context = await server.GetContextAsync();
|
||||||
|
Assert.Equal(string.Empty, context.Request.PathBase);
|
||||||
|
Assert.Equal("/", context.Request.Path);
|
||||||
|
context.Dispose();
|
||||||
|
|
||||||
|
var response = await responseTask;
|
||||||
|
Assert.Equal(string.Empty, response);
|
||||||
|
|
||||||
|
address += "pathbase/";
|
||||||
|
server.UrlPrefixes.Add(address);
|
||||||
|
|
||||||
|
responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
|
context = await server.GetContextAsync();
|
||||||
|
Assert.Equal("/pathbase", context.Request.PathBase);
|
||||||
|
Assert.Equal("/", context.Request.Path);
|
||||||
|
context.Dispose();
|
||||||
|
|
||||||
|
response = await responseTask;
|
||||||
|
Assert.Equal(string.Empty, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Server_HotRemovePrefix_Success()
|
||||||
|
{
|
||||||
|
string address;
|
||||||
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
|
{
|
||||||
|
address += "pathbase/";
|
||||||
|
server.UrlPrefixes.Add(address);
|
||||||
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
|
var context = await server.GetContextAsync();
|
||||||
|
Assert.Equal("/pathbase", context.Request.PathBase);
|
||||||
|
Assert.Equal("/", context.Request.Path);
|
||||||
|
context.Dispose();
|
||||||
|
|
||||||
|
var response = await responseTask;
|
||||||
|
Assert.Equal(string.Empty, response);
|
||||||
|
|
||||||
|
Assert.True(server.UrlPrefixes.Remove(address));
|
||||||
|
|
||||||
|
responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
|
context = await server.GetContextAsync();
|
||||||
|
Assert.Equal(string.Empty, context.Request.PathBase);
|
||||||
|
Assert.Equal("/pathbase/", context.Request.Path);
|
||||||
|
context.Dispose();
|
||||||
|
|
||||||
|
response = await responseTask;
|
||||||
|
Assert.Equal(string.Empty, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> SendRequestAsync(string uri)
|
private async Task<string> SendRequestAsync(string uri)
|
||||||
{
|
{
|
||||||
ServicePointManager.DefaultConnectionLimit = 100;
|
ServicePointManager.DefaultConnectionLimit = 100;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue