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
|
||||
{
|
||||
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>>;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -18,7 +18,7 @@ using System.Threading.Tasks;
|
|||
|
||||
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 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
|
||||
RegisterForDisconnectNotification(requestContext);
|
||||
await _appFunc(requestContext.Environment).SupressContext();
|
||||
await _appFunc(requestContext.Features).SupressContext();
|
||||
await requestContext.ProcessResponseAsync().SupressContext();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -832,7 +832,8 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
ulong connectionId = requestContext.Request.ConnectionId;
|
||||
CancellationToken ct = GetConnectionCancellation(connectionId);
|
||||
requestContext.Request.RegisterForDisconnect(ct);
|
||||
requestContext.Environment.ConnectionDisconnect = ct;
|
||||
// TODO: Need a feature equivalent for owin.CallCancelled.
|
||||
// requestContext.Environment.ConnectionDisconnect = ct;
|
||||
}
|
||||
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.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
#if NET45
|
||||
using System.Security.Authentication.ExtendedProtection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
#endif
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.HttpFeature;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener
|
||||
{
|
||||
internal sealed unsafe class Request : IDisposable
|
||||
internal sealed class Request : IHttpRequestInformation, IHttpConnection, IHttpTransportLayerSecurity, IDisposable
|
||||
{
|
||||
private RequestContext _requestContext;
|
||||
private NativeRequestContext _nativeRequestContext;
|
||||
|
@ -27,29 +31,46 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
private ulong _contextId;
|
||||
|
||||
private SslStatus _sslStatus;
|
||||
private string _scheme;
|
||||
|
||||
private string _httpMethod;
|
||||
private Version _httpVersion;
|
||||
private string _httpProtocolVersion;
|
||||
|
||||
private Uri _requestUri;
|
||||
// private Uri _requestUri;
|
||||
private string _rawUrl;
|
||||
private string _cookedUrlHost;
|
||||
private string _cookedUrlPath;
|
||||
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 long _contentLength;
|
||||
private Stream _nativeStream;
|
||||
private Stream _requestStream;
|
||||
|
||||
private SocketAddress _localEndPoint;
|
||||
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 bool _isDisposed = false;
|
||||
private CancellationTokenRegistration _disconnectRegistration;
|
||||
|
||||
internal Request(RequestContext httpContext, NativeRequestContext memoryBlob)
|
||||
internal unsafe Request(RequestContext httpContext, NativeRequestContext memoryBlob)
|
||||
{
|
||||
// TODO: Verbose log
|
||||
_requestContext = httpContext;
|
||||
|
@ -82,6 +103,24 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
_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 minor = memoryBlob.RequestBlob->Version.MinorVersion;
|
||||
if (major == 1 && minor == 1)
|
||||
|
@ -155,16 +194,16 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
}
|
||||
}
|
||||
|
||||
// Without the leading ?
|
||||
internal string Query
|
||||
// With the leading ?, if any
|
||||
public string QueryString
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(_cookedUrlQuery))
|
||||
{
|
||||
return _cookedUrlQuery.Substring(1);
|
||||
}
|
||||
return string.Empty;
|
||||
return _cookedUrlQuery ?? string.Empty;
|
||||
}
|
||||
set
|
||||
{
|
||||
_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.
|
||||
internal long ContentLength64
|
||||
{
|
||||
|
@ -210,40 +268,101 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
}
|
||||
}
|
||||
|
||||
internal IDictionary<string, string[]> Headers
|
||||
public IDictionary<string, string[]> Headers
|
||||
{
|
||||
get
|
||||
{
|
||||
return _headers;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
_headers = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal string HttpMethod
|
||||
public string Method
|
||||
{
|
||||
get
|
||||
{
|
||||
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
|
||||
{
|
||||
if (_requestStream == null)
|
||||
{
|
||||
// TODO: Move this to the constructor (or a lazy Env dictionary)
|
||||
_requestStream = HasEntityBody ? new RequestStream(RequestContext) : Stream.Null;
|
||||
_requestStream = NativeStream;
|
||||
}
|
||||
return _requestStream;
|
||||
}
|
||||
set
|
||||
{
|
||||
_requestStream = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsLocal
|
||||
public string PathBase
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
internal string RawUrl
|
||||
{
|
||||
get
|
||||
|
@ -262,7 +381,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
return _rawUrl;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
internal Version ProtocolVersion
|
||||
{
|
||||
get
|
||||
|
@ -271,22 +390,34 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
}
|
||||
}
|
||||
|
||||
internal string Protocol
|
||||
public string Protocol
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
internal string RequestScheme
|
||||
#if NET45
|
||||
public IPAddress RemoteIpAddress
|
||||
{
|
||||
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
|
||||
{
|
||||
get
|
||||
|
@ -348,7 +551,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
return _requestUri;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
internal string RequestPath
|
||||
{
|
||||
get
|
||||
|
@ -391,6 +594,47 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
#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
|
||||
// disposed.
|
||||
internal void DetachBlob(NativeRequestContext memoryBlob)
|
||||
|
@ -419,9 +663,9 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
_nativeRequestContext = null;
|
||||
}
|
||||
_disconnectRegistration.Dispose();
|
||||
if (_requestStream != null)
|
||||
if (_nativeStream != null)
|
||||
{
|
||||
_requestStream.Dispose();
|
||||
_nativeStream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -435,9 +679,9 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
|
||||
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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FeatureModel;
|
||||
using Microsoft.AspNet.HttpFeature;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener
|
||||
{
|
||||
using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>;
|
||||
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 CallEnvironment _environment;
|
||||
private FeatureCollection _features;
|
||||
private OwinWebListener _server;
|
||||
private Request _request;
|
||||
private Response _response;
|
||||
|
@ -41,17 +43,17 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
_memoryBlob = memoryBlob;
|
||||
_request = new Request(this, _memoryBlob);
|
||||
_response = new Response(this);
|
||||
_environment = new CallEnvironment(this);
|
||||
_cts = new CancellationTokenSource();
|
||||
|
||||
PopulateEnvironment();
|
||||
_features = new FeatureCollection();
|
||||
PopulateFeatures();
|
||||
|
||||
_request.ReleasePins();
|
||||
}
|
||||
|
||||
internal CallEnvironment Environment
|
||||
internal IFeatureCollection Features
|
||||
{
|
||||
get { return _environment; }
|
||||
get { return _features; }
|
||||
}
|
||||
|
||||
internal Request Request
|
||||
|
@ -99,105 +101,31 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
}
|
||||
}
|
||||
|
||||
private void PopulateEnvironment()
|
||||
private void PopulateFeatures()
|
||||
{
|
||||
// General
|
||||
_environment.OwinVersion = Constants.OwinVersion;
|
||||
_environment.CallCancelled = _cts.Token;
|
||||
_features.Add(typeof(IHttpRequestInformation), Request);
|
||||
_features.Add(typeof(IHttpConnection), Request);
|
||||
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
|
||||
_environment.ServerCapabilities = _server.Capabilities;
|
||||
_environment.Listener = _server;
|
||||
|
||||
// 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.");
|
||||
_environment.ConnectionId = _request.ConnectionId;
|
||||
*/
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (_request.IsUpgradable)
|
||||
|
@ -213,6 +141,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
value = Server.GetChannelBinding(Request.ConnectionId, Request.IsSecureConnection);
|
||||
return value != null;
|
||||
}
|
||||
*/
|
||||
|
||||
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)
|
||||
{
|
||||
// Parameters are ignored for now
|
||||
|
@ -339,8 +232,8 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
}
|
||||
|
||||
// Set the status code and reason phrase
|
||||
Environment.ResponseStatusCode = (int)HttpStatusCode.SwitchingProtocols;
|
||||
Environment.ResponseReasonPhrase = HttpReasonPhrase.Get(HttpStatusCode.SwitchingProtocols);
|
||||
Response.StatusCode = (int)HttpStatusCode.SwitchingProtocols;
|
||||
Response.ReasonPhrase = HttpReasonPhrase.Get(HttpStatusCode.SwitchingProtocols);
|
||||
|
||||
// Store the callback and process it after the stack unwind.
|
||||
_opaqueCallback = callback;
|
||||
|
@ -351,7 +244,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
{
|
||||
// If an upgrade was requested, perform it
|
||||
if (!Response.SentHeaders && _opaqueCallback != null
|
||||
&& Environment.ResponseStatusCode == (int)HttpStatusCode.SwitchingProtocols)
|
||||
&& Response.StatusCode == (int)HttpStatusCode.SwitchingProtocols)
|
||||
{
|
||||
Response.SendOpaqueUpgrade();
|
||||
|
||||
|
@ -368,21 +261,21 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
|
||||
opaqueEnv[Constants.OpaqueVersionKey] = Constants.OpaqueVersion;
|
||||
// TODO: Separate CT?
|
||||
opaqueEnv[Constants.OpaqueCallCancelledKey] = Environment.CallCancelled;
|
||||
// opaqueEnv[Constants.OpaqueCallCancelledKey] = Environment.CallCancelled;
|
||||
|
||||
Request.SwitchToOpaqueMode();
|
||||
Response.SwitchToOpaqueMode();
|
||||
opaqueEnv[Constants.OpaqueStreamKey] = new OpaqueStream(Request.InputStream, Response.OutputStream);
|
||||
opaqueEnv[Constants.OpaqueStreamKey] = new OpaqueStream(Request.NativeStream, Response.NativeStream);
|
||||
|
||||
return opaqueEnv;
|
||||
}
|
||||
|
||||
internal void SetFatalResponse()
|
||||
{
|
||||
Environment.ResponseStatusCode = 500;
|
||||
Environment.ResponseReasonPhrase = string.Empty;
|
||||
Environment.ResponseHeaders.Clear();
|
||||
Environment.ResponseHeaders.Add(HttpKnownHeaderNames.ContentLength, ZeroContentLength);
|
||||
Response.StatusCode = 500;
|
||||
Response.ReasonPhrase = string.Empty;
|
||||
Response.Headers.Clear();
|
||||
Response.Headers.Add(HttpKnownHeaderNames.ContentLength, ZeroContentLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,14 +14,17 @@ using System.Runtime.InteropServices;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.HttpFeature;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener
|
||||
{
|
||||
internal sealed unsafe class Response : IDisposable
|
||||
internal sealed unsafe class Response : IHttpResponseInformation, IHttpSendFile, IDisposable
|
||||
{
|
||||
private ResponseState _responseState;
|
||||
private IDictionary<string, string[]> _headers;
|
||||
private ResponseStream _responseStream;
|
||||
private string _reasonPhrase;
|
||||
private ResponseStream _nativeStream;
|
||||
private Stream _responseStream;
|
||||
private long _contentLength;
|
||||
private BoundaryType _boundaryType;
|
||||
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
|
||||
{
|
||||
CheckDisposed();
|
||||
EnsureResponseStream();
|
||||
return _responseStream;
|
||||
return _nativeStream;
|
||||
}
|
||||
}
|
||||
|
||||
internal int GetStatusCode()
|
||||
public Stream Body
|
||||
{
|
||||
int statusCode = _requestContext.Environment.ResponseStatusCode ?? 200;
|
||||
if (statusCode <= 100 || statusCode > 999)
|
||||
get
|
||||
{
|
||||
// TODO: Move this validation to the dictionary facade so it throws when the app sets it, rather than durring send?
|
||||
throw new InvalidOperationException(string.Format(Resources.Exception_InvalidStatusCode, statusCode));
|
||||
if (_responseStream == null)
|
||||
{
|
||||
_responseStream = NativeStream;
|
||||
}
|
||||
return _responseStream;
|
||||
}
|
||||
set
|
||||
{
|
||||
_responseStream = value;
|
||||
}
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
internal string GetReasonPhrase(int statusCode)
|
||||
{
|
||||
// TODO: Validate user input for illegal chars, length limit, etc.?
|
||||
string reasonPhrase = _requestContext.Environment.ResponseReasonPhrase;
|
||||
string reasonPhrase = ReasonPhrase;
|
||||
if (string.IsNullOrWhiteSpace(reasonPhrase))
|
||||
{
|
||||
// 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()
|
||||
{
|
||||
/*
|
||||
Version requestVersion = Request.ProtocolVersion;
|
||||
Version responseVersion = requestVersion;
|
||||
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 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()
|
||||
|
@ -188,7 +225,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
}
|
||||
// TODO: Verbose log
|
||||
EnsureResponseStream();
|
||||
_responseStream.Dispose();
|
||||
_nativeStream.Dispose();
|
||||
_responseState = ResponseState.Closed;
|
||||
}
|
||||
}
|
||||
|
@ -221,9 +258,9 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
|
||||
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
|
||||
_responseState = ResponseState.SentHeaders;
|
||||
|
||||
_nativeResponse.StatusCode = (ushort)GetStatusCode();
|
||||
string reasonPhrase = GetReasonPhrase(_nativeResponse.StatusCode);
|
||||
|
||||
/*
|
||||
|
@ -369,7 +404,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
NotifyOnSendingHeaders();
|
||||
|
||||
// 401
|
||||
if (GetStatusCode() == (ushort)HttpStatusCode.Unauthorized)
|
||||
if (StatusCode == (ushort)HttpStatusCode.Unauthorized)
|
||||
{
|
||||
RequestContext.Server.AuthenticationManager.SetAuthenticationChallenge(this);
|
||||
}
|
||||
|
@ -468,7 +503,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
_boundaryType = BoundaryType.Chunked;
|
||||
}
|
||||
|
||||
if (CanSendResponseBody(_requestContext.Response.GetStatusCode()))
|
||||
if (CanSendResponseBody(_requestContext.Response.StatusCode))
|
||||
{
|
||||
_contentLength = -1;
|
||||
}
|
||||
|
@ -708,25 +743,25 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
|
||||
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();
|
||||
return _responseStream.SendFileAsync(fileName, offset, count, cancel);
|
||||
return _nativeStream.SendFileAsync(path, offset, count, cancel);
|
||||
}
|
||||
|
||||
internal void SwitchToOpaqueMode()
|
||||
{
|
||||
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;
|
||||
if (actions == null)
|
||||
|
|
|
@ -736,7 +736,7 @@ namespace Microsoft.AspNet.Server.WebListener
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
{
|
||||
"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 },
|
||||
"configurations": {
|
||||
"net45" : { },
|
||||
|
|
|
@ -9,12 +9,14 @@ using System.Collections.Generic;
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FeatureModel;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Xunit;
|
||||
using Xunit.Extensions;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||
{
|
||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
||||
using AppFunc = Func<object, Task>;
|
||||
|
||||
public class AuthenticationTests
|
||||
{
|
||||
|
@ -49,7 +51,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(authType, env =>
|
||||
{
|
||||
env["owin.ResponseStatusCode"] = 401;
|
||||
new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
|
@ -65,7 +67,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
// TODO: Not implemented - Digest
|
||||
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);
|
||||
}))
|
||||
{
|
||||
|
@ -74,26 +76,27 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
Assert.Equal("Kerberos, Negotiate, NTLM, basic", response.Headers.WwwAuthenticate.ToString(), StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: User
|
||||
[Theory]
|
||||
[InlineData(AuthenticationType.Kerberos)]
|
||||
[InlineData(AuthenticationType.Negotiate)]
|
||||
[InlineData(AuthenticationType.Ntlm)]
|
||||
// [InlineData(AuthenticationType.Digest)] // TODO: Not implemented
|
||||
// [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)
|
||||
{
|
||||
int requestCount = 0;
|
||||
using (CreateServer(authType, env =>
|
||||
{
|
||||
requestCount++;
|
||||
/ * // TODO: Expose user as feature.
|
||||
object obj;
|
||||
if (env.TryGetValue("server.User", out obj) && obj != null)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
env["owin.ResponseStatusCode"] = 401;
|
||||
}* /
|
||||
new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
|
@ -101,7 +104,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
private IDisposable CreateServer(AuthenticationType authType, AppFunc app)
|
||||
{
|
||||
IDictionary<string, object> properties = new Dictionary<string, object>();
|
||||
|
|
|
@ -11,11 +11,14 @@ using System.Net.Http;
|
|||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FeatureModel;
|
||||
using Microsoft.AspNet.HttpFeature;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||
{
|
||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
||||
using AppFunc = Func<object, Task>;
|
||||
|
||||
public class HttpsTests
|
||||
{
|
||||
|
@ -39,10 +42,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
byte[] body = Encoding.UTF8.GetBytes("Hello World");
|
||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
||||
responseHeaders["Content-Length"] = new string[] { body.Length.ToString() };
|
||||
return env.Get<Stream>("owin.ResponseBody").WriteAsync(body, 0, body.Length);
|
||||
httpContext.Response.ContentLength = body.Length;
|
||||
return httpContext.Response.Body.WriteAsync(body, 0, body.Length);
|
||||
}))
|
||||
{
|
||||
string response = await SendRequestAsync(Address);
|
||||
|
@ -55,12 +58,12 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
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);
|
||||
byte[] body = Encoding.UTF8.GetBytes("Hello World");
|
||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
||||
responseHeaders["Content-Length"] = new string[] { body.Length.ToString() };
|
||||
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
|
||||
httpContext.Response.ContentLength = body.Length;
|
||||
httpContext.Response.Body.Write(body, 0, body.Length);
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
|
@ -72,38 +75,36 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
[Fact]
|
||||
public async Task Https_ClientCertNotSent_ClientCertNotPresent()
|
||||
{
|
||||
X509Certificate clientCert = null;
|
||||
using (CreateServer(env =>
|
||||
using (CreateServer(async env =>
|
||||
{
|
||||
var loadAsync = env.Get<Func<Task>>("ssl.LoadClientCertAsync");
|
||||
loadAsync().Wait();
|
||||
clientCert = env.Get<X509Certificate>("ssl.ClientCertificate");
|
||||
return Task.FromResult(0);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var tls = httpContext.GetFeature<IHttpTransportLayerSecurity>();
|
||||
Assert.NotNull(tls);
|
||||
await tls.LoadAsync();
|
||||
Assert.Null(tls.ClientCertificate);
|
||||
}))
|
||||
{
|
||||
string response = await SendRequestAsync(Address);
|
||||
Assert.Equal(string.Empty, response);
|
||||
Assert.Null(clientCert);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Https_ClientCertRequested_ClientCertPresent()
|
||||
{
|
||||
X509Certificate clientCert = null;
|
||||
using (CreateServer(env =>
|
||||
using (CreateServer(async env =>
|
||||
{
|
||||
var loadAsync = env.Get<Func<Task>>("ssl.LoadClientCertAsync");
|
||||
loadAsync().Wait();
|
||||
clientCert = env.Get<X509Certificate>("ssl.ClientCertificate");
|
||||
return Task.FromResult(0);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var tls = httpContext.GetFeature<IHttpTransportLayerSecurity>();
|
||||
Assert.NotNull(tls);
|
||||
await tls.LoadAsync();
|
||||
Assert.NotNull(tls.ClientCertificate);
|
||||
}))
|
||||
{
|
||||
X509Certificate2 cert = FindClientCert();
|
||||
Assert.NotNull(cert);
|
||||
string response = await SendRequestAsync(Address, cert);
|
||||
Assert.Equal(string.Empty, response);
|
||||
Assert.NotNull(clientCert);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/* TODO: Opaque
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
@ -17,7 +17,7 @@ using Xunit.Extensions;
|
|||
|
||||
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>>;
|
||||
|
||||
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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FeatureModel;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||
{
|
||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
||||
using AppFunc = Func<object, Task>;
|
||||
|
||||
public class RequestBodyTests
|
||||
{
|
||||
|
@ -26,12 +28,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
byte[] input = new byte[100];
|
||||
int read = env.Get<Stream>("owin.RequestBody").Read(input, 0, input.Length);
|
||||
|
||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
||||
responseHeaders["Content-Length"] = new string[] { read.ToString() };
|
||||
env.Get<Stream>("owin.ResponseBody").Write(input, 0, read);
|
||||
int read = httpContext.Request.Body.Read(input, 0, input.Length);
|
||||
httpContext.Response.ContentLength = read;
|
||||
httpContext.Response.Body.Write(input, 0, read);
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
|
@ -45,32 +46,28 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(async env =>
|
||||
{
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
byte[] input = new byte[100];
|
||||
int read = await env.Get<Stream>("owin.RequestBody").ReadAsync(input, 0, input.Length);
|
||||
|
||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
||||
responseHeaders["Content-Length"] = new string[] { read.ToString() };
|
||||
await env.Get<Stream>("owin.ResponseBody").WriteAsync(input, 0, read);
|
||||
int read = await httpContext.Request.Body.ReadAsync(input, 0, input.Length);
|
||||
httpContext.Response.ContentLength = read;
|
||||
await httpContext.Response.Body.WriteAsync(input, 0, read);
|
||||
}))
|
||||
{
|
||||
string response = await SendRequestAsync(Address, "Hello World");
|
||||
Assert.Equal("Hello World", response);
|
||||
}
|
||||
}
|
||||
|
||||
#if NET45
|
||||
[Fact]
|
||||
public async Task RequestBody_ReadBeginEnd_Success()
|
||||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
Stream requestStream = env.Get<Stream>("owin.RequestBody");
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
byte[] input = new byte[100];
|
||||
int read = requestStream.EndRead(requestStream.BeginRead(input, 0, input.Length, null, null));
|
||||
|
||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
||||
responseHeaders["Content-Length"] = new string[] { read.ToString() };
|
||||
Stream responseStream = env.Get<Stream>("owin.ResponseBody");
|
||||
responseStream.EndWrite(responseStream.BeginWrite(input, 0, read, null, null));
|
||||
int read = httpContext.Request.Body.EndRead(httpContext.Request.Body.BeginRead(input, 0, input.Length, null, null));
|
||||
httpContext.Response.ContentLength = read;
|
||||
httpContext.Response.Body.EndWrite(httpContext.Response.Body.BeginWrite(input, 0, read, null, null));
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
|
@ -78,18 +75,19 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
Assert.Equal("Hello World", response);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
[Fact]
|
||||
public async Task RequestBody_ReadSyncPartialBody_Success()
|
||||
{
|
||||
StaggardContent content = new StaggardContent();
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
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);
|
||||
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);
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
|
@ -105,11 +103,12 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
StaggardContent content = new StaggardContent();
|
||||
using (CreateServer(async env =>
|
||||
{
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
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);
|
||||
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);
|
||||
}))
|
||||
{
|
||||
|
|
|
@ -10,11 +10,13 @@ using System.Net.Http;
|
|||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FeatureModel;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||
{
|
||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
||||
using AppFunc = Func<object, Task>;
|
||||
|
||||
public class RequestHeaderTests
|
||||
{
|
||||
|
@ -25,7 +27,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
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.
|
||||
// Assert.Equal(2, requestHeaders.Count);
|
||||
// Assert.Equal("Keep-Alive", requestHeaders.Get("Connection"));
|
||||
|
@ -44,7 +46,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
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("localhost:8080", requestHeaders.Get("Host"));
|
||||
Assert.Equal("close", requestHeaders.Get("Connection"));
|
||||
|
|
|
@ -11,12 +11,15 @@ using System.Net.Http;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FeatureModel;
|
||||
using Microsoft.AspNet.HttpFeature;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Xunit;
|
||||
using Xunit.Extensions;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||
{
|
||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
||||
using AppFunc = Func<object, Task>;
|
||||
|
||||
public class RequestTests
|
||||
{
|
||||
|
@ -27,36 +30,40 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
try
|
||||
{
|
||||
// General keys
|
||||
Assert.Equal("1.0", env.Get<string>("owin.Version"));
|
||||
Assert.True(env.Get<CancellationToken>("owin.CallCancelled").CanBeCanceled);
|
||||
// TODO: Assert.True(env.Get<CancellationToken>("owin.CallCancelled").CanBeCanceled);
|
||||
|
||||
var requestInfo = httpContext.GetFeature<IHttpRequestInformation>();
|
||||
|
||||
// Request Keys
|
||||
Assert.Equal("GET", env.Get<string>("owin.RequestMethod"));
|
||||
Assert.Equal(Stream.Null, env.Get<Stream>("owin.RequestBody"));
|
||||
Assert.NotNull(env.Get<IDictionary<string, string[]>>("owin.RequestHeaders"));
|
||||
Assert.Equal("http", env.Get<string>("owin.RequestScheme"));
|
||||
Assert.Equal("/basepath", env.Get<string>("owin.RequestPathBase"));
|
||||
Assert.Equal("/SomePath", env.Get<string>("owin.RequestPath"));
|
||||
Assert.Equal("SomeQuery", env.Get<string>("owin.RequestQueryString"));
|
||||
Assert.Equal("HTTP/1.1", env.Get<string>("owin.RequestProtocol"));
|
||||
Assert.Equal("GET", requestInfo.Method);
|
||||
Assert.Equal(Stream.Null, requestInfo.Body);
|
||||
Assert.NotNull(requestInfo.Headers);
|
||||
Assert.Equal("http", requestInfo.Scheme);
|
||||
Assert.Equal("/basepath", requestInfo.PathBase);
|
||||
Assert.Equal("/SomePath", requestInfo.Path);
|
||||
Assert.Equal("?SomeQuery", requestInfo.QueryString);
|
||||
Assert.Equal("HTTP/1.1", requestInfo.Protocol);
|
||||
|
||||
// Server Keys
|
||||
Assert.NotNull(env.Get<IDictionary<string, object>>("server.Capabilities"));
|
||||
Assert.Equal("::1", env.Get<string>("server.RemoteIpAddress"));
|
||||
Assert.NotNull(env.Get<string>("server.RemotePort"));
|
||||
Assert.Equal("::1", env.Get<string>("server.LocalIpAddress"));
|
||||
Assert.Equal("8080", env.Get<string>("server.LocalPort"));
|
||||
Assert.True(env.Get<bool>("server.IsLocal"));
|
||||
// TODO: Assert.NotNull(env.Get<IDictionary<string, object>>("server.Capabilities"));
|
||||
|
||||
var connectionInfo = httpContext.GetFeature<IHttpConnection>();
|
||||
Assert.Equal("::1", connectionInfo.RemoteIpAddress.ToString());
|
||||
Assert.NotEqual(0, connectionInfo.RemotePort);
|
||||
Assert.Equal("::1", connectionInfo.LocalIpAddress.ToString());
|
||||
Assert.NotEqual(0, connectionInfo.LocalPort);
|
||||
Assert.True(connectionInfo.IsLocal);
|
||||
|
||||
// Note: Response keys are validated in the ResponseTests
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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);
|
||||
}, "http", "localhost", "8080", "/basepath"))
|
||||
|
@ -78,21 +85,23 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(requestUri);
|
||||
string expectedQuery = uri.Query.Length > 0 ? uri.Query.Substring(1) : string.Empty;
|
||||
var requestInfo = httpContext.GetFeature<IHttpRequestInformation>();
|
||||
var connectionInfo = httpContext.GetFeature<IHttpConnection>();
|
||||
|
||||
// Request Keys
|
||||
Assert.Equal(scheme, env.Get<string>("owin.RequestScheme"));
|
||||
Assert.Equal(expectedPath, env.Get<string>("owin.RequestPath"));
|
||||
Assert.Equal(expectedPathBase, env.Get<string>("owin.RequestPathBase"));
|
||||
Assert.Equal(expectedQuery, env.Get<string>("owin.RequestQueryString"));
|
||||
Assert.Equal(port, env.Get<string>("server.LocalPort"));
|
||||
Assert.Equal(scheme, requestInfo.Scheme);
|
||||
Assert.Equal(expectedPath, requestInfo.Path);
|
||||
Assert.Equal(expectedPathBase, requestInfo.PathBase);
|
||||
Assert.Equal(string.Empty, requestInfo.QueryString);
|
||||
Assert.Equal(port, connectionInfo.LocalPort.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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);
|
||||
}, scheme, host, port, pathBase))
|
||||
|
@ -119,15 +128,17 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var requestInfo = httpContext.GetFeature<IHttpRequestInformation>();
|
||||
try
|
||||
{
|
||||
Assert.Equal(expectedPath, env.Get<string>("owin.RequestPath"));
|
||||
Assert.Equal(expectedPathBase, env.Get<string>("owin.RequestPathBase"));
|
||||
Assert.Equal(expectedPath, requestInfo.Path);
|
||||
Assert.Equal(expectedPathBase, requestInfo.PathBase);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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);
|
||||
}))
|
||||
|
|
|
@ -11,11 +11,14 @@ using System.Linq;
|
|||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FeatureModel;
|
||||
using Microsoft.AspNet.HttpFeature;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||
{
|
||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
||||
using AppFunc = Func<object, Task>;
|
||||
|
||||
public class ResponseBodyTests
|
||||
{
|
||||
|
@ -26,8 +29,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env.Get<Stream>("owin.ResponseBody").Write(new byte[10], 0, 10);
|
||||
return env.Get<Stream>("owin.ResponseBody").WriteAsync(new byte[10], 0, 10);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
httpContext.Response.Body.Write(new byte[10], 0, 10);
|
||||
return httpContext.Response.Body.WriteAsync(new byte[10], 0, 10);
|
||||
}))
|
||||
{
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
|
@ -45,8 +49,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["transfeR-Encoding"] = new string[] { " CHunked " };
|
||||
Stream stream = env.Get<Stream>("owin.ResponseBody");
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
httpContext.Request.Headers["transfeR-Encoding"] = " CHunked ";
|
||||
Stream stream = httpContext.Response.Body;
|
||||
stream.EndWrite(stream.BeginWrite(new byte[10], 0, 10, null, null));
|
||||
stream.Write(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 =>
|
||||
{
|
||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 30 " };
|
||||
Stream stream = env.Get<Stream>("owin.ResponseBody");
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
httpContext.Response.Headers["Content-lenGth"] = " 30 ";
|
||||
Stream stream = httpContext.Response.Body;
|
||||
stream.EndWrite(stream.BeginWrite(new byte[10], 0, 10, null, null));
|
||||
stream.Write(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());
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: response protocol
|
||||
[Fact]
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
[Fact]
|
||||
public void ResponseBody_WriteContentLengthNoneWritten_Throws()
|
||||
{
|
||||
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);
|
||||
}))
|
||||
{
|
||||
|
@ -123,8 +130,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 20 " };
|
||||
env.Get<Stream>("owin.ResponseBody").Write(new byte[5], 0, 5);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
httpContext.Response.Headers["Content-lenGth"] = " 20 ";
|
||||
httpContext.Response.Body.Write(new byte[5], 0, 5);
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
|
@ -137,9 +145,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 10 " };
|
||||
env.Get<Stream>("owin.ResponseBody").Write(new byte[5], 0, 5);
|
||||
env.Get<Stream>("owin.ResponseBody").Write(new byte[6], 0, 6);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
httpContext.Response.Headers["Content-lenGth"] = " 10 ";
|
||||
httpContext.Response.Body.Write(new byte[5], 0, 5);
|
||||
httpContext.Response.Body.Write(new byte[6], 0, 6);
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
|
@ -156,9 +165,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
try
|
||||
{
|
||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 10 " };
|
||||
env.Get<Stream>("owin.ResponseBody").Write(new byte[10], 0, 10);
|
||||
env.Get<Stream>("owin.ResponseBody").Write(new byte[9], 0, 9);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
httpContext.Response.Headers["Content-lenGth"] = " 10 ";
|
||||
httpContext.Response.Body.Write(new byte[10], 0, 10);
|
||||
httpContext.Response.Body.Write(new byte[9], 0, 9);
|
||||
appThrew = false;
|
||||
}
|
||||
catch (Exception)
|
||||
|
|
|
@ -10,11 +10,14 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FeatureModel;
|
||||
using Microsoft.AspNet.HttpFeature;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||
{
|
||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
||||
using AppFunc = Func<object, Task>;
|
||||
|
||||
public class ResponseHeaderTests
|
||||
{
|
||||
|
@ -44,7 +47,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
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" };
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
|
@ -66,7 +71,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
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" };
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
|
@ -77,7 +84,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
Assert.Equal(new string[] { "close" }, response.Headers.GetValues("Connection"));
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
[Fact]
|
||||
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"));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
[Fact]
|
||||
public async Task ResponseHeaders_HTTP10Request_Gets11Close()
|
||||
|
@ -140,9 +148,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
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" };
|
||||
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())
|
||||
|
@ -166,12 +176,14 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
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.Add("Custom1", new string[] { "value1a", "value1b" });
|
||||
responseHeaders.Add("Custom2", new string[] { "value2a, value2b" });
|
||||
var body = env.Get<Stream>("owin.ResponseBody");
|
||||
var body = responseInfo.Body;
|
||||
body.Flush();
|
||||
env["owin.ResponseStatusCode"] = 404; // Ignored
|
||||
responseInfo.StatusCode = 404; // Ignored
|
||||
responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
|
@ -194,12 +206,14 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
using (CreateServer(
|
||||
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("Custom2", new string[] { "value2a, value2b" });
|
||||
var body = env.Get<Stream>("owin.ResponseBody");
|
||||
var body = responseInfo.Body;
|
||||
await body.FlushAsync();
|
||||
env["owin.ResponseStatusCode"] = 404; // Ignored
|
||||
responseInfo.StatusCode = 404; // Ignored
|
||||
responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored
|
||||
}))
|
||||
{
|
||||
|
|
|
@ -13,12 +13,14 @@ using System.Net.Http;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FeatureModel;
|
||||
using Microsoft.AspNet.HttpFeature;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||
{
|
||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
||||
using SendFileFunc = Func<string, long, long?, CancellationToken, Task>;
|
||||
using AppFunc = Func<object, Task>;
|
||||
|
||||
public class ResponseSendFileTests
|
||||
{
|
||||
|
@ -32,8 +34,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
try
|
||||
{
|
||||
/* TODO:
|
||||
IDictionary<string, object> capabilities = env.Get<IDictionary<string, object>>("server.Capabilities");
|
||||
Assert.NotNull(capabilities);
|
||||
|
||||
|
@ -43,14 +47,15 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
Assert.NotNull(support);
|
||||
|
||||
Assert.Equal("Overlapped", support.Get<string>("sendfile.Concurrency"));
|
||||
*/
|
||||
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
Assert.NotNull(sendFileAsync);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
Assert.NotNull(sendFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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);
|
||||
}))
|
||||
|
@ -72,10 +77,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
bool? appThrew = null;
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
try
|
||||
{
|
||||
sendFileAsync(string.Empty, 0, null, CancellationToken.None).Wait();
|
||||
sendFile.SendFileAsync(string.Empty, 0, null, CancellationToken.None).Wait();
|
||||
appThrew = false;
|
||||
}
|
||||
catch (Exception)
|
||||
|
@ -103,8 +109,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
return sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
return sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||
}))
|
||||
{
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
|
@ -121,8 +128,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
return sendFileAsync(RelativeFilePath, 0, null, CancellationToken.None);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
return sendFile.SendFileAsync(RelativeFilePath, 0, null, CancellationToken.None);
|
||||
}))
|
||||
{
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
|
@ -139,9 +147,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Transfer-EncodinG"] = new string[] { "CHUNKED" };
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
return sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
httpContext.Response.Headers["Transfer-EncodinG"] = "CHUNKED";
|
||||
return sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||
}))
|
||||
{
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
|
@ -158,10 +167,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Transfer-EncodinG"] = new string[] { "CHUNKED" };
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None).Wait();
|
||||
return sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
httpContext.Response.Headers["Transfer-EncodinG"] = "CHUNKED";
|
||||
sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None).Wait();
|
||||
return sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||
}))
|
||||
{
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
|
@ -178,8 +188,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
return sendFileAsync(AbsoluteFilePath, 0, FileLength / 2, CancellationToken.None);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
return sendFile.SendFileAsync(AbsoluteFilePath, 0, FileLength / 2, CancellationToken.None);
|
||||
}))
|
||||
{
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
|
@ -196,8 +207,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
return sendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
return sendFile.SendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None);
|
||||
}))
|
||||
{
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
|
@ -210,8 +222,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
return sendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
return sendFile.SendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None);
|
||||
}))
|
||||
{
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
|
@ -224,8 +237,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
return sendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
return sendFile.SendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
|
||||
}))
|
||||
{
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
|
@ -242,9 +256,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { FileLength.ToString() };
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
return sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
httpContext.Response.Headers["Content-lenGth"] = FileLength.ToString();
|
||||
return sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||
}))
|
||||
{
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
|
@ -262,9 +277,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { "10" };
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
return sendFileAsync(AbsoluteFilePath, 0, 10, CancellationToken.None);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
httpContext.Response.Headers["Content-lenGth"] = "10";
|
||||
return sendFile.SendFileAsync(AbsoluteFilePath, 0, 10, CancellationToken.None);
|
||||
}))
|
||||
{
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
|
@ -282,9 +298,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { "0" };
|
||||
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync");
|
||||
return sendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
var sendFile = httpContext.GetFeature<IHttpSendFile>();
|
||||
httpContext.Response.Headers["Content-lenGth"] = "0";
|
||||
return sendFile.SendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
|
||||
}))
|
||||
{
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
|
|
|
@ -6,13 +6,17 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FeatureModel;
|
||||
using Microsoft.AspNet.HttpFeature;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||
{
|
||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
||||
using AppFunc = Func<object, Task>;
|
||||
|
||||
public class ResponseTests
|
||||
{
|
||||
|
@ -23,7 +27,8 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
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);
|
||||
}))
|
||||
{
|
||||
|
@ -40,8 +45,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env["owin.ResponseStatusCode"] = 201;
|
||||
env["owin.ResponseProtocol"] = "HTTP/1.0"; // Http.Sys ignores this value
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
httpContext.Response.StatusCode = 201;
|
||||
// TODO: env["owin.ResponseProtocol"] = "HTTP/1.0"; // Http.Sys ignores this value
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
|
@ -58,9 +64,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env["owin.ResponseStatusCode"] = 201;
|
||||
env["owin.ResponseReasonPhrase"] = "CustomReasonPhrase";
|
||||
env["owin.ResponseProtocol"] = "HTTP/1.0"; // Http.Sys ignores this value
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
httpContext.Response.StatusCode = 201;
|
||||
httpContext.GetFeature<IHttpResponseInformation>().ReasonPhrase = "CustomReasonPhrase"; // TODO?
|
||||
// TODO: env["owin.ResponseProtocol"] = "HTTP/1.0"; // Http.Sys ignores this value
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
|
@ -77,7 +84,8 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env["owin.ResponseStatusCode"] = 901;
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
httpContext.Response.StatusCode = 901;
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
|
@ -89,28 +97,32 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void Response_100_Throws()
|
||||
public async Task Response_100_Throws()
|
||||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env["owin.ResponseStatusCode"] = 100;
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
httpContext.Response.StatusCode = 100;
|
||||
return Task.FromResult(0);
|
||||
}))
|
||||
{
|
||||
Assert.Throws<AggregateException>(() => SendRequestAsync(Address).Result);
|
||||
HttpResponseMessage response = await SendRequestAsync(Address);
|
||||
Assert.Equal(500, (int)response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Response_0_Throws()
|
||||
public async Task Response_0_Throws()
|
||||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
env["owin.ResponseStatusCode"] = 0;
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
httpContext.Response.StatusCode = 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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FeatureModel;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Server.WebListener.Tests
|
||||
{
|
||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
||||
using AppFunc = Func<object, Task>;
|
||||
|
||||
public class ServerTests
|
||||
{
|
||||
|
@ -41,11 +43,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
using (CreateServer(env =>
|
||||
{
|
||||
byte[] body = Encoding.UTF8.GetBytes("Hello World");
|
||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
||||
responseHeaders["Content-Length"] = new string[] { body.Length.ToString() };
|
||||
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
|
||||
return Task.FromResult(0);
|
||||
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
|
||||
httpContext.Response.ContentLength = 11;
|
||||
return httpContext.Response.WriteAsync("Hello World");
|
||||
}))
|
||||
{
|
||||
string response = await SendRequestAsync(Address);
|
||||
|
@ -58,13 +58,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
{
|
||||
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);
|
||||
byte[] body = Encoding.UTF8.GetBytes("Hello World");
|
||||
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders");
|
||||
responseHeaders["Content-Length"] = new string[] { body.Length.ToString() };
|
||||
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
|
||||
return Task.FromResult(0);
|
||||
httpContext.Response.ContentLength = 11;
|
||||
return httpContext.Response.WriteAsync("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");
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
[Fact]
|
||||
public async Task Server_ClientDisconnects_CallCancelled()
|
||||
{
|
||||
|
@ -204,7 +202,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
|
|||
Assert.Equal(string.Empty, response);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
private IDisposable CreateServer(AppFunc app)
|
||||
{
|
||||
IDictionary<string, object> properties = new Dictionary<string, object>();
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
{
|
||||
"version" : "0.1-alpha-*",
|
||||
"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": {
|
||||
"net45": {
|
||||
|
|
Загрузка…
Ссылка в новой задаче