Change to the new abstractions.
This commit is contained in:
Родитель
5cece8b4af
Коммит
742db6ad65
|
@ -26,7 +26,7 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener
|
namespace Microsoft.AspNet.Server.WebListener
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>;
|
using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -18,7 +18,7 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener
|
namespace Microsoft.AspNet.Server.WebListener
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>;
|
using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>;
|
||||||
using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>;
|
using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>;
|
||||||
|
|
||||||
|
@ -518,7 +518,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
{
|
{
|
||||||
// TODO: Make disconnect registration lazy
|
// TODO: Make disconnect registration lazy
|
||||||
RegisterForDisconnectNotification(requestContext);
|
RegisterForDisconnectNotification(requestContext);
|
||||||
await _appFunc(requestContext.Environment).SupressContext();
|
await _appFunc(requestContext.Features).SupressContext();
|
||||||
await requestContext.ProcessResponseAsync().SupressContext();
|
await requestContext.ProcessResponseAsync().SupressContext();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -832,7 +832,8 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
ulong connectionId = requestContext.Request.ConnectionId;
|
ulong connectionId = requestContext.Request.ConnectionId;
|
||||||
CancellationToken ct = GetConnectionCancellation(connectionId);
|
CancellationToken ct = GetConnectionCancellation(connectionId);
|
||||||
requestContext.Request.RegisterForDisconnect(ct);
|
requestContext.Request.RegisterForDisconnect(ct);
|
||||||
requestContext.Environment.ConnectionDisconnect = ct;
|
// TODO: Need a feature equivalent for owin.CallCancelled.
|
||||||
|
// requestContext.Environment.ConnectionDisconnect = ct;
|
||||||
}
|
}
|
||||||
catch (Win32Exception exception)
|
catch (Win32Exception exception)
|
||||||
{
|
{
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,316 +0,0 @@
|
||||||
<#@ template language="C#" #>
|
|
||||||
<#@ assembly name="System.Core.dll" #>
|
|
||||||
<#@ import namespace="System.Linq" #>
|
|
||||||
<#
|
|
||||||
var Init = new {Yes = new object(), No = new object(), Maybe = new object()};
|
|
||||||
|
|
||||||
var props = new[]
|
|
||||||
{
|
|
||||||
// owin standard keys
|
|
||||||
new {Key="owin.Version", Type="string", Name="OwinVersion", Init=Init.No},
|
|
||||||
new {Key="owin.CallCancelled", Type="CancellationToken", Name="CallCancelled", Init=Init.No},
|
|
||||||
|
|
||||||
new {Key="owin.RequestProtocol", Type="string", Name="RequestProtocol", Init=Init.No},
|
|
||||||
new {Key="owin.RequestMethod", Type="string", Name="RequestMethod", Init=Init.No},
|
|
||||||
new {Key="owin.RequestScheme", Type="string", Name="RequestScheme", Init=Init.No},
|
|
||||||
new {Key="owin.RequestPathBase", Type="string", Name="RequestPathBase", Init=Init.No},
|
|
||||||
new {Key="owin.RequestPath", Type="string", Name="RequestPath", Init=Init.No},
|
|
||||||
new {Key="owin.RequestQueryString", Type="string", Name="RequestQueryString", Init=Init.No},
|
|
||||||
new {Key="owin.RequestHeaders", Type="IDictionary<string, string[]>", Name="RequestHeaders", Init=Init.No},
|
|
||||||
new {Key="owin.RequestBody", Type="Stream", Name="RequestBody", Init=Init.Yes},
|
|
||||||
|
|
||||||
new {Key="owin.ResponseHeaders", Type="IDictionary<string, string[]>", Name="ResponseHeaders", Init=Init.No},
|
|
||||||
new {Key="owin.ResponseBody", Type="Stream", Name="ResponseBody", Init=Init.No},
|
|
||||||
new {Key="owin.ResponseStatusCode", Type="int?", Name="ResponseStatusCode", Init=Init.No},
|
|
||||||
new {Key="owin.ResponseReasonPhrase", Type="string", Name="ResponseReasonPhrase", Init=Init.No},
|
|
||||||
|
|
||||||
// defacto host keys
|
|
||||||
new {Key="host.TraceOutput", Type="TextWriter", Name="HostTraceOutput", Init=Init.No},
|
|
||||||
new {Key="host.AppName", Type="string", Name="HostAppName", Init=Init.No},
|
|
||||||
new {Key="host.AppMode", Type="string", Name="HostAppMode", Init=Init.No},
|
|
||||||
new {Key="host.OnAppDisposing", Type="CancellationToken", Name="OnAppDisposing", Init=Init.No},
|
|
||||||
new {Key="server.User", Type="System.Security.Principal.IPrincipal", Name="User", Init=Init.No},
|
|
||||||
new {Key="server.OnSendingHeaders", Type="Action<Action<object>, object>", Name="OnSendingHeaders", Init=Init.No},
|
|
||||||
new {Key="server.Capabilities", Type="IDictionary<string, object>", Name="ServerCapabilities", Init=Init.No},
|
|
||||||
|
|
||||||
// ServerVariable keys
|
|
||||||
new {Key="server.RemoteIpAddress", Type="string", Name="RemoteIpAddress", Init=Init.Yes},
|
|
||||||
new {Key="server.RemotePort", Type="string", Name="RemotePort", Init=Init.Yes},
|
|
||||||
new {Key="server.LocalIpAddress", Type="string", Name="LocalIpAddress", Init=Init.Yes},
|
|
||||||
new {Key="server.LocalPort", Type="string", Name="LocalPort", Init=Init.Yes},
|
|
||||||
new {Key="server.IsLocal", Type="bool", Name="IsLocal", Init=Init.Yes},
|
|
||||||
new {Key="server.ConnectionId", Type="object", Name="ConnectionId", Init=Init.No},
|
|
||||||
new {Key="server.ConnectionDisconnect", Type="CancellationToken", Name="ConnectionDisconnect", Init=Init.No},
|
|
||||||
|
|
||||||
// SSL
|
|
||||||
new { Key="ssl.ClientCertificate", Type="object", Name="ClientCert", Init=Init.No},
|
|
||||||
new { Key="ssl.LoadClientCertAsync", Type="Func<Task>", Name="LoadClientCert", Init=Init.No },
|
|
||||||
new { Key="ssl.ChannelBinding", Type="ChannelBinding", Name="ChannelBinding", Init=Init.Maybe },
|
|
||||||
|
|
||||||
// SendFile keys
|
|
||||||
new {Key="sendfile.SendAsync", Type="Func<string, long, long?, CancellationToken, Task>", Name="SendFileAsync", Init=Init.No},
|
|
||||||
|
|
||||||
// Opaque keys
|
|
||||||
new {Key="opaque.Upgrade", Type="OpaqueUpgrade", Name="OpaqueUpgrade", Init=Init.Maybe},
|
|
||||||
|
|
||||||
// Server specific keys
|
|
||||||
new { Key="Microsoft.AspNet.Server.WebListener.OwinWebListener", Type="OwinWebListener", Name="Listener", Init=Init.No},
|
|
||||||
}.Select((prop, Index)=>new {prop.Key, prop.Type, prop.Name, prop.Init, Index});
|
|
||||||
|
|
||||||
var lengths = props.OrderBy(prop=>prop.Key.Length).GroupBy(prop=>prop.Key.Length);
|
|
||||||
|
|
||||||
Func<int,string> IsSet = Index => "((_flag" + (Index / 32) + " & 0x" + (1<<(Index % 32)).ToString("x") + "u) != 0)";
|
|
||||||
Func<int,string> Set = Index => "_flag" + (Index / 32) + " |= 0x" + (1<<(Index % 32)).ToString("x") + "u";
|
|
||||||
Func<int,string> Clear = Index => "_flag" + (Index / 32) + " &= ~0x" + (1<<(Index % 32)).ToString("x") + "u";
|
|
||||||
|
|
||||||
Func<int,string> IsInitRequired = Index => "((_initFlag" + (Index / 32) + " & 0x" + (1<<(Index % 32)).ToString("x") + "u) != 0)";
|
|
||||||
Func<int,string> IsInitCompleted = Index => "((_initFlag" + (Index / 32) + " & 0x" + (1<<(Index % 32)).ToString("x") + "u) == 0)";
|
|
||||||
Func<int,string> CompleteInit = Index => "_initFlag" + (Index / 32) + " &= ~0x" + (1<<(Index % 32)).ToString("x") + "u";
|
|
||||||
|
|
||||||
#>
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
// <copyright>
|
|
||||||
// Copyright (c) Katana Contributors. All rights reserved.
|
|
||||||
// </copyright>
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
// <auto-generated />
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.CodeDom.Compiler;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Security.Authentication.ExtendedProtection;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.Owin.Host.WebListener
|
|
||||||
{
|
|
||||||
using OpaqueUpgrade = Action<IDictionary<string, object>, Func<IDictionary<string, object>, Task>>;
|
|
||||||
|
|
||||||
[GeneratedCode("TextTemplatingFileGenerator", "")]
|
|
||||||
internal partial class CallEnvironment
|
|
||||||
{
|
|
||||||
// Mark all fields with delay initialization support as set.
|
|
||||||
private UInt32 _flag0 = 0x<#=props.Aggregate(0, (agg,p) => agg | (((p.Init != Init.No) && (p.Index/32==0) ? 1:0)<<p.Index)).ToString("x")#>u;
|
|
||||||
private UInt32 _flag1 = 0x<#=props.Aggregate(0, (agg,p) => agg | (((p.Init != Init.No) && (p.Index/32==1) ? 1:0)<<p.Index)).ToString("x")#>u;
|
|
||||||
// Mark all fields with delay initialization support as requiring initialization.
|
|
||||||
private UInt32 _initFlag0 = 0x<#=props.Aggregate(0, (agg,p) => agg | (((p.Init != Init.No) && (p.Index/32==0) ? 1:0)<<p.Index)).ToString("x")#>u;
|
|
||||||
private UInt32 _initFlag1 = 0x<#=props.Aggregate(0, (agg,p) => agg | (((p.Init != Init.No) && (p.Index/32==1) ? 1:0)<<p.Index)).ToString("x")#>u;
|
|
||||||
|
|
||||||
internal interface IPropertySource
|
|
||||||
{
|
|
||||||
<# foreach(var prop in props) { #>
|
|
||||||
<# if (prop.Init == Init.Yes) { #>
|
|
||||||
<#=prop.Type#> Get<#=prop.Name#>();
|
|
||||||
<# } #>
|
|
||||||
<# if (prop.Init == Init.Maybe) { #>
|
|
||||||
bool TryGet<#=prop.Name#>(ref <#=prop.Type#> value);
|
|
||||||
<# } #>
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
|
|
||||||
<# foreach(var prop in props) { #>
|
|
||||||
private <#=prop.Type#> _<#=prop.Name#>;
|
|
||||||
<# } #>
|
|
||||||
|
|
||||||
<# foreach(var prop in props) { #>
|
|
||||||
<# // call TryGet once if init flag is set, clear value flag if TryGet returns false
|
|
||||||
if (prop.Init == Init.Maybe) { #>
|
|
||||||
bool InitProperty<#=prop.Name#>()
|
|
||||||
{
|
|
||||||
if (!_propertySource.TryGet<#=prop.Name#>(ref _<#=prop.Name#>))
|
|
||||||
{
|
|
||||||
<#=Clear(prop.Index)#>;
|
|
||||||
<#=CompleteInit(prop.Index)#>;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
<#=CompleteInit(prop.Index)#>;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
<# } #>
|
|
||||||
<# } #>
|
|
||||||
<# foreach(var prop in props) { #>
|
|
||||||
internal <#=prop.Type#> <#=prop.Name#>
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
<# // call Get once if init flag is set
|
|
||||||
if (prop.Init == Init.Yes) { #>
|
|
||||||
if (<#=IsInitRequired(prop.Index)#>)
|
|
||||||
{
|
|
||||||
_<#=prop.Name#> = _propertySource.Get<#=prop.Name#>();
|
|
||||||
<#=CompleteInit(prop.Index)#>;
|
|
||||||
}
|
|
||||||
<# } #>
|
|
||||||
<# // call TryGet once if init flag is set, clear value flag if TryGet returns false
|
|
||||||
if (prop.Init == Init.Maybe) { #>
|
|
||||||
if (<#=IsInitRequired(prop.Index)#>)
|
|
||||||
{
|
|
||||||
InitProperty<#=prop.Name#>();
|
|
||||||
}
|
|
||||||
<# } #>
|
|
||||||
return _<#=prop.Name#>;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
<# // clear init flag - the assigned value is definitive
|
|
||||||
if (prop.Init != Init.No) { #>
|
|
||||||
<#=CompleteInit(prop.Index)#>;
|
|
||||||
<# } #>
|
|
||||||
<#=Set(prop.Index)#>;
|
|
||||||
_<#=prop.Name#> = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<# } #>
|
|
||||||
private bool PropertiesContainsKey(string key)
|
|
||||||
{
|
|
||||||
switch (key.Length)
|
|
||||||
{
|
|
||||||
<# foreach(var length in lengths) { #>
|
|
||||||
case <#=length.Key#>:
|
|
||||||
<# foreach(var prop in length) { #>
|
|
||||||
if (<#=IsSet(prop.Index)#> && string.Equals(key, "<#=prop.Key#>", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
<# // variable maybe init might revert
|
|
||||||
if (prop.Init == Init.Maybe) { #>
|
|
||||||
if (<#=IsInitCompleted(prop.Index)#> || InitProperty<#=prop.Name#>())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
<# } else { #>
|
|
||||||
return true;
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
<# } #>
|
|
||||||
break;
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool PropertiesTryGetValue(string key, out object value)
|
|
||||||
{
|
|
||||||
switch (key.Length)
|
|
||||||
{
|
|
||||||
<# foreach(var length in lengths) { #>
|
|
||||||
case <#=length.Key#>:
|
|
||||||
<# foreach(var prop in length) { #>
|
|
||||||
if (<#=IsSet(prop.Index)#> && string.Equals(key, "<#=prop.Key#>", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
value = <#=prop.Name#>;
|
|
||||||
<# if (prop.Init == Init.Maybe) { #>
|
|
||||||
// Delayed initialization in the property getter may determine that the element is not actually present
|
|
||||||
if (!<#=IsSet(prop.Index)#>)
|
|
||||||
{
|
|
||||||
value = default(<#=prop.Type#>);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
<# } #>
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
<# } #>
|
|
||||||
break;
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
value = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool PropertiesTrySetValue(string key, object value)
|
|
||||||
{
|
|
||||||
switch (key.Length)
|
|
||||||
{
|
|
||||||
<# foreach(var length in lengths) { #>
|
|
||||||
case <#=length.Key#>:
|
|
||||||
<# foreach(var prop in length) { #>
|
|
||||||
if (string.Equals(key, "<#=prop.Key#>", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
<#=prop.Name#> = (<#=prop.Type#>)value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
<# } #>
|
|
||||||
break;
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool PropertiesTryRemove(string key)
|
|
||||||
{
|
|
||||||
switch (key.Length)
|
|
||||||
{
|
|
||||||
<# foreach(var length in lengths) { #>
|
|
||||||
case <#=length.Key#>:
|
|
||||||
<# foreach(var prop in length) { #>
|
|
||||||
if (<#=IsSet(prop.Index)#> && string.Equals(key, "<#=prop.Key#>", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
<# if (prop.Init != Init.No) { #>
|
|
||||||
<#=CompleteInit(prop.Index)#>;
|
|
||||||
<# } #>
|
|
||||||
<#=Clear(prop.Index)#>;
|
|
||||||
_<#=prop.Name#> = default(<#=prop.Type#>);
|
|
||||||
// This can return true incorrectly for values that delayed initialization may determine are not actually present.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
<# } #>
|
|
||||||
break;
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<string> PropertiesKeys()
|
|
||||||
{
|
|
||||||
<# foreach(var prop in props) { #>
|
|
||||||
if (<#=IsSet(prop.Index)#>)
|
|
||||||
{
|
|
||||||
<# if (prop.Init == Init.Maybe) { #>
|
|
||||||
if (<#=IsInitCompleted(prop.Index)#> || InitProperty<#=prop.Name#>())
|
|
||||||
{
|
|
||||||
yield return "<#=prop.Key#>";
|
|
||||||
}
|
|
||||||
<# } else { #>
|
|
||||||
yield return "<#=prop.Key#>";
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<object> PropertiesValues()
|
|
||||||
{
|
|
||||||
<# foreach(var prop in props) { #>
|
|
||||||
if (<#=IsSet(prop.Index)#>)
|
|
||||||
{
|
|
||||||
<# if (prop.Init == Init.Maybe) { #>
|
|
||||||
if (<#=IsInitCompleted(prop.Index)#> || InitProperty<#=prop.Name#>())
|
|
||||||
{
|
|
||||||
yield return <#=prop.Name#>;
|
|
||||||
}
|
|
||||||
<# } else { #>
|
|
||||||
yield return <#=prop.Name#>;
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<KeyValuePair<string, object>> PropertiesEnumerable()
|
|
||||||
{
|
|
||||||
<# foreach(var prop in props) { #>
|
|
||||||
if (<#=IsSet(prop.Index)#>)
|
|
||||||
{
|
|
||||||
<# if (prop.Init == Init.Maybe) { #>
|
|
||||||
if (<#=IsInitCompleted(prop.Index)#> || InitProperty<#=prop.Name#>())
|
|
||||||
{
|
|
||||||
yield return new KeyValuePair<string, object>("<#=prop.Key#>", <#=prop.Name#>);
|
|
||||||
}
|
|
||||||
<# } else { #>
|
|
||||||
yield return new KeyValuePair<string, object>("<#=prop.Key#>", <#=prop.Name#>);
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
<# } #>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,165 +0,0 @@
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// <copyright file="CallEnvironment.cs" company="Microsoft">
|
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// </copyright>
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// Copyright 2011-2012 Katana contributors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener
|
|
||||||
{
|
|
||||||
internal partial class CallEnvironment : IDictionary<string, object>
|
|
||||||
{
|
|
||||||
private static readonly IDictionary<string, object> WeakNilEnvironment = new NilEnvDictionary();
|
|
||||||
|
|
||||||
private readonly IPropertySource _propertySource;
|
|
||||||
private IDictionary<string, object> _extra = WeakNilEnvironment;
|
|
||||||
|
|
||||||
internal CallEnvironment(IPropertySource propertySource)
|
|
||||||
{
|
|
||||||
_propertySource = propertySource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IDictionary<string, object> Extra
|
|
||||||
{
|
|
||||||
get { return _extra; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private IDictionary<string, object> StrongExtra
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_extra == WeakNilEnvironment)
|
|
||||||
{
|
|
||||||
Interlocked.CompareExchange(ref _extra, new Dictionary<string, object>(), WeakNilEnvironment);
|
|
||||||
}
|
|
||||||
return _extra;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool IsExtraDictionaryCreated
|
|
||||||
{
|
|
||||||
get { return _extra != WeakNilEnvironment; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public object this[string key]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
object value;
|
|
||||||
return PropertiesTryGetValue(key, out value) ? value : Extra[key];
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (!PropertiesTrySetValue(key, value))
|
|
||||||
{
|
|
||||||
StrongExtra[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(string key, object value)
|
|
||||||
{
|
|
||||||
if (!PropertiesTrySetValue(key, value))
|
|
||||||
{
|
|
||||||
StrongExtra.Add(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ContainsKey(string key)
|
|
||||||
{
|
|
||||||
return PropertiesContainsKey(key) || Extra.ContainsKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ICollection<string> Keys
|
|
||||||
{
|
|
||||||
get { return PropertiesKeys().Concat(Extra.Keys).ToArray(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Remove(string key)
|
|
||||||
{
|
|
||||||
// Although this is a mutating operation, Extra is used instead of StrongExtra,
|
|
||||||
// because if a real dictionary has not been allocated the default behavior of the
|
|
||||||
// nil dictionary is perfectly fine.
|
|
||||||
return PropertiesTryRemove(key) || Extra.Remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetValue(string key, out object value)
|
|
||||||
{
|
|
||||||
return PropertiesTryGetValue(key, out value) || Extra.TryGetValue(key, out value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ICollection<object> Values
|
|
||||||
{
|
|
||||||
get { return PropertiesValues().Concat(Extra.Values).ToArray(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(KeyValuePair<string, object> item)
|
|
||||||
{
|
|
||||||
((IDictionary<string, object>)this).Add(item.Key, item.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
foreach (var key in PropertiesKeys())
|
|
||||||
{
|
|
||||||
PropertiesTryRemove(key);
|
|
||||||
}
|
|
||||||
Extra.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Contains(KeyValuePair<string, object> item)
|
|
||||||
{
|
|
||||||
object value;
|
|
||||||
return ((IDictionary<string, object>)this).TryGetValue(item.Key, out value) && Object.Equals(value, item.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
|
|
||||||
{
|
|
||||||
PropertiesEnumerable().Concat(Extra).ToArray().CopyTo(array, arrayIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get { return PropertiesKeys().Count() + Extra.Count; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsReadOnly
|
|
||||||
{
|
|
||||||
get { return false; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Remove(KeyValuePair<string, object> item)
|
|
||||||
{
|
|
||||||
return ((IDictionary<string, object>)this).Contains(item) &&
|
|
||||||
((IDictionary<string, object>)this).Remove(item.Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
|
||||||
{
|
|
||||||
return PropertiesEnumerable().Concat(Extra).GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
{
|
|
||||||
return ((IDictionary<string, object>)this).GetEnumerator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,14 +10,18 @@ using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
#if NET45
|
||||||
using System.Security.Authentication.ExtendedProtection;
|
using System.Security.Authentication.ExtendedProtection;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
#endif
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.HttpFeature;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener
|
namespace Microsoft.AspNet.Server.WebListener
|
||||||
{
|
{
|
||||||
internal sealed unsafe class Request : IDisposable
|
internal sealed class Request : IHttpRequestInformation, IHttpConnection, IHttpTransportLayerSecurity, IDisposable
|
||||||
{
|
{
|
||||||
private RequestContext _requestContext;
|
private RequestContext _requestContext;
|
||||||
private NativeRequestContext _nativeRequestContext;
|
private NativeRequestContext _nativeRequestContext;
|
||||||
|
@ -27,29 +31,46 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
private ulong _contextId;
|
private ulong _contextId;
|
||||||
|
|
||||||
private SslStatus _sslStatus;
|
private SslStatus _sslStatus;
|
||||||
|
private string _scheme;
|
||||||
|
|
||||||
private string _httpMethod;
|
private string _httpMethod;
|
||||||
private Version _httpVersion;
|
private Version _httpVersion;
|
||||||
|
private string _httpProtocolVersion;
|
||||||
|
|
||||||
private Uri _requestUri;
|
// private Uri _requestUri;
|
||||||
private string _rawUrl;
|
private string _rawUrl;
|
||||||
private string _cookedUrlHost;
|
private string _cookedUrlHost;
|
||||||
private string _cookedUrlPath;
|
private string _cookedUrlPath;
|
||||||
private string _cookedUrlQuery;
|
private string _cookedUrlQuery;
|
||||||
|
private string _pathBase;
|
||||||
|
private string _path;
|
||||||
|
|
||||||
private RequestHeaders _headers;
|
#if NET45
|
||||||
|
private X509Certificate _clientCert;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private IDictionary<string, string[]> _headers;
|
||||||
private BoundaryType _contentBoundaryType;
|
private BoundaryType _contentBoundaryType;
|
||||||
private long _contentLength;
|
private long _contentLength;
|
||||||
|
private Stream _nativeStream;
|
||||||
private Stream _requestStream;
|
private Stream _requestStream;
|
||||||
|
|
||||||
private SocketAddress _localEndPoint;
|
private SocketAddress _localEndPoint;
|
||||||
private SocketAddress _remoteEndPoint;
|
private SocketAddress _remoteEndPoint;
|
||||||
|
#if NET45
|
||||||
|
private IPAddress _remoteIpAddress;
|
||||||
|
private IPAddress _localIpAddress;
|
||||||
|
#endif
|
||||||
|
private int? _remotePort;
|
||||||
|
private int? _localPort;
|
||||||
|
private bool? _isLocal;
|
||||||
|
|
||||||
private IPrincipal _user;
|
private IPrincipal _user;
|
||||||
|
|
||||||
private bool _isDisposed = false;
|
private bool _isDisposed = false;
|
||||||
private CancellationTokenRegistration _disconnectRegistration;
|
private CancellationTokenRegistration _disconnectRegistration;
|
||||||
|
|
||||||
internal Request(RequestContext httpContext, NativeRequestContext memoryBlob)
|
internal unsafe Request(RequestContext httpContext, NativeRequestContext memoryBlob)
|
||||||
{
|
{
|
||||||
// TODO: Verbose log
|
// TODO: Verbose log
|
||||||
_requestContext = httpContext;
|
_requestContext = httpContext;
|
||||||
|
@ -82,6 +103,24 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
_cookedUrlQuery = Marshal.PtrToStringUni((IntPtr)cookedUrl.pQueryString, cookedUrl.QueryStringLength / 2);
|
_cookedUrlQuery = Marshal.PtrToStringUni((IntPtr)cookedUrl.pQueryString, cookedUrl.QueryStringLength / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Prefix prefix = httpContext.Server.UriPrefixes[(int)_contextId];
|
||||||
|
string orriginalPath = RequestPath;
|
||||||
|
|
||||||
|
// These paths are both unescaped already.
|
||||||
|
if (orriginalPath.Length == prefix.Path.Length - 1)
|
||||||
|
{
|
||||||
|
// They matched exactly except for the trailing slash.
|
||||||
|
_pathBase = orriginalPath;
|
||||||
|
_path = string.Empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// url: /base/path, prefix: /base/, base: /base, path: /path
|
||||||
|
// url: /, prefix: /, base: , path: /
|
||||||
|
_pathBase = orriginalPath.Substring(0, prefix.Path.Length - 1);
|
||||||
|
_path = orriginalPath.Substring(prefix.Path.Length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
int major = memoryBlob.RequestBlob->Version.MajorVersion;
|
int major = memoryBlob.RequestBlob->Version.MajorVersion;
|
||||||
int minor = memoryBlob.RequestBlob->Version.MinorVersion;
|
int minor = memoryBlob.RequestBlob->Version.MinorVersion;
|
||||||
if (major == 1 && minor == 1)
|
if (major == 1 && minor == 1)
|
||||||
|
@ -155,16 +194,16 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Without the leading ?
|
// With the leading ?, if any
|
||||||
internal string Query
|
public string QueryString
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(_cookedUrlQuery))
|
return _cookedUrlQuery ?? string.Empty;
|
||||||
{
|
}
|
||||||
return _cookedUrlQuery.Substring(1);
|
set
|
||||||
}
|
{
|
||||||
return string.Empty;
|
_cookedUrlQuery = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +215,25 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET45
|
||||||
|
X509Certificate IHttpTransportLayerSecurity.ClientCertificate
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_clientCert == null)
|
||||||
|
{
|
||||||
|
// TODO: Sync
|
||||||
|
((IHttpTransportLayerSecurity)this).LoadAsync().Wait();
|
||||||
|
}
|
||||||
|
return _clientCert;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_clientCert = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// TODO: Move this to the constructor, that's where it will be called.
|
// TODO: Move this to the constructor, that's where it will be called.
|
||||||
internal long ContentLength64
|
internal long ContentLength64
|
||||||
{
|
{
|
||||||
|
@ -210,40 +268,101 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IDictionary<string, string[]> Headers
|
public IDictionary<string, string[]> Headers
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _headers;
|
return _headers;
|
||||||
}
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("value");
|
||||||
|
}
|
||||||
|
_headers = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal string HttpMethod
|
public string Method
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _httpMethod;
|
return _httpMethod;
|
||||||
}
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_httpMethod = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Stream InputStream
|
internal Stream NativeStream
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_nativeStream == null)
|
||||||
|
{
|
||||||
|
// TODO: Move this to the constructor (or a lazy Env dictionary)
|
||||||
|
_nativeStream = HasEntityBody ? new RequestStream(RequestContext) : Stream.Null;
|
||||||
|
}
|
||||||
|
return _nativeStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream Body
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_requestStream == null)
|
if (_requestStream == null)
|
||||||
{
|
{
|
||||||
// TODO: Move this to the constructor (or a lazy Env dictionary)
|
// TODO: Move this to the constructor (or a lazy Env dictionary)
|
||||||
_requestStream = HasEntityBody ? new RequestStream(RequestContext) : Stream.Null;
|
_requestStream = NativeStream;
|
||||||
}
|
}
|
||||||
return _requestStream;
|
return _requestStream;
|
||||||
}
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_requestStream = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool IsLocal
|
public string PathBase
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return LocalEndPoint.GetIPAddressString().Equals(RemoteEndPoint.GetIPAddressString());
|
return _pathBase;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_pathBase = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Path
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _path;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_path = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsLocal
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!_isLocal.HasValue)
|
||||||
|
{
|
||||||
|
_isLocal = LocalEndPoint.GetIPAddressString().Equals(RemoteEndPoint.GetIPAddressString());
|
||||||
|
}
|
||||||
|
return _isLocal.Value;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isLocal = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +373,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
return _sslStatus != SslStatus.Insecure;
|
return _sslStatus != SslStatus.Insecure;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
internal string RawUrl
|
internal string RawUrl
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -262,7 +381,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
return _rawUrl;
|
return _rawUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
internal Version ProtocolVersion
|
internal Version ProtocolVersion
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -271,22 +390,34 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal string Protocol
|
public string Protocol
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_httpVersion.Major == 1)
|
if (_httpProtocolVersion == null)
|
||||||
{
|
{
|
||||||
if (_httpVersion.Minor == 1)
|
if (_httpVersion.Major == 1)
|
||||||
{
|
{
|
||||||
return "HTTP/1.1";
|
if (_httpVersion.Minor == 1)
|
||||||
|
{
|
||||||
|
_httpProtocolVersion = "HTTP/1.1";
|
||||||
|
}
|
||||||
|
else if (_httpVersion.Minor == 0)
|
||||||
|
{
|
||||||
|
_httpProtocolVersion = "HTTP/1.0";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (_httpVersion.Minor == 0)
|
else
|
||||||
{
|
{
|
||||||
return "HTTP/1.0";
|
_httpProtocolVersion = "HTTP/" + _httpVersion.ToString(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "HTTP/" + _httpVersion.ToString(2);
|
return _httpProtocolVersion;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// TODO: Set _httpVersion?
|
||||||
|
_httpProtocolVersion = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,15 +457,87 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
return _localEndPoint;
|
return _localEndPoint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if NET45
|
||||||
internal string RequestScheme
|
public IPAddress RemoteIpAddress
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return IsSecureConnection ? Constants.HttpsScheme : Constants.HttpScheme;
|
if (_remoteIpAddress == null)
|
||||||
|
{
|
||||||
|
_remoteIpAddress = IPAddress.Parse(RemoteEndPoint.GetIPAddressString()); // TODO: Create directly from bytes
|
||||||
|
}
|
||||||
|
return _remoteIpAddress;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_remoteIpAddress = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IPAddress LocalIpAddress
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_localIpAddress == null)
|
||||||
|
{
|
||||||
|
_localIpAddress = IPAddress.Parse(LocalEndPoint.GetIPAddressString()); // TODO: Create directly from bytes
|
||||||
|
}
|
||||||
|
return _localIpAddress;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_localIpAddress = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
public int RemotePort
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!_remotePort.HasValue)
|
||||||
|
{
|
||||||
|
_remotePort = RemoteEndPoint.GetPort();
|
||||||
|
}
|
||||||
|
return _remotePort.Value;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_remotePort = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int LocalPort
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!_localPort.HasValue)
|
||||||
|
{
|
||||||
|
_localPort = LocalEndPoint.GetPort();
|
||||||
|
}
|
||||||
|
return _localPort.Value;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_localPort = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Scheme
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_scheme == null)
|
||||||
|
{
|
||||||
|
_scheme = IsSecureConnection ? Constants.HttpsScheme : Constants.HttpScheme;
|
||||||
|
}
|
||||||
|
return _scheme;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_scheme = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
internal Uri RequestUri
|
internal Uri RequestUri
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -348,7 +551,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
return _requestUri;
|
return _requestUri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
internal string RequestPath
|
internal string RequestPath
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -391,6 +594,47 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Populates the client certificate. The result may be null if there is no client cert.
|
||||||
|
// TODO: Does it make sense for this to be invoked multiple times (e.g. renegotiate)? Client and server code appear to
|
||||||
|
// enable this, but it's unclear what Http.Sys would do.
|
||||||
|
async Task IHttpTransportLayerSecurity.LoadAsync()
|
||||||
|
{
|
||||||
|
if (SslStatus == SslStatus.Insecure)
|
||||||
|
{
|
||||||
|
// Non-SSL
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: Verbose log
|
||||||
|
#if NET45
|
||||||
|
if (_clientCert != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientCertLoader certLoader = new ClientCertLoader(RequestContext);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await certLoader.LoadClientCertificateAsync().SupressContext();
|
||||||
|
// Populate the environment.
|
||||||
|
if (certLoader.ClientCert != null)
|
||||||
|
{
|
||||||
|
_clientCert = certLoader.ClientCert;
|
||||||
|
}
|
||||||
|
// TODO: Expose errors and exceptions?
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
if (certLoader != null)
|
||||||
|
{
|
||||||
|
certLoader.Dispose();
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
throw new NotImplementedException();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Use this to save the blob from dispose if this object was never used (never given to a user) and is about to be
|
// Use this to save the blob from dispose if this object was never used (never given to a user) and is about to be
|
||||||
// disposed.
|
// disposed.
|
||||||
internal void DetachBlob(NativeRequestContext memoryBlob)
|
internal void DetachBlob(NativeRequestContext memoryBlob)
|
||||||
|
@ -419,9 +663,9 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
_nativeRequestContext = null;
|
_nativeRequestContext = null;
|
||||||
}
|
}
|
||||||
_disconnectRegistration.Dispose();
|
_disconnectRegistration.Dispose();
|
||||||
if (_requestStream != null)
|
if (_nativeStream != null)
|
||||||
{
|
{
|
||||||
_requestStream.Dispose();
|
_nativeStream.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,9 +679,9 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
|
|
||||||
internal void SwitchToOpaqueMode()
|
internal void SwitchToOpaqueMode()
|
||||||
{
|
{
|
||||||
if (_requestStream == null || _requestStream == Stream.Null)
|
if (_nativeStream == null || _nativeStream == Stream.Null)
|
||||||
{
|
{
|
||||||
_requestStream = new RequestStream(RequestContext);
|
_nativeStream = new RequestStream(RequestContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,17 +15,19 @@ using System.Runtime.InteropServices;
|
||||||
using System.Security.Authentication.ExtendedProtection;
|
using System.Security.Authentication.ExtendedProtection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.FeatureModel;
|
||||||
|
using Microsoft.AspNet.HttpFeature;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener
|
namespace Microsoft.AspNet.Server.WebListener
|
||||||
{
|
{
|
||||||
using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>;
|
using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>;
|
||||||
using OpaqueFunc = Func<IDictionary<string, object>, Task>;
|
using OpaqueFunc = Func<IDictionary<string, object>, Task>;
|
||||||
|
|
||||||
internal sealed class RequestContext : IDisposable, CallEnvironment.IPropertySource
|
internal sealed class RequestContext : IDisposable
|
||||||
{
|
{
|
||||||
private static readonly string[] ZeroContentLength = new[] { "0" };
|
private static readonly string[] ZeroContentLength = new[] { "0" };
|
||||||
|
|
||||||
private CallEnvironment _environment;
|
private FeatureCollection _features;
|
||||||
private OwinWebListener _server;
|
private OwinWebListener _server;
|
||||||
private Request _request;
|
private Request _request;
|
||||||
private Response _response;
|
private Response _response;
|
||||||
|
@ -41,17 +43,17 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
_memoryBlob = memoryBlob;
|
_memoryBlob = memoryBlob;
|
||||||
_request = new Request(this, _memoryBlob);
|
_request = new Request(this, _memoryBlob);
|
||||||
_response = new Response(this);
|
_response = new Response(this);
|
||||||
_environment = new CallEnvironment(this);
|
|
||||||
_cts = new CancellationTokenSource();
|
_cts = new CancellationTokenSource();
|
||||||
|
|
||||||
PopulateEnvironment();
|
_features = new FeatureCollection();
|
||||||
|
PopulateFeatures();
|
||||||
|
|
||||||
_request.ReleasePins();
|
_request.ReleasePins();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal CallEnvironment Environment
|
internal IFeatureCollection Features
|
||||||
{
|
{
|
||||||
get { return _environment; }
|
get { return _features; }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Request Request
|
internal Request Request
|
||||||
|
@ -99,105 +101,31 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PopulateEnvironment()
|
private void PopulateFeatures()
|
||||||
{
|
{
|
||||||
// General
|
_features.Add(typeof(IHttpRequestInformation), Request);
|
||||||
_environment.OwinVersion = Constants.OwinVersion;
|
_features.Add(typeof(IHttpConnection), Request);
|
||||||
_environment.CallCancelled = _cts.Token;
|
if (Request.IsSecureConnection)
|
||||||
|
{
|
||||||
|
// TODO: Should this feature be conditional? Should we add this for HTTP requests?
|
||||||
|
_features.Add(typeof(IHttpTransportLayerSecurity), Request);
|
||||||
|
}
|
||||||
|
_features.Add(typeof(IHttpResponseInformation), Response);
|
||||||
|
_features.Add(typeof(IHttpSendFile), Response);
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// _environment.CallCancelled = _cts.Token;
|
||||||
|
// _environment.User = _request.User;
|
||||||
|
// Opaque/WebSockets
|
||||||
|
// Channel binding
|
||||||
|
|
||||||
|
/*
|
||||||
// Server
|
// Server
|
||||||
_environment.ServerCapabilities = _server.Capabilities;
|
|
||||||
_environment.Listener = _server;
|
_environment.Listener = _server;
|
||||||
|
_environment.ConnectionId = _request.ConnectionId;
|
||||||
// Request
|
*/
|
||||||
_environment.RequestProtocol = _request.Protocol;
|
|
||||||
_environment.RequestMethod = _request.HttpMethod;
|
|
||||||
_environment.RequestScheme = _request.RequestScheme;
|
|
||||||
_environment.RequestQueryString = _request.Query;
|
|
||||||
_environment.RequestHeaders = _request.Headers;
|
|
||||||
|
|
||||||
SetPaths();
|
|
||||||
|
|
||||||
_environment.ConnectionId = _request.ConnectionId;
|
|
||||||
|
|
||||||
if (_request.IsSecureConnection)
|
|
||||||
{
|
|
||||||
_environment.LoadClientCert = LoadClientCertificateAsync;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_request.User != null)
|
|
||||||
{
|
|
||||||
_environment.User = _request.User;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Response
|
|
||||||
_environment.ResponseStatusCode = 200;
|
|
||||||
_environment.ResponseHeaders = _response.Headers;
|
|
||||||
_environment.ResponseBody = _response.OutputStream;
|
|
||||||
_environment.SendFileAsync = _response.SendFileAsync;
|
|
||||||
|
|
||||||
_environment.OnSendingHeaders = _response.RegisterForOnSendingHeaders;
|
|
||||||
|
|
||||||
Contract.Assert(!_environment.IsExtraDictionaryCreated,
|
|
||||||
"All server keys should have a reserved slot in the environment.");
|
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
// Find the closest matching prefix and use it to separate the request path in to path and base path.
|
|
||||||
// Scheme and port must match. Path will use a longest match. Host names are more complicated due to
|
|
||||||
// wildcards, IP addresses, etc.
|
|
||||||
private void SetPaths()
|
|
||||||
{
|
|
||||||
Prefix prefix = _server.UriPrefixes[(int)Request.ContextId];
|
|
||||||
string orriginalPath = _request.RequestPath;
|
|
||||||
|
|
||||||
// These paths are both unescaped already.
|
|
||||||
if (orriginalPath.Length == prefix.Path.Length - 1)
|
|
||||||
{
|
|
||||||
// They matched exactly except for the trailing slash.
|
|
||||||
_environment.RequestPathBase = orriginalPath;
|
|
||||||
_environment.RequestPath = string.Empty;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// url: /base/path, prefix: /base/, base: /base, path: /path
|
|
||||||
// url: /, prefix: /, base: , path: /
|
|
||||||
_environment.RequestPathBase = orriginalPath.Substring(0, prefix.Path.Length - 1);
|
|
||||||
_environment.RequestPath = orriginalPath.Substring(prefix.Path.Length - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lazy environment init
|
|
||||||
|
|
||||||
public Stream GetRequestBody()
|
|
||||||
{
|
|
||||||
return _request.InputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetRemoteIpAddress()
|
|
||||||
{
|
|
||||||
return _request.RemoteEndPoint.GetIPAddressString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetRemotePort()
|
|
||||||
{
|
|
||||||
return _request.RemoteEndPoint.GetPort().ToString(CultureInfo.InvariantCulture.NumberFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetLocalIpAddress()
|
|
||||||
{
|
|
||||||
return _request.LocalEndPoint.GetIPAddressString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetLocalPort()
|
|
||||||
{
|
|
||||||
return _request.LocalEndPoint.GetPort().ToString(CultureInfo.InvariantCulture.NumberFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetIsLocal()
|
|
||||||
{
|
|
||||||
return _request.IsLocal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetOpaqueUpgrade(ref Action<IDictionary<string, object>, OpaqueFunc> value)
|
public bool TryGetOpaqueUpgrade(ref Action<IDictionary<string, object>, OpaqueFunc> value)
|
||||||
{
|
{
|
||||||
if (_request.IsUpgradable)
|
if (_request.IsUpgradable)
|
||||||
|
@ -213,6 +141,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
value = Server.GetChannelBinding(Request.ConnectionId, Request.IsSecureConnection);
|
value = Server.GetChannelBinding(Request.ConnectionId, Request.IsSecureConnection);
|
||||||
return value != null;
|
return value != null;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
@ -290,42 +219,6 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populates the environment ClicentCertificate. The result may be null if there is no client cert.
|
|
||||||
// TODO: Does it make sense for this to be invoked multiple times (e.g. renegotiate)? Client and server code appear to
|
|
||||||
// enable this, but it's unclear what Http.Sys would do.
|
|
||||||
private async Task LoadClientCertificateAsync()
|
|
||||||
{
|
|
||||||
if (Request.SslStatus == SslStatus.Insecure)
|
|
||||||
{
|
|
||||||
// Non-SSL
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO: Verbose log
|
|
||||||
#if NET45
|
|
||||||
ClientCertLoader certLoader = new ClientCertLoader(this);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await certLoader.LoadClientCertificateAsync().SupressContext();
|
|
||||||
// Populate the environment.
|
|
||||||
if (certLoader.ClientCert != null)
|
|
||||||
{
|
|
||||||
Environment.ClientCert = certLoader.ClientCert;
|
|
||||||
}
|
|
||||||
// TODO: Expose errors and exceptions?
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
if (certLoader != null)
|
|
||||||
{
|
|
||||||
certLoader.Dispose();
|
|
||||||
}
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
throw new NotImplementedException();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OpaqueUpgrade(IDictionary<string, object> parameters, OpaqueFunc callback)
|
internal void OpaqueUpgrade(IDictionary<string, object> parameters, OpaqueFunc callback)
|
||||||
{
|
{
|
||||||
// Parameters are ignored for now
|
// Parameters are ignored for now
|
||||||
|
@ -339,8 +232,8 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the status code and reason phrase
|
// Set the status code and reason phrase
|
||||||
Environment.ResponseStatusCode = (int)HttpStatusCode.SwitchingProtocols;
|
Response.StatusCode = (int)HttpStatusCode.SwitchingProtocols;
|
||||||
Environment.ResponseReasonPhrase = HttpReasonPhrase.Get(HttpStatusCode.SwitchingProtocols);
|
Response.ReasonPhrase = HttpReasonPhrase.Get(HttpStatusCode.SwitchingProtocols);
|
||||||
|
|
||||||
// Store the callback and process it after the stack unwind.
|
// Store the callback and process it after the stack unwind.
|
||||||
_opaqueCallback = callback;
|
_opaqueCallback = callback;
|
||||||
|
@ -351,7 +244,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
{
|
{
|
||||||
// If an upgrade was requested, perform it
|
// If an upgrade was requested, perform it
|
||||||
if (!Response.SentHeaders && _opaqueCallback != null
|
if (!Response.SentHeaders && _opaqueCallback != null
|
||||||
&& Environment.ResponseStatusCode == (int)HttpStatusCode.SwitchingProtocols)
|
&& Response.StatusCode == (int)HttpStatusCode.SwitchingProtocols)
|
||||||
{
|
{
|
||||||
Response.SendOpaqueUpgrade();
|
Response.SendOpaqueUpgrade();
|
||||||
|
|
||||||
|
@ -368,21 +261,21 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
|
|
||||||
opaqueEnv[Constants.OpaqueVersionKey] = Constants.OpaqueVersion;
|
opaqueEnv[Constants.OpaqueVersionKey] = Constants.OpaqueVersion;
|
||||||
// TODO: Separate CT?
|
// TODO: Separate CT?
|
||||||
opaqueEnv[Constants.OpaqueCallCancelledKey] = Environment.CallCancelled;
|
// opaqueEnv[Constants.OpaqueCallCancelledKey] = Environment.CallCancelled;
|
||||||
|
|
||||||
Request.SwitchToOpaqueMode();
|
Request.SwitchToOpaqueMode();
|
||||||
Response.SwitchToOpaqueMode();
|
Response.SwitchToOpaqueMode();
|
||||||
opaqueEnv[Constants.OpaqueStreamKey] = new OpaqueStream(Request.InputStream, Response.OutputStream);
|
opaqueEnv[Constants.OpaqueStreamKey] = new OpaqueStream(Request.NativeStream, Response.NativeStream);
|
||||||
|
|
||||||
return opaqueEnv;
|
return opaqueEnv;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetFatalResponse()
|
internal void SetFatalResponse()
|
||||||
{
|
{
|
||||||
Environment.ResponseStatusCode = 500;
|
Response.StatusCode = 500;
|
||||||
Environment.ResponseReasonPhrase = string.Empty;
|
Response.ReasonPhrase = string.Empty;
|
||||||
Environment.ResponseHeaders.Clear();
|
Response.Headers.Clear();
|
||||||
Environment.ResponseHeaders.Add(HttpKnownHeaderNames.ContentLength, ZeroContentLength);
|
Response.Headers.Add(HttpKnownHeaderNames.ContentLength, ZeroContentLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,17 @@ using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.HttpFeature;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener
|
namespace Microsoft.AspNet.Server.WebListener
|
||||||
{
|
{
|
||||||
internal sealed unsafe class Response : IDisposable
|
internal sealed unsafe class Response : IHttpResponseInformation, IHttpSendFile, IDisposable
|
||||||
{
|
{
|
||||||
private ResponseState _responseState;
|
private ResponseState _responseState;
|
||||||
private IDictionary<string, string[]> _headers;
|
private IDictionary<string, string[]> _headers;
|
||||||
private ResponseStream _responseStream;
|
private string _reasonPhrase;
|
||||||
|
private ResponseStream _nativeStream;
|
||||||
|
private Stream _responseStream;
|
||||||
private long _contentLength;
|
private long _contentLength;
|
||||||
private BoundaryType _boundaryType;
|
private BoundaryType _boundaryType;
|
||||||
private UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE _nativeResponse;
|
private UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE _nativeResponse;
|
||||||
|
@ -67,31 +70,55 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Stream OutputStream
|
public int StatusCode
|
||||||
|
{
|
||||||
|
get { return _nativeResponse.StatusCode; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value <= 100 || 999 < value)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException("value", value, string.Format(Resources.Exception_InvalidStatusCode, value));
|
||||||
|
}
|
||||||
|
_nativeResponse.StatusCode = (ushort)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ReasonPhrase
|
||||||
|
{
|
||||||
|
get { return _reasonPhrase; }
|
||||||
|
set { _reasonPhrase = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ResponseStream NativeStream
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
EnsureResponseStream();
|
EnsureResponseStream();
|
||||||
return _responseStream;
|
return _nativeStream;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal int GetStatusCode()
|
public Stream Body
|
||||||
{
|
{
|
||||||
int statusCode = _requestContext.Environment.ResponseStatusCode ?? 200;
|
get
|
||||||
if (statusCode <= 100 || statusCode > 999)
|
|
||||||
{
|
{
|
||||||
// TODO: Move this validation to the dictionary facade so it throws when the app sets it, rather than durring send?
|
if (_responseStream == null)
|
||||||
throw new InvalidOperationException(string.Format(Resources.Exception_InvalidStatusCode, statusCode));
|
{
|
||||||
|
_responseStream = NativeStream;
|
||||||
|
}
|
||||||
|
return _responseStream;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_responseStream = value;
|
||||||
}
|
}
|
||||||
return statusCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal string GetReasonPhrase(int statusCode)
|
internal string GetReasonPhrase(int statusCode)
|
||||||
{
|
{
|
||||||
// TODO: Validate user input for illegal chars, length limit, etc.?
|
// TODO: Validate user input for illegal chars, length limit, etc.?
|
||||||
string reasonPhrase = _requestContext.Environment.ResponseReasonPhrase;
|
string reasonPhrase = ReasonPhrase;
|
||||||
if (string.IsNullOrWhiteSpace(reasonPhrase))
|
if (string.IsNullOrWhiteSpace(reasonPhrase))
|
||||||
{
|
{
|
||||||
// if the user hasn't set this, generated on the fly, if possible.
|
// if the user hasn't set this, generated on the fly, if possible.
|
||||||
|
@ -124,11 +151,16 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IDictionary<string, string[]> Headers
|
public IDictionary<string, string[]> Headers
|
||||||
{
|
{
|
||||||
get
|
get { return _headers; }
|
||||||
|
set
|
||||||
{
|
{
|
||||||
return _headers;
|
if (value == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("value");
|
||||||
|
}
|
||||||
|
_headers = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +174,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
|
|
||||||
private Version GetProtocolVersion()
|
private Version GetProtocolVersion()
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
Version requestVersion = Request.ProtocolVersion;
|
Version requestVersion = Request.ProtocolVersion;
|
||||||
Version responseVersion = requestVersion;
|
Version responseVersion = requestVersion;
|
||||||
string protocolVersion = RequestContext.Environment.Get<string>(Constants.HttpResponseProtocolKey);
|
string protocolVersion = RequestContext.Environment.Get<string>(Constants.HttpResponseProtocolKey);
|
||||||
|
@ -170,7 +203,11 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the lesser of the two versions. There are only two, so it it will always be 1.0.
|
// Return the lesser of the two versions. There are only two, so it it will always be 1.0.
|
||||||
return Constants.V1_0;
|
return Constants.V1_0;*/
|
||||||
|
|
||||||
|
// TODO: IHttpResponseInformation does not define a response protocol version. Http.Sys doesn't let
|
||||||
|
// us send anything but 1.1 anyways, but we could at least use it to set things like the connection header.
|
||||||
|
return Request.ProtocolVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -188,7 +225,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
}
|
}
|
||||||
// TODO: Verbose log
|
// TODO: Verbose log
|
||||||
EnsureResponseStream();
|
EnsureResponseStream();
|
||||||
_responseStream.Dispose();
|
_nativeStream.Dispose();
|
||||||
_responseState = ResponseState.Closed;
|
_responseState = ResponseState.Closed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,9 +258,9 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
|
|
||||||
private void EnsureResponseStream()
|
private void EnsureResponseStream()
|
||||||
{
|
{
|
||||||
if (_responseStream == null)
|
if (_nativeStream == null)
|
||||||
{
|
{
|
||||||
_responseStream = new ResponseStream(RequestContext);
|
_nativeStream = new ResponseStream(RequestContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,8 +303,6 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
|
|
||||||
// TODO: Verbose log headers
|
// TODO: Verbose log headers
|
||||||
_responseState = ResponseState.SentHeaders;
|
_responseState = ResponseState.SentHeaders;
|
||||||
|
|
||||||
_nativeResponse.StatusCode = (ushort)GetStatusCode();
|
|
||||||
string reasonPhrase = GetReasonPhrase(_nativeResponse.StatusCode);
|
string reasonPhrase = GetReasonPhrase(_nativeResponse.StatusCode);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -369,7 +404,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
NotifyOnSendingHeaders();
|
NotifyOnSendingHeaders();
|
||||||
|
|
||||||
// 401
|
// 401
|
||||||
if (GetStatusCode() == (ushort)HttpStatusCode.Unauthorized)
|
if (StatusCode == (ushort)HttpStatusCode.Unauthorized)
|
||||||
{
|
{
|
||||||
RequestContext.Server.AuthenticationManager.SetAuthenticationChallenge(this);
|
RequestContext.Server.AuthenticationManager.SetAuthenticationChallenge(this);
|
||||||
}
|
}
|
||||||
|
@ -468,7 +503,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
_boundaryType = BoundaryType.Chunked;
|
_boundaryType = BoundaryType.Chunked;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CanSendResponseBody(_requestContext.Response.GetStatusCode()))
|
if (CanSendResponseBody(_requestContext.Response.StatusCode))
|
||||||
{
|
{
|
||||||
_contentLength = -1;
|
_contentLength = -1;
|
||||||
}
|
}
|
||||||
|
@ -708,25 +743,25 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
|
|
||||||
internal void CancelLastWrite(SafeHandle requestQueueHandle)
|
internal void CancelLastWrite(SafeHandle requestQueueHandle)
|
||||||
{
|
{
|
||||||
if (_responseStream != null)
|
if (_nativeStream != null)
|
||||||
{
|
{
|
||||||
_responseStream.CancelLastWrite(requestQueueHandle);
|
_nativeStream.CancelLastWrite(requestQueueHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task SendFileAsync(string fileName, long offset, long? count, CancellationToken cancel)
|
public Task SendFileAsync(string path, long offset, long? count, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
EnsureResponseStream();
|
EnsureResponseStream();
|
||||||
return _responseStream.SendFileAsync(fileName, offset, count, cancel);
|
return _nativeStream.SendFileAsync(path, offset, count, cancel);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SwitchToOpaqueMode()
|
internal void SwitchToOpaqueMode()
|
||||||
{
|
{
|
||||||
EnsureResponseStream();
|
EnsureResponseStream();
|
||||||
_responseStream.SwitchToOpaqueMode();
|
_nativeStream.SwitchToOpaqueMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RegisterForOnSendingHeaders(Action<object> callback, object state)
|
public void OnSendingHeaders(Action<object> callback, object state)
|
||||||
{
|
{
|
||||||
IList<Tuple<Action<object>, object>> actions = _onSendingHeadersActions;
|
IList<Tuple<Action<object>, object>> actions = _onSendingHeadersActions;
|
||||||
if (actions == null)
|
if (actions == null)
|
||||||
|
|
|
@ -736,7 +736,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
}
|
}
|
||||||
|
|
||||||
uint statusCode = 0;
|
uint statusCode = 0;
|
||||||
if ((_requestContext.Response.BoundaryType == BoundaryType.Chunked || _requestContext.Response.BoundaryType == BoundaryType.None) && (String.Compare(_requestContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) != 0))
|
if ((_requestContext.Response.BoundaryType == BoundaryType.Chunked || _requestContext.Response.BoundaryType == BoundaryType.None) && (String.Compare(_requestContext.Request.Method, "HEAD", StringComparison.OrdinalIgnoreCase) != 0))
|
||||||
{
|
{
|
||||||
if (_requestContext.Response.BoundaryType == BoundaryType.None)
|
if (_requestContext.Response.BoundaryType == BoundaryType.None)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
{
|
{
|
||||||
"version": "0.1-alpha-*",
|
"version": "0.1-alpha-*",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNet.Abstractions" : "0.1-alpha-*",
|
||||||
|
"Microsoft.AspNet.HttpFeature" : "0.1-alpha-*",
|
||||||
|
"Microsoft.AspNet.FeatureModel" : "0.1-alpha-*"
|
||||||
|
},
|
||||||
"compilationOptions" : { "allowUnsafe": true },
|
"compilationOptions" : { "allowUnsafe": true },
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"net45" : { },
|
"net45" : { },
|
||||||
|
|
|
@ -9,12 +9,14 @@ using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.FeatureModel;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Extensions;
|
using Xunit.Extensions;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
|
|
||||||
public class AuthenticationTests
|
public class AuthenticationTests
|
||||||
{
|
{
|
||||||
|
@ -49,7 +51,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(authType, env =>
|
using (CreateServer(authType, env =>
|
||||||
{
|
{
|
||||||
env["owin.ResponseStatusCode"] = 401;
|
new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -65,7 +67,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
// TODO: Not implemented - Digest
|
// TODO: Not implemented - Digest
|
||||||
using (CreateServer(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | /*AuthenticationType.Digest |*/ AuthenticationType.Basic, env =>
|
using (CreateServer(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | /*AuthenticationType.Digest |*/ AuthenticationType.Basic, env =>
|
||||||
{
|
{
|
||||||
env["owin.ResponseStatusCode"] = 401;
|
new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -74,26 +76,27 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
Assert.Equal("Kerberos, Negotiate, NTLM, basic", response.Headers.WwwAuthenticate.ToString(), StringComparer.OrdinalIgnoreCase);
|
Assert.Equal("Kerberos, Negotiate, NTLM, basic", response.Headers.WwwAuthenticate.ToString(), StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* TODO: User
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(AuthenticationType.Kerberos)]
|
[InlineData(AuthenticationType.Kerberos)]
|
||||||
[InlineData(AuthenticationType.Negotiate)]
|
[InlineData(AuthenticationType.Negotiate)]
|
||||||
[InlineData(AuthenticationType.Ntlm)]
|
[InlineData(AuthenticationType.Ntlm)]
|
||||||
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented
|
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented
|
||||||
// [InlineData(AuthenticationType.Basic)] // Doesn't work with default creds
|
// [InlineData(AuthenticationType.Basic)] // Doesn't work with default creds
|
||||||
[InlineData(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | /*AuthenticationType.Digest |*/ AuthenticationType.Basic)]
|
[InlineData(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | / *AuthenticationType.Digest |* / AuthenticationType.Basic)]
|
||||||
public async Task AuthTypes_Login_Success(AuthenticationType authType)
|
public async Task AuthTypes_Login_Success(AuthenticationType authType)
|
||||||
{
|
{
|
||||||
int requestCount = 0;
|
int requestCount = 0;
|
||||||
using (CreateServer(authType, env =>
|
using (CreateServer(authType, env =>
|
||||||
{
|
{
|
||||||
requestCount++;
|
requestCount++;
|
||||||
|
/ * // TODO: Expose user as feature.
|
||||||
object obj;
|
object obj;
|
||||||
if (env.TryGetValue("server.User", out obj) && obj != null)
|
if (env.TryGetValue("server.User", out obj) && obj != null)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}* /
|
||||||
env["owin.ResponseStatusCode"] = 401;
|
new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -101,7 +104,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
private IDisposable CreateServer(AuthenticationType authType, AppFunc app)
|
private IDisposable CreateServer(AuthenticationType authType, AppFunc app)
|
||||||
{
|
{
|
||||||
IDictionary<string, object> properties = new Dictionary<string, object>();
|
IDictionary<string, object> properties = new Dictionary<string, object>();
|
||||||
|
|
|
@ -11,11 +11,14 @@ using System.Net.Http;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.FeatureModel;
|
||||||
|
using Microsoft.AspNet.HttpFeature;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
|
|
||||||
public class HttpsTests
|
public class HttpsTests
|
||||||
{
|
{
|
||||||
|
@ -39,10 +42,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
byte[] body = Encoding.UTF8.GetBytes("Hello World");
|
byte[] body = Encoding.UTF8.GetBytes("Hello World");
|
||||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
httpContext.Response.ContentLength = body.Length;
|
||||||
responseHeaders["Content-Length"] = new string[] { body.Length.ToString() };
|
return httpContext.Response.Body.WriteAsync(body, 0, body.Length);
|
||||||
return env.Get<Stream>("owin.ResponseBody").WriteAsync(body, 0, body.Length);
|
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
string response = await SendRequestAsync(Address);
|
string response = await SendRequestAsync(Address);
|
||||||
|
@ -55,12 +58,12 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
string input = new StreamReader(env.Get<Stream>("owin.RequestBody")).ReadToEnd();
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
string input = new StreamReader(httpContext.Request.Body).ReadToEnd();
|
||||||
Assert.Equal("Hello World", input);
|
Assert.Equal("Hello World", input);
|
||||||
byte[] body = Encoding.UTF8.GetBytes("Hello World");
|
byte[] body = Encoding.UTF8.GetBytes("Hello World");
|
||||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
httpContext.Response.ContentLength = body.Length;
|
||||||
responseHeaders["Content-Length"] = new string[] { body.Length.ToString() };
|
httpContext.Response.Body.Write(body, 0, body.Length);
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
|
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -72,38 +75,36 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Https_ClientCertNotSent_ClientCertNotPresent()
|
public async Task Https_ClientCertNotSent_ClientCertNotPresent()
|
||||||
{
|
{
|
||||||
X509Certificate clientCert = null;
|
using (CreateServer(async env =>
|
||||||
using (CreateServer(env =>
|
|
||||||
{
|
{
|
||||||
var loadAsync = env.Get<Func<Task>>("ssl.LoadClientCertAsync");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
loadAsync().Wait();
|
var tls = httpContext.GetFeature<IHttpTransportLayerSecurity>();
|
||||||
clientCert = env.Get<X509Certificate>("ssl.ClientCertificate");
|
Assert.NotNull(tls);
|
||||||
return Task.FromResult(0);
|
await tls.LoadAsync();
|
||||||
|
Assert.Null(tls.ClientCertificate);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
string response = await SendRequestAsync(Address);
|
string response = await SendRequestAsync(Address);
|
||||||
Assert.Equal(string.Empty, response);
|
Assert.Equal(string.Empty, response);
|
||||||
Assert.Null(clientCert);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Https_ClientCertRequested_ClientCertPresent()
|
public async Task Https_ClientCertRequested_ClientCertPresent()
|
||||||
{
|
{
|
||||||
X509Certificate clientCert = null;
|
using (CreateServer(async env =>
|
||||||
using (CreateServer(env =>
|
|
||||||
{
|
{
|
||||||
var loadAsync = env.Get<Func<Task>>("ssl.LoadClientCertAsync");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
loadAsync().Wait();
|
var tls = httpContext.GetFeature<IHttpTransportLayerSecurity>();
|
||||||
clientCert = env.Get<X509Certificate>("ssl.ClientCertificate");
|
Assert.NotNull(tls);
|
||||||
return Task.FromResult(0);
|
await tls.LoadAsync();
|
||||||
|
Assert.NotNull(tls.ClientCertificate);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
X509Certificate2 cert = FindClientCert();
|
X509Certificate2 cert = FindClientCert();
|
||||||
Assert.NotNull(cert);
|
Assert.NotNull(cert);
|
||||||
string response = await SendRequestAsync(Address, cert);
|
string response = await SendRequestAsync(Address, cert);
|
||||||
Assert.Equal(string.Empty, response);
|
Assert.Equal(string.Empty, response);
|
||||||
Assert.NotNull(clientCert);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
// </copyright>
|
// </copyright>
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
/* TODO: Opaque
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -17,7 +17,7 @@ using Xunit.Extensions;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
using OpaqueUpgrade = Action<IDictionary<string, object>, Func<IDictionary<string, object>, Task>>;
|
using OpaqueUpgrade = Action<IDictionary<string, object>, Func<IDictionary<string, object>, Task>>;
|
||||||
|
|
||||||
public class OpaqueUpgradeTests
|
public class OpaqueUpgradeTests
|
||||||
|
@ -322,3 +322,4 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
|
@ -11,11 +11,13 @@ using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.FeatureModel;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
|
|
||||||
public class RequestBodyTests
|
public class RequestBodyTests
|
||||||
{
|
{
|
||||||
|
@ -26,12 +28,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
byte[] input = new byte[100];
|
byte[] input = new byte[100];
|
||||||
int read = env.Get<Stream>("owin.RequestBody").Read(input, 0, input.Length);
|
int read = httpContext.Request.Body.Read(input, 0, input.Length);
|
||||||
|
httpContext.Response.ContentLength = read;
|
||||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
httpContext.Response.Body.Write(input, 0, read);
|
||||||
responseHeaders["Content-Length"] = new string[] { read.ToString() };
|
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(input, 0, read);
|
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -45,32 +46,28 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(async env =>
|
using (CreateServer(async env =>
|
||||||
{
|
{
|
||||||
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
byte[] input = new byte[100];
|
byte[] input = new byte[100];
|
||||||
int read = await env.Get<Stream>("owin.RequestBody").ReadAsync(input, 0, input.Length);
|
int read = await httpContext.Request.Body.ReadAsync(input, 0, input.Length);
|
||||||
|
httpContext.Response.ContentLength = read;
|
||||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
await httpContext.Response.Body.WriteAsync(input, 0, read);
|
||||||
responseHeaders["Content-Length"] = new string[] { read.ToString() };
|
|
||||||
await env.Get<Stream>("owin.ResponseBody").WriteAsync(input, 0, read);
|
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
string response = await SendRequestAsync(Address, "Hello World");
|
string response = await SendRequestAsync(Address, "Hello World");
|
||||||
Assert.Equal("Hello World", response);
|
Assert.Equal("Hello World", response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if NET45
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RequestBody_ReadBeginEnd_Success()
|
public async Task RequestBody_ReadBeginEnd_Success()
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
Stream requestStream = env.Get<Stream>("owin.RequestBody");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
byte[] input = new byte[100];
|
byte[] input = new byte[100];
|
||||||
int read = requestStream.EndRead(requestStream.BeginRead(input, 0, input.Length, null, null));
|
int read = httpContext.Request.Body.EndRead(httpContext.Request.Body.BeginRead(input, 0, input.Length, null, null));
|
||||||
|
httpContext.Response.ContentLength = read;
|
||||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
httpContext.Response.Body.EndWrite(httpContext.Response.Body.BeginWrite(input, 0, read, null, null));
|
||||||
responseHeaders["Content-Length"] = new string[] { read.ToString() };
|
|
||||||
Stream responseStream = env.Get<Stream>("owin.ResponseBody");
|
|
||||||
responseStream.EndWrite(responseStream.BeginWrite(input, 0, read, null, null));
|
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -78,18 +75,19 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
Assert.Equal("Hello World", response);
|
Assert.Equal("Hello World", response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RequestBody_ReadSyncPartialBody_Success()
|
public async Task RequestBody_ReadSyncPartialBody_Success()
|
||||||
{
|
{
|
||||||
StaggardContent content = new StaggardContent();
|
StaggardContent content = new StaggardContent();
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
byte[] input = new byte[10];
|
byte[] input = new byte[10];
|
||||||
int read = env.Get<Stream>("owin.RequestBody").Read(input, 0, input.Length);
|
int read = httpContext.Request.Body.Read(input, 0, input.Length);
|
||||||
Assert.Equal(5, read);
|
Assert.Equal(5, read);
|
||||||
content.Block.Release();
|
content.Block.Release();
|
||||||
read = env.Get<Stream>("owin.RequestBody").Read(input, 0, input.Length);
|
read = httpContext.Request.Body.Read(input, 0, input.Length);
|
||||||
Assert.Equal(5, read);
|
Assert.Equal(5, read);
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
|
@ -105,11 +103,12 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
StaggardContent content = new StaggardContent();
|
StaggardContent content = new StaggardContent();
|
||||||
using (CreateServer(async env =>
|
using (CreateServer(async env =>
|
||||||
{
|
{
|
||||||
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
byte[] input = new byte[10];
|
byte[] input = new byte[10];
|
||||||
int read = await env.Get<Stream>("owin.RequestBody").ReadAsync(input, 0, input.Length);
|
int read = await httpContext.Request.Body.ReadAsync(input, 0, input.Length);
|
||||||
Assert.Equal(5, read);
|
Assert.Equal(5, read);
|
||||||
content.Block.Release();
|
content.Block.Release();
|
||||||
read = await env.Get<Stream>("owin.RequestBody").ReadAsync(input, 0, input.Length);
|
read = await httpContext.Request.Body.ReadAsync(input, 0, input.Length);
|
||||||
Assert.Equal(5, read);
|
Assert.Equal(5, read);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,11 +10,13 @@ using System.Net.Http;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.FeatureModel;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
|
|
||||||
public class RequestHeaderTests
|
public class RequestHeaderTests
|
||||||
{
|
{
|
||||||
|
@ -25,7 +27,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
var requestHeaders = env.Get<IDictionary<string, string[]>>("owin.RequestHeaders");
|
var requestHeaders = new DefaultHttpContext((IFeatureCollection)env).Request.Headers;
|
||||||
// NOTE: The System.Net client only sends the Connection: keep-alive header on the first connection per service-point.
|
// NOTE: The System.Net client only sends the Connection: keep-alive header on the first connection per service-point.
|
||||||
// Assert.Equal(2, requestHeaders.Count);
|
// Assert.Equal(2, requestHeaders.Count);
|
||||||
// Assert.Equal("Keep-Alive", requestHeaders.Get("Connection"));
|
// Assert.Equal("Keep-Alive", requestHeaders.Get("Connection"));
|
||||||
|
@ -44,7 +46,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
var requestHeaders = env.Get<IDictionary<string, string[]>>("owin.RequestHeaders");
|
var requestHeaders = new DefaultHttpContext((IFeatureCollection)env).Request.Headers;
|
||||||
Assert.Equal(4, requestHeaders.Count);
|
Assert.Equal(4, requestHeaders.Count);
|
||||||
Assert.Equal("localhost:8080", requestHeaders.Get("Host"));
|
Assert.Equal("localhost:8080", requestHeaders.Get("Host"));
|
||||||
Assert.Equal("close", requestHeaders.Get("Connection"));
|
Assert.Equal("close", requestHeaders.Get("Connection"));
|
||||||
|
|
|
@ -11,12 +11,15 @@ using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.FeatureModel;
|
||||||
|
using Microsoft.AspNet.HttpFeature;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Extensions;
|
using Xunit.Extensions;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
|
|
||||||
public class RequestTests
|
public class RequestTests
|
||||||
{
|
{
|
||||||
|
@ -27,36 +30,40 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// General keys
|
// General keys
|
||||||
Assert.Equal("1.0", env.Get<string>("owin.Version"));
|
// TODO: Assert.True(env.Get<CancellationToken>("owin.CallCancelled").CanBeCanceled);
|
||||||
Assert.True(env.Get<CancellationToken>("owin.CallCancelled").CanBeCanceled);
|
|
||||||
|
var requestInfo = httpContext.GetFeature<IHttpRequestInformation>();
|
||||||
|
|
||||||
// Request Keys
|
// Request Keys
|
||||||
Assert.Equal("GET", env.Get<string>("owin.RequestMethod"));
|
Assert.Equal("GET", requestInfo.Method);
|
||||||
Assert.Equal(Stream.Null, env.Get<Stream>("owin.RequestBody"));
|
Assert.Equal(Stream.Null, requestInfo.Body);
|
||||||
Assert.NotNull(env.Get<IDictionary<string, string[]>>("owin.RequestHeaders"));
|
Assert.NotNull(requestInfo.Headers);
|
||||||
Assert.Equal("http", env.Get<string>("owin.RequestScheme"));
|
Assert.Equal("http", requestInfo.Scheme);
|
||||||
Assert.Equal("/basepath", env.Get<string>("owin.RequestPathBase"));
|
Assert.Equal("/basepath", requestInfo.PathBase);
|
||||||
Assert.Equal("/SomePath", env.Get<string>("owin.RequestPath"));
|
Assert.Equal("/SomePath", requestInfo.Path);
|
||||||
Assert.Equal("SomeQuery", env.Get<string>("owin.RequestQueryString"));
|
Assert.Equal("?SomeQuery", requestInfo.QueryString);
|
||||||
Assert.Equal("HTTP/1.1", env.Get<string>("owin.RequestProtocol"));
|
Assert.Equal("HTTP/1.1", requestInfo.Protocol);
|
||||||
|
|
||||||
// Server Keys
|
// Server Keys
|
||||||
Assert.NotNull(env.Get<IDictionary<string, object>>("server.Capabilities"));
|
// TODO: Assert.NotNull(env.Get<IDictionary<string, object>>("server.Capabilities"));
|
||||||
Assert.Equal("::1", env.Get<string>("server.RemoteIpAddress"));
|
|
||||||
Assert.NotNull(env.Get<string>("server.RemotePort"));
|
var connectionInfo = httpContext.GetFeature<IHttpConnection>();
|
||||||
Assert.Equal("::1", env.Get<string>("server.LocalIpAddress"));
|
Assert.Equal("::1", connectionInfo.RemoteIpAddress.ToString());
|
||||||
Assert.Equal("8080", env.Get<string>("server.LocalPort"));
|
Assert.NotEqual(0, connectionInfo.RemotePort);
|
||||||
Assert.True(env.Get<bool>("server.IsLocal"));
|
Assert.Equal("::1", connectionInfo.LocalIpAddress.ToString());
|
||||||
|
Assert.NotEqual(0, connectionInfo.LocalPort);
|
||||||
|
Assert.True(connectionInfo.IsLocal);
|
||||||
|
|
||||||
// Note: Response keys are validated in the ResponseTests
|
// Note: Response keys are validated in the ResponseTests
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
byte[] body = Encoding.ASCII.GetBytes(ex.ToString());
|
byte[] body = Encoding.ASCII.GetBytes(ex.ToString());
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
|
httpContext.Response.Body.Write(body, 0, body.Length);
|
||||||
}
|
}
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}, "http", "localhost", "8080", "/basepath"))
|
}, "http", "localhost", "8080", "/basepath"))
|
||||||
|
@ -78,21 +85,23 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Uri uri = new Uri(requestUri);
|
var requestInfo = httpContext.GetFeature<IHttpRequestInformation>();
|
||||||
string expectedQuery = uri.Query.Length > 0 ? uri.Query.Substring(1) : string.Empty;
|
var connectionInfo = httpContext.GetFeature<IHttpConnection>();
|
||||||
|
|
||||||
// Request Keys
|
// Request Keys
|
||||||
Assert.Equal(scheme, env.Get<string>("owin.RequestScheme"));
|
Assert.Equal(scheme, requestInfo.Scheme);
|
||||||
Assert.Equal(expectedPath, env.Get<string>("owin.RequestPath"));
|
Assert.Equal(expectedPath, requestInfo.Path);
|
||||||
Assert.Equal(expectedPathBase, env.Get<string>("owin.RequestPathBase"));
|
Assert.Equal(expectedPathBase, requestInfo.PathBase);
|
||||||
Assert.Equal(expectedQuery, env.Get<string>("owin.RequestQueryString"));
|
Assert.Equal(string.Empty, requestInfo.QueryString);
|
||||||
Assert.Equal(port, env.Get<string>("server.LocalPort"));
|
Assert.Equal(port, connectionInfo.LocalPort.ToString());
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
byte[] body = Encoding.ASCII.GetBytes(ex.ToString());
|
byte[] body = Encoding.ASCII.GetBytes(ex.ToString());
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
|
httpContext.Response.Body.Write(body, 0, body.Length);
|
||||||
}
|
}
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}, scheme, host, port, pathBase))
|
}, scheme, host, port, pathBase))
|
||||||
|
@ -119,15 +128,17 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
var requestInfo = httpContext.GetFeature<IHttpRequestInformation>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Assert.Equal(expectedPath, env.Get<string>("owin.RequestPath"));
|
Assert.Equal(expectedPath, requestInfo.Path);
|
||||||
Assert.Equal(expectedPathBase, env.Get<string>("owin.RequestPathBase"));
|
Assert.Equal(expectedPathBase, requestInfo.PathBase);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
byte[] body = Encoding.ASCII.GetBytes(ex.ToString());
|
byte[] body = Encoding.ASCII.GetBytes(ex.ToString());
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
|
httpContext.Response.Body.Write(body, 0, body.Length);
|
||||||
}
|
}
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -11,11 +11,14 @@ using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.FeatureModel;
|
||||||
|
using Microsoft.AspNet.HttpFeature;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
|
|
||||||
public class ResponseBodyTests
|
public class ResponseBodyTests
|
||||||
{
|
{
|
||||||
|
@ -26,8 +29,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(new byte[10], 0, 10);
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
return env.Get<Stream>("owin.ResponseBody").WriteAsync(new byte[10], 0, 10);
|
httpContext.Response.Body.Write(new byte[10], 0, 10);
|
||||||
|
return httpContext.Response.Body.WriteAsync(new byte[10], 0, 10);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
@ -45,8 +49,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["transfeR-Encoding"] = new string[] { " CHunked " };
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
Stream stream = env.Get<Stream>("owin.ResponseBody");
|
httpContext.Request.Headers["transfeR-Encoding"] = " CHunked ";
|
||||||
|
Stream stream = httpContext.Response.Body;
|
||||||
stream.EndWrite(stream.BeginWrite(new byte[10], 0, 10, null, null));
|
stream.EndWrite(stream.BeginWrite(new byte[10], 0, 10, null, null));
|
||||||
stream.Write(new byte[10], 0, 10);
|
stream.Write(new byte[10], 0, 10);
|
||||||
return stream.WriteAsync(new byte[10], 0, 10);
|
return stream.WriteAsync(new byte[10], 0, 10);
|
||||||
|
@ -67,8 +72,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 30 " };
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
Stream stream = env.Get<Stream>("owin.ResponseBody");
|
httpContext.Response.Headers["Content-lenGth"] = " 30 ";
|
||||||
|
Stream stream = httpContext.Response.Body;
|
||||||
stream.EndWrite(stream.BeginWrite(new byte[10], 0, 10, null, null));
|
stream.EndWrite(stream.BeginWrite(new byte[10], 0, 10, null, null));
|
||||||
stream.Write(new byte[10], 0, 10);
|
stream.Write(new byte[10], 0, 10);
|
||||||
return stream.WriteAsync(new byte[10], 0, 10);
|
return stream.WriteAsync(new byte[10], 0, 10);
|
||||||
|
@ -84,7 +90,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
Assert.Equal(new byte[30], await response.Content.ReadAsByteArrayAsync());
|
Assert.Equal(new byte[30], await response.Content.ReadAsByteArrayAsync());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* TODO: response protocol
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ResponseBody_Http10WriteNoHeaders_DefaultsConnectionClose()
|
public async Task ResponseBody_Http10WriteNoHeaders_DefaultsConnectionClose()
|
||||||
{
|
{
|
||||||
|
@ -104,13 +110,14 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
Assert.Equal(new byte[20], await response.Content.ReadAsByteArrayAsync());
|
Assert.Equal(new byte[20], await response.Content.ReadAsByteArrayAsync());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ResponseBody_WriteContentLengthNoneWritten_Throws()
|
public void ResponseBody_WriteContentLengthNoneWritten_Throws()
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 20 " };
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
httpContext.Response.Headers["Content-lenGth"] = " 20 ";
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -123,8 +130,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 20 " };
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(new byte[5], 0, 5);
|
httpContext.Response.Headers["Content-lenGth"] = " 20 ";
|
||||||
|
httpContext.Response.Body.Write(new byte[5], 0, 5);
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -137,9 +145,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 10 " };
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(new byte[5], 0, 5);
|
httpContext.Response.Headers["Content-lenGth"] = " 10 ";
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(new byte[6], 0, 6);
|
httpContext.Response.Body.Write(new byte[5], 0, 5);
|
||||||
|
httpContext.Response.Body.Write(new byte[6], 0, 6);
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -156,9 +165,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 10 " };
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(new byte[10], 0, 10);
|
httpContext.Response.Headers["Content-lenGth"] = " 10 ";
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(new byte[9], 0, 9);
|
httpContext.Response.Body.Write(new byte[10], 0, 10);
|
||||||
|
httpContext.Response.Body.Write(new byte[9], 0, 9);
|
||||||
appThrew = false;
|
appThrew = false;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
|
|
|
@ -10,11 +10,14 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.FeatureModel;
|
||||||
|
using Microsoft.AspNet.HttpFeature;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
|
|
||||||
public class ResponseHeaderTests
|
public class ResponseHeaderTests
|
||||||
{
|
{
|
||||||
|
@ -44,7 +47,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
var responseInfo = httpContext.GetFeature<IHttpResponseInformation>();
|
||||||
|
var responseHeaders = responseInfo.Headers;
|
||||||
responseHeaders["Custom-Header1"] = new string[] { "custom1, and custom2", "custom3" };
|
responseHeaders["Custom-Header1"] = new string[] { "custom1, and custom2", "custom3" };
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
|
@ -66,7 +71,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
var responseInfo = httpContext.GetFeature<IHttpResponseInformation>();
|
||||||
|
var responseHeaders = responseInfo.Headers;
|
||||||
responseHeaders["Connection"] = new string[] { "Close" };
|
responseHeaders["Connection"] = new string[] { "Close" };
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
|
@ -77,7 +84,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
Assert.Equal(new string[] { "close" }, response.Headers.GetValues("Connection"));
|
Assert.Equal(new string[] { "close" }, response.Headers.GetValues("Connection"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* TODO:
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ResponseHeaders_SendsHttp10_Gets11Close()
|
public async Task ResponseHeaders_SendsHttp10_Gets11Close()
|
||||||
{
|
{
|
||||||
|
@ -113,6 +120,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
Assert.Equal(new string[] { "close" }, response.Headers.GetValues("Connection"));
|
Assert.Equal(new string[] { "close" }, response.Headers.GetValues("Connection"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ResponseHeaders_HTTP10Request_Gets11Close()
|
public async Task ResponseHeaders_HTTP10Request_Gets11Close()
|
||||||
|
@ -140,9 +148,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
var responseInfo = httpContext.GetFeature<IHttpResponseInformation>();
|
||||||
|
var responseHeaders = responseInfo.Headers;
|
||||||
responseHeaders["Transfer-Encoding"] = new string[] { "chunked" };
|
responseHeaders["Transfer-Encoding"] = new string[] { "chunked" };
|
||||||
return env.Get<Stream>("owin.ResponseBody").WriteAsync(new byte[10], 0, 10);
|
return responseInfo.Body.WriteAsync(new byte[10], 0, 10);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
using (HttpClient client = new HttpClient())
|
using (HttpClient client = new HttpClient())
|
||||||
|
@ -166,12 +176,14 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
using (CreateServer(
|
using (CreateServer(
|
||||||
env =>
|
env =>
|
||||||
{
|
{
|
||||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
var responseInfo = httpContext.GetFeature<IHttpResponseInformation>();
|
||||||
|
var responseHeaders = responseInfo.Headers;
|
||||||
responseHeaders.Add("Custom1", new string[] { "value1a", "value1b" });
|
responseHeaders.Add("Custom1", new string[] { "value1a", "value1b" });
|
||||||
responseHeaders.Add("Custom2", new string[] { "value2a, value2b" });
|
responseHeaders.Add("Custom2", new string[] { "value2a, value2b" });
|
||||||
var body = env.Get<Stream>("owin.ResponseBody");
|
var body = responseInfo.Body;
|
||||||
body.Flush();
|
body.Flush();
|
||||||
env["owin.ResponseStatusCode"] = 404; // Ignored
|
responseInfo.StatusCode = 404; // Ignored
|
||||||
responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored
|
responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
|
@ -194,12 +206,14 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
using (CreateServer(
|
using (CreateServer(
|
||||||
async env =>
|
async env =>
|
||||||
{
|
{
|
||||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
var responseInfo = httpContext.GetFeature<IHttpResponseInformation>();
|
||||||
|
var responseHeaders = responseInfo.Headers;
|
||||||
responseHeaders.Add("Custom1", new string[] { "value1a", "value1b" });
|
responseHeaders.Add("Custom1", new string[] { "value1a", "value1b" });
|
||||||
responseHeaders.Add("Custom2", new string[] { "value2a, value2b" });
|
responseHeaders.Add("Custom2", new string[] { "value2a, value2b" });
|
||||||
var body = env.Get<Stream>("owin.ResponseBody");
|
var body = responseInfo.Body;
|
||||||
await body.FlushAsync();
|
await body.FlushAsync();
|
||||||
env["owin.ResponseStatusCode"] = 404; // Ignored
|
responseInfo.StatusCode = 404; // Ignored
|
||||||
responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored
|
responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,12 +13,14 @@ using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.FeatureModel;
|
||||||
|
using Microsoft.AspNet.HttpFeature;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
using SendFileFunc = Func<string, long, long?, CancellationToken, Task>;
|
|
||||||
|
|
||||||
public class ResponseSendFileTests
|
public class ResponseSendFileTests
|
||||||
{
|
{
|
||||||
|
@ -32,8 +34,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
/* TODO:
|
||||||
IDictionary<string, object> capabilities = env.Get<IDictionary<string, object>>("server.Capabilities");
|
IDictionary<string, object> capabilities = env.Get<IDictionary<string, object>>("server.Capabilities");
|
||||||
Assert.NotNull(capabilities);
|
Assert.NotNull(capabilities);
|
||||||
|
|
||||||
|
@ -43,14 +47,15 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
Assert.NotNull(support);
|
Assert.NotNull(support);
|
||||||
|
|
||||||
Assert.Equal("Overlapped", support.Get<string>("sendfile.Concurrency"));
|
Assert.Equal("Overlapped", support.Get<string>("sendfile.Concurrency"));
|
||||||
|
*/
|
||||||
|
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
Assert.NotNull(sendFileAsync);
|
Assert.NotNull(sendFile);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
byte[] body = Encoding.UTF8.GetBytes(ex.ToString());
|
byte[] body = Encoding.UTF8.GetBytes(ex.ToString());
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
|
httpContext.Response.Body.Write(body, 0, body.Length);
|
||||||
}
|
}
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
|
@ -72,10 +77,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
bool? appThrew = null;
|
bool? appThrew = null;
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
sendFileAsync(string.Empty, 0, null, CancellationToken.None).Wait();
|
sendFile.SendFileAsync(string.Empty, 0, null, CancellationToken.None).Wait();
|
||||||
appThrew = false;
|
appThrew = false;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
|
@ -103,8 +109,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
return sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
|
return sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
@ -121,8 +128,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
return sendFileAsync(RelativeFilePath, 0, null, CancellationToken.None);
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
|
return sendFile.SendFileAsync(RelativeFilePath, 0, null, CancellationToken.None);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
@ -139,9 +147,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Transfer-EncodinG"] = new string[] { "CHUNKED" };
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
return sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
httpContext.Response.Headers["Transfer-EncodinG"] = "CHUNKED";
|
||||||
|
return sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
@ -158,10 +167,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Transfer-EncodinG"] = new string[] { "CHUNKED" };
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None).Wait();
|
httpContext.Response.Headers["Transfer-EncodinG"] = "CHUNKED";
|
||||||
return sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None).Wait();
|
||||||
|
return sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
@ -178,8 +188,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
return sendFileAsync(AbsoluteFilePath, 0, FileLength / 2, CancellationToken.None);
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
|
return sendFile.SendFileAsync(AbsoluteFilePath, 0, FileLength / 2, CancellationToken.None);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
@ -196,8 +207,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
return sendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None);
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
|
return sendFile.SendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
@ -210,8 +222,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
return sendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None);
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
|
return sendFile.SendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
@ -224,8 +237,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
return sendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
|
return sendFile.SendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
@ -242,9 +256,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { FileLength.ToString() };
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
return sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
httpContext.Response.Headers["Content-lenGth"] = FileLength.ToString();
|
||||||
|
return sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
@ -262,9 +277,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { "10" };
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
return sendFileAsync(AbsoluteFilePath, 0, 10, CancellationToken.None);
|
httpContext.Response.Headers["Content-lenGth"] = "10";
|
||||||
|
return sendFile.SendFileAsync(AbsoluteFilePath, 0, 10, CancellationToken.None);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
@ -282,9 +298,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { "0" };
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||||
return sendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
|
httpContext.Response.Headers["Content-lenGth"] = "0";
|
||||||
|
return sendFile.SendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
|
|
@ -6,13 +6,17 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.FeatureModel;
|
||||||
|
using Microsoft.AspNet.HttpFeature;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
|
|
||||||
public class ResponseTests
|
public class ResponseTests
|
||||||
{
|
{
|
||||||
|
@ -23,7 +27,8 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
Assert.Equal(200, env["owin.ResponseStatusCode"]);
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
Assert.Equal(200, httpContext.Response.StatusCode);
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -40,8 +45,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env["owin.ResponseStatusCode"] = 201;
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
env["owin.ResponseProtocol"] = "HTTP/1.0"; // Http.Sys ignores this value
|
httpContext.Response.StatusCode = 201;
|
||||||
|
// TODO: env["owin.ResponseProtocol"] = "HTTP/1.0"; // Http.Sys ignores this value
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -58,9 +64,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env["owin.ResponseStatusCode"] = 201;
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
env["owin.ResponseReasonPhrase"] = "CustomReasonPhrase";
|
httpContext.Response.StatusCode = 201;
|
||||||
env["owin.ResponseProtocol"] = "HTTP/1.0"; // Http.Sys ignores this value
|
httpContext.GetFeature<IHttpResponseInformation>().ReasonPhrase = "CustomReasonPhrase"; // TODO?
|
||||||
|
// TODO: env["owin.ResponseProtocol"] = "HTTP/1.0"; // Http.Sys ignores this value
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -77,7 +84,8 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env["owin.ResponseStatusCode"] = 901;
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
httpContext.Response.StatusCode = 901;
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
|
@ -89,28 +97,32 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Response_100_Throws()
|
public async Task Response_100_Throws()
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env["owin.ResponseStatusCode"] = 100;
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
httpContext.Response.StatusCode = 100;
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
Assert.Throws<AggregateException>(() => SendRequestAsync(Address).Result);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
Assert.Equal(500, (int)response.StatusCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Response_0_Throws()
|
public async Task Response_0_Throws()
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
env["owin.ResponseStatusCode"] = 0;
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
httpContext.Response.StatusCode = 0;
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
Assert.Throws<AggregateException>(() => SendRequestAsync(Address).Result);
|
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||||
|
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,13 @@ using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.FeatureModel;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
using AppFunc = Func<object, Task>;
|
||||||
|
|
||||||
public class ServerTests
|
public class ServerTests
|
||||||
{
|
{
|
||||||
|
@ -41,11 +43,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
byte[] body = Encoding.UTF8.GetBytes("Hello World");
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
httpContext.Response.ContentLength = 11;
|
||||||
responseHeaders["Content-Length"] = new string[] { body.Length.ToString() };
|
return httpContext.Response.WriteAsync("Hello World");
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
string response = await SendRequestAsync(Address);
|
string response = await SendRequestAsync(Address);
|
||||||
|
@ -58,13 +58,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
{
|
{
|
||||||
using (CreateServer(env =>
|
using (CreateServer(env =>
|
||||||
{
|
{
|
||||||
string input = new StreamReader(env.Get<Stream>("owin.RequestBody")).ReadToEnd();
|
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||||
|
string input = new StreamReader(httpContext.Request.Body).ReadToEnd();
|
||||||
Assert.Equal("Hello World", input);
|
Assert.Equal("Hello World", input);
|
||||||
byte[] body = Encoding.UTF8.GetBytes("Hello World");
|
httpContext.Response.ContentLength = 11;
|
||||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
return httpContext.Response.WriteAsync("Hello World");
|
||||||
responseHeaders["Content-Length"] = new string[] { body.Length.ToString() };
|
|
||||||
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
string response = await SendRequestAsync(Address, "Hello World");
|
string response = await SendRequestAsync(Address, "Hello World");
|
||||||
|
@ -154,7 +152,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
Assert.True(Task.WaitAll(requestTasks.ToArray(), TimeSpan.FromSeconds(2)), "Timed out");
|
Assert.True(Task.WaitAll(requestTasks.ToArray(), TimeSpan.FromSeconds(2)), "Timed out");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* TODO:
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Server_ClientDisconnects_CallCancelled()
|
public async Task Server_ClientDisconnects_CallCancelled()
|
||||||
{
|
{
|
||||||
|
@ -204,7 +202,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||||
Assert.Equal(string.Empty, response);
|
Assert.Equal(string.Empty, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
private IDisposable CreateServer(AppFunc app)
|
private IDisposable CreateServer(AppFunc app)
|
||||||
{
|
{
|
||||||
IDictionary<string, object> properties = new Dictionary<string, object>();
|
IDictionary<string, object> properties = new Dictionary<string, object>();
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
{
|
{
|
||||||
"version" : "0.1-alpha-*",
|
"version" : "0.1-alpha-*",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNet.Server.WebListener" : ""
|
"Microsoft.AspNet.Server.WebListener" : "",
|
||||||
|
"Microsoft.AspNet.Abstractions" : "0.1-alpha-*",
|
||||||
|
"Microsoft.AspNet.HttpFeature" : "0.1-alpha-*",
|
||||||
|
"Microsoft.AspNet.FeatureModel" : "0.1-alpha-*",
|
||||||
|
"Microsoft.AspNet.PipelineCore" : "0.1-alpha-*"
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"net45": {
|
"net45": {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче