Change to the new abstractions.

This commit is contained in:
Chris Ross 2014-02-15 15:38:17 -08:00
Родитель 5cece8b4af
Коммит 742db6ad65
22 изменённых файлов: 645 добавлений и 2789 удалений

Просмотреть файл

@ -26,7 +26,7 @@ using System.Threading.Tasks;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>; using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>;
/// <summary> /// <summary>

Просмотреть файл

@ -18,7 +18,7 @@ using System.Threading.Tasks;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>; using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>;
using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>; using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>;
@ -518,7 +518,7 @@ namespace Microsoft.AspNet.Server.WebListener
{ {
// TODO: Make disconnect registration lazy // TODO: Make disconnect registration lazy
RegisterForDisconnectNotification(requestContext); RegisterForDisconnectNotification(requestContext);
await _appFunc(requestContext.Environment).SupressContext(); await _appFunc(requestContext.Features).SupressContext();
await requestContext.ProcessResponseAsync().SupressContext(); await requestContext.ProcessResponseAsync().SupressContext();
} }
catch (Exception ex) catch (Exception ex)
@ -832,7 +832,8 @@ namespace Microsoft.AspNet.Server.WebListener
ulong connectionId = requestContext.Request.ConnectionId; ulong connectionId = requestContext.Request.ConnectionId;
CancellationToken ct = GetConnectionCancellation(connectionId); CancellationToken ct = GetConnectionCancellation(connectionId);
requestContext.Request.RegisterForDisconnect(ct); requestContext.Request.RegisterForDisconnect(ct);
requestContext.Environment.ConnectionDisconnect = ct; // TODO: Need a feature equivalent for owin.CallCancelled.
// requestContext.Environment.ConnectionDisconnect = ct;
} }
catch (Win32Exception exception) catch (Win32Exception exception)
{ {

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,316 +0,0 @@
<#@ template language="C#" #>
<#@ assembly name="System.Core.dll" #>
<#@ import namespace="System.Linq" #>
<#
var Init = new {Yes = new object(), No = new object(), Maybe = new object()};
var props = new[]
{
// owin standard keys
new {Key="owin.Version", Type="string", Name="OwinVersion", Init=Init.No},
new {Key="owin.CallCancelled", Type="CancellationToken", Name="CallCancelled", Init=Init.No},
new {Key="owin.RequestProtocol", Type="string", Name="RequestProtocol", Init=Init.No},
new {Key="owin.RequestMethod", Type="string", Name="RequestMethod", Init=Init.No},
new {Key="owin.RequestScheme", Type="string", Name="RequestScheme", Init=Init.No},
new {Key="owin.RequestPathBase", Type="string", Name="RequestPathBase", Init=Init.No},
new {Key="owin.RequestPath", Type="string", Name="RequestPath", Init=Init.No},
new {Key="owin.RequestQueryString", Type="string", Name="RequestQueryString", Init=Init.No},
new {Key="owin.RequestHeaders", Type="IDictionary<string, string[]>", Name="RequestHeaders", Init=Init.No},
new {Key="owin.RequestBody", Type="Stream", Name="RequestBody", Init=Init.Yes},
new {Key="owin.ResponseHeaders", Type="IDictionary<string, string[]>", Name="ResponseHeaders", Init=Init.No},
new {Key="owin.ResponseBody", Type="Stream", Name="ResponseBody", Init=Init.No},
new {Key="owin.ResponseStatusCode", Type="int?", Name="ResponseStatusCode", Init=Init.No},
new {Key="owin.ResponseReasonPhrase", Type="string", Name="ResponseReasonPhrase", Init=Init.No},
// defacto host keys
new {Key="host.TraceOutput", Type="TextWriter", Name="HostTraceOutput", Init=Init.No},
new {Key="host.AppName", Type="string", Name="HostAppName", Init=Init.No},
new {Key="host.AppMode", Type="string", Name="HostAppMode", Init=Init.No},
new {Key="host.OnAppDisposing", Type="CancellationToken", Name="OnAppDisposing", Init=Init.No},
new {Key="server.User", Type="System.Security.Principal.IPrincipal", Name="User", Init=Init.No},
new {Key="server.OnSendingHeaders", Type="Action<Action<object>, object>", Name="OnSendingHeaders", Init=Init.No},
new {Key="server.Capabilities", Type="IDictionary<string, object>", Name="ServerCapabilities", Init=Init.No},
// ServerVariable keys
new {Key="server.RemoteIpAddress", Type="string", Name="RemoteIpAddress", Init=Init.Yes},
new {Key="server.RemotePort", Type="string", Name="RemotePort", Init=Init.Yes},
new {Key="server.LocalIpAddress", Type="string", Name="LocalIpAddress", Init=Init.Yes},
new {Key="server.LocalPort", Type="string", Name="LocalPort", Init=Init.Yes},
new {Key="server.IsLocal", Type="bool", Name="IsLocal", Init=Init.Yes},
new {Key="server.ConnectionId", Type="object", Name="ConnectionId", Init=Init.No},
new {Key="server.ConnectionDisconnect", Type="CancellationToken", Name="ConnectionDisconnect", Init=Init.No},
// SSL
new { Key="ssl.ClientCertificate", Type="object", Name="ClientCert", Init=Init.No},
new { Key="ssl.LoadClientCertAsync", Type="Func<Task>", Name="LoadClientCert", Init=Init.No },
new { Key="ssl.ChannelBinding", Type="ChannelBinding", Name="ChannelBinding", Init=Init.Maybe },
// SendFile keys
new {Key="sendfile.SendAsync", Type="Func<string, long, long?, CancellationToken, Task>", Name="SendFileAsync", Init=Init.No},
// Opaque keys
new {Key="opaque.Upgrade", Type="OpaqueUpgrade", Name="OpaqueUpgrade", Init=Init.Maybe},
// Server specific keys
new { Key="Microsoft.AspNet.Server.WebListener.OwinWebListener", Type="OwinWebListener", Name="Listener", Init=Init.No},
}.Select((prop, Index)=>new {prop.Key, prop.Type, prop.Name, prop.Init, Index});
var lengths = props.OrderBy(prop=>prop.Key.Length).GroupBy(prop=>prop.Key.Length);
Func<int,string> IsSet = Index => "((_flag" + (Index / 32) + " & 0x" + (1<<(Index % 32)).ToString("x") + "u) != 0)";
Func<int,string> Set = Index => "_flag" + (Index / 32) + " |= 0x" + (1<<(Index % 32)).ToString("x") + "u";
Func<int,string> Clear = Index => "_flag" + (Index / 32) + " &= ~0x" + (1<<(Index % 32)).ToString("x") + "u";
Func<int,string> IsInitRequired = Index => "((_initFlag" + (Index / 32) + " & 0x" + (1<<(Index % 32)).ToString("x") + "u) != 0)";
Func<int,string> IsInitCompleted = Index => "((_initFlag" + (Index / 32) + " & 0x" + (1<<(Index % 32)).ToString("x") + "u) == 0)";
Func<int,string> CompleteInit = Index => "_initFlag" + (Index / 32) + " &= ~0x" + (1<<(Index % 32)).ToString("x") + "u";
#>
//-----------------------------------------------------------------------
// <copyright>
// Copyright (c) Katana Contributors. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
// <auto-generated />
using System;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Security.Authentication.ExtendedProtection;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Owin.Host.WebListener
{
using OpaqueUpgrade = Action<IDictionary<string, object>, Func<IDictionary<string, object>, Task>>;
[GeneratedCode("TextTemplatingFileGenerator", "")]
internal partial class CallEnvironment
{
// Mark all fields with delay initialization support as set.
private UInt32 _flag0 = 0x<#=props.Aggregate(0, (agg,p) => agg | (((p.Init != Init.No) && (p.Index/32==0) ? 1:0)<<p.Index)).ToString("x")#>u;
private UInt32 _flag1 = 0x<#=props.Aggregate(0, (agg,p) => agg | (((p.Init != Init.No) && (p.Index/32==1) ? 1:0)<<p.Index)).ToString("x")#>u;
// Mark all fields with delay initialization support as requiring initialization.
private UInt32 _initFlag0 = 0x<#=props.Aggregate(0, (agg,p) => agg | (((p.Init != Init.No) && (p.Index/32==0) ? 1:0)<<p.Index)).ToString("x")#>u;
private UInt32 _initFlag1 = 0x<#=props.Aggregate(0, (agg,p) => agg | (((p.Init != Init.No) && (p.Index/32==1) ? 1:0)<<p.Index)).ToString("x")#>u;
internal interface IPropertySource
{
<# foreach(var prop in props) { #>
<# if (prop.Init == Init.Yes) { #>
<#=prop.Type#> Get<#=prop.Name#>();
<# } #>
<# if (prop.Init == Init.Maybe) { #>
bool TryGet<#=prop.Name#>(ref <#=prop.Type#> value);
<# } #>
<# } #>
}
<# foreach(var prop in props) { #>
private <#=prop.Type#> _<#=prop.Name#>;
<# } #>
<# foreach(var prop in props) { #>
<# // call TryGet once if init flag is set, clear value flag if TryGet returns false
if (prop.Init == Init.Maybe) { #>
bool InitProperty<#=prop.Name#>()
{
if (!_propertySource.TryGet<#=prop.Name#>(ref _<#=prop.Name#>))
{
<#=Clear(prop.Index)#>;
<#=CompleteInit(prop.Index)#>;
return false;
}
<#=CompleteInit(prop.Index)#>;
return true;
}
<# } #>
<# } #>
<# foreach(var prop in props) { #>
internal <#=prop.Type#> <#=prop.Name#>
{
get
{
<# // call Get once if init flag is set
if (prop.Init == Init.Yes) { #>
if (<#=IsInitRequired(prop.Index)#>)
{
_<#=prop.Name#> = _propertySource.Get<#=prop.Name#>();
<#=CompleteInit(prop.Index)#>;
}
<# } #>
<# // call TryGet once if init flag is set, clear value flag if TryGet returns false
if (prop.Init == Init.Maybe) { #>
if (<#=IsInitRequired(prop.Index)#>)
{
InitProperty<#=prop.Name#>();
}
<# } #>
return _<#=prop.Name#>;
}
set
{
<# // clear init flag - the assigned value is definitive
if (prop.Init != Init.No) { #>
<#=CompleteInit(prop.Index)#>;
<# } #>
<#=Set(prop.Index)#>;
_<#=prop.Name#> = value;
}
}
<# } #>
private bool PropertiesContainsKey(string key)
{
switch (key.Length)
{
<# foreach(var length in lengths) { #>
case <#=length.Key#>:
<# foreach(var prop in length) { #>
if (<#=IsSet(prop.Index)#> && string.Equals(key, "<#=prop.Key#>", StringComparison.Ordinal))
{
<# // variable maybe init might revert
if (prop.Init == Init.Maybe) { #>
if (<#=IsInitCompleted(prop.Index)#> || InitProperty<#=prop.Name#>())
{
return true;
}
<# } else { #>
return true;
<# } #>
}
<# } #>
break;
<# } #>
}
return false;
}
private bool PropertiesTryGetValue(string key, out object value)
{
switch (key.Length)
{
<# foreach(var length in lengths) { #>
case <#=length.Key#>:
<# foreach(var prop in length) { #>
if (<#=IsSet(prop.Index)#> && string.Equals(key, "<#=prop.Key#>", StringComparison.Ordinal))
{
value = <#=prop.Name#>;
<# if (prop.Init == Init.Maybe) { #>
// Delayed initialization in the property getter may determine that the element is not actually present
if (!<#=IsSet(prop.Index)#>)
{
value = default(<#=prop.Type#>);
return false;
}
<# } #>
return true;
}
<# } #>
break;
<# } #>
}
value = null;
return false;
}
private bool PropertiesTrySetValue(string key, object value)
{
switch (key.Length)
{
<# foreach(var length in lengths) { #>
case <#=length.Key#>:
<# foreach(var prop in length) { #>
if (string.Equals(key, "<#=prop.Key#>", StringComparison.Ordinal))
{
<#=prop.Name#> = (<#=prop.Type#>)value;
return true;
}
<# } #>
break;
<# } #>
}
return false;
}
private bool PropertiesTryRemove(string key)
{
switch (key.Length)
{
<# foreach(var length in lengths) { #>
case <#=length.Key#>:
<# foreach(var prop in length) { #>
if (<#=IsSet(prop.Index)#> && string.Equals(key, "<#=prop.Key#>", StringComparison.Ordinal))
{
<# if (prop.Init != Init.No) { #>
<#=CompleteInit(prop.Index)#>;
<# } #>
<#=Clear(prop.Index)#>;
_<#=prop.Name#> = default(<#=prop.Type#>);
// This can return true incorrectly for values that delayed initialization may determine are not actually present.
return true;
}
<# } #>
break;
<# } #>
}
return false;
}
private IEnumerable<string> PropertiesKeys()
{
<# foreach(var prop in props) { #>
if (<#=IsSet(prop.Index)#>)
{
<# if (prop.Init == Init.Maybe) { #>
if (<#=IsInitCompleted(prop.Index)#> || InitProperty<#=prop.Name#>())
{
yield return "<#=prop.Key#>";
}
<# } else { #>
yield return "<#=prop.Key#>";
<# } #>
}
<# } #>
}
private IEnumerable<object> PropertiesValues()
{
<# foreach(var prop in props) { #>
if (<#=IsSet(prop.Index)#>)
{
<# if (prop.Init == Init.Maybe) { #>
if (<#=IsInitCompleted(prop.Index)#> || InitProperty<#=prop.Name#>())
{
yield return <#=prop.Name#>;
}
<# } else { #>
yield return <#=prop.Name#>;
<# } #>
}
<# } #>
}
private IEnumerable<KeyValuePair<string, object>> PropertiesEnumerable()
{
<# foreach(var prop in props) { #>
if (<#=IsSet(prop.Index)#>)
{
<# if (prop.Init == Init.Maybe) { #>
if (<#=IsInitCompleted(prop.Index)#> || InitProperty<#=prop.Name#>())
{
yield return new KeyValuePair<string, object>("<#=prop.Key#>", <#=prop.Name#>);
}
<# } else { #>
yield return new KeyValuePair<string, object>("<#=prop.Key#>", <#=prop.Name#>);
<# } #>
}
<# } #>
}
}
}

Просмотреть файл

@ -1,165 +0,0 @@
// -----------------------------------------------------------------------
// <copyright file="CallEnvironment.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------
// Copyright 2011-2012 Katana contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Microsoft.AspNet.Server.WebListener
{
internal partial class CallEnvironment : IDictionary<string, object>
{
private static readonly IDictionary<string, object> WeakNilEnvironment = new NilEnvDictionary();
private readonly IPropertySource _propertySource;
private IDictionary<string, object> _extra = WeakNilEnvironment;
internal CallEnvironment(IPropertySource propertySource)
{
_propertySource = propertySource;
}
private IDictionary<string, object> Extra
{
get { return _extra; }
}
private IDictionary<string, object> StrongExtra
{
get
{
if (_extra == WeakNilEnvironment)
{
Interlocked.CompareExchange(ref _extra, new Dictionary<string, object>(), WeakNilEnvironment);
}
return _extra;
}
}
internal bool IsExtraDictionaryCreated
{
get { return _extra != WeakNilEnvironment; }
}
public object this[string key]
{
get
{
object value;
return PropertiesTryGetValue(key, out value) ? value : Extra[key];
}
set
{
if (!PropertiesTrySetValue(key, value))
{
StrongExtra[key] = value;
}
}
}
public void Add(string key, object value)
{
if (!PropertiesTrySetValue(key, value))
{
StrongExtra.Add(key, value);
}
}
public bool ContainsKey(string key)
{
return PropertiesContainsKey(key) || Extra.ContainsKey(key);
}
public ICollection<string> Keys
{
get { return PropertiesKeys().Concat(Extra.Keys).ToArray(); }
}
public bool Remove(string key)
{
// Although this is a mutating operation, Extra is used instead of StrongExtra,
// because if a real dictionary has not been allocated the default behavior of the
// nil dictionary is perfectly fine.
return PropertiesTryRemove(key) || Extra.Remove(key);
}
public bool TryGetValue(string key, out object value)
{
return PropertiesTryGetValue(key, out value) || Extra.TryGetValue(key, out value);
}
public ICollection<object> Values
{
get { return PropertiesValues().Concat(Extra.Values).ToArray(); }
}
public void Add(KeyValuePair<string, object> item)
{
((IDictionary<string, object>)this).Add(item.Key, item.Value);
}
public void Clear()
{
foreach (var key in PropertiesKeys())
{
PropertiesTryRemove(key);
}
Extra.Clear();
}
public bool Contains(KeyValuePair<string, object> item)
{
object value;
return ((IDictionary<string, object>)this).TryGetValue(item.Key, out value) && Object.Equals(value, item.Value);
}
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
PropertiesEnumerable().Concat(Extra).ToArray().CopyTo(array, arrayIndex);
}
public int Count
{
get { return PropertiesKeys().Count() + Extra.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(KeyValuePair<string, object> item)
{
return ((IDictionary<string, object>)this).Contains(item) &&
((IDictionary<string, object>)this).Remove(item.Key);
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return PropertiesEnumerable().Concat(Extra).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IDictionary<string, object>)this).GetEnumerator();
}
}
}

Просмотреть файл

@ -10,14 +10,18 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if NET45
using System.Security.Authentication.ExtendedProtection; using System.Security.Authentication.ExtendedProtection;
using System.Security.Cryptography.X509Certificates;
#endif
using System.Security.Principal; using System.Security.Principal;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.HttpFeature;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
internal sealed unsafe class Request : IDisposable internal sealed class Request : IHttpRequestInformation, IHttpConnection, IHttpTransportLayerSecurity, IDisposable
{ {
private RequestContext _requestContext; private RequestContext _requestContext;
private NativeRequestContext _nativeRequestContext; private NativeRequestContext _nativeRequestContext;
@ -27,29 +31,46 @@ namespace Microsoft.AspNet.Server.WebListener
private ulong _contextId; private ulong _contextId;
private SslStatus _sslStatus; private SslStatus _sslStatus;
private string _scheme;
private string _httpMethod; private string _httpMethod;
private Version _httpVersion; private Version _httpVersion;
private string _httpProtocolVersion;
private Uri _requestUri; // private Uri _requestUri;
private string _rawUrl; private string _rawUrl;
private string _cookedUrlHost; private string _cookedUrlHost;
private string _cookedUrlPath; private string _cookedUrlPath;
private string _cookedUrlQuery; private string _cookedUrlQuery;
private string _pathBase;
private string _path;
private RequestHeaders _headers; #if NET45
private X509Certificate _clientCert;
#endif
private IDictionary<string, string[]> _headers;
private BoundaryType _contentBoundaryType; private BoundaryType _contentBoundaryType;
private long _contentLength; private long _contentLength;
private Stream _nativeStream;
private Stream _requestStream; private Stream _requestStream;
private SocketAddress _localEndPoint; private SocketAddress _localEndPoint;
private SocketAddress _remoteEndPoint; private SocketAddress _remoteEndPoint;
#if NET45
private IPAddress _remoteIpAddress;
private IPAddress _localIpAddress;
#endif
private int? _remotePort;
private int? _localPort;
private bool? _isLocal;
private IPrincipal _user; private IPrincipal _user;
private bool _isDisposed = false; private bool _isDisposed = false;
private CancellationTokenRegistration _disconnectRegistration; private CancellationTokenRegistration _disconnectRegistration;
internal Request(RequestContext httpContext, NativeRequestContext memoryBlob) internal unsafe Request(RequestContext httpContext, NativeRequestContext memoryBlob)
{ {
// TODO: Verbose log // TODO: Verbose log
_requestContext = httpContext; _requestContext = httpContext;
@ -82,6 +103,24 @@ namespace Microsoft.AspNet.Server.WebListener
_cookedUrlQuery = Marshal.PtrToStringUni((IntPtr)cookedUrl.pQueryString, cookedUrl.QueryStringLength / 2); _cookedUrlQuery = Marshal.PtrToStringUni((IntPtr)cookedUrl.pQueryString, cookedUrl.QueryStringLength / 2);
} }
Prefix prefix = httpContext.Server.UriPrefixes[(int)_contextId];
string orriginalPath = RequestPath;
// These paths are both unescaped already.
if (orriginalPath.Length == prefix.Path.Length - 1)
{
// They matched exactly except for the trailing slash.
_pathBase = orriginalPath;
_path = string.Empty;
}
else
{
// url: /base/path, prefix: /base/, base: /base, path: /path
// url: /, prefix: /, base: , path: /
_pathBase = orriginalPath.Substring(0, prefix.Path.Length - 1);
_path = orriginalPath.Substring(prefix.Path.Length - 1);
}
int major = memoryBlob.RequestBlob->Version.MajorVersion; int major = memoryBlob.RequestBlob->Version.MajorVersion;
int minor = memoryBlob.RequestBlob->Version.MinorVersion; int minor = memoryBlob.RequestBlob->Version.MinorVersion;
if (major == 1 && minor == 1) if (major == 1 && minor == 1)
@ -155,16 +194,16 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
// Without the leading ? // With the leading ?, if any
internal string Query public string QueryString
{ {
get get
{ {
if (!string.IsNullOrWhiteSpace(_cookedUrlQuery)) return _cookedUrlQuery ?? string.Empty;
{
return _cookedUrlQuery.Substring(1);
} }
return 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. // TODO: Move this to the constructor, that's where it will be called.
internal long ContentLength64 internal long ContentLength64
{ {
@ -210,40 +268,101 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
internal IDictionary<string, string[]> Headers public IDictionary<string, string[]> Headers
{ {
get get
{ {
return _headers; return _headers;
} }
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
_headers = value;
}
} }
internal string HttpMethod public string Method
{ {
get get
{ {
return _httpMethod; return _httpMethod;
} }
set
{
_httpMethod = value;
}
} }
internal Stream InputStream internal Stream NativeStream
{
get
{
if (_nativeStream == null)
{
// TODO: Move this to the constructor (or a lazy Env dictionary)
_nativeStream = HasEntityBody ? new RequestStream(RequestContext) : Stream.Null;
}
return _nativeStream;
}
}
public Stream Body
{ {
get get
{ {
if (_requestStream == null) if (_requestStream == null)
{ {
// TODO: Move this to the constructor (or a lazy Env dictionary) // TODO: Move this to the constructor (or a lazy Env dictionary)
_requestStream = HasEntityBody ? new RequestStream(RequestContext) : Stream.Null; _requestStream = NativeStream;
} }
return _requestStream; return _requestStream;
} }
set
{
_requestStream = value;
}
} }
internal bool IsLocal public string PathBase
{ {
get get
{ {
return LocalEndPoint.GetIPAddressString().Equals(RemoteEndPoint.GetIPAddressString()); return _pathBase;
}
set
{
_pathBase = value;
}
}
public string Path
{
get
{
return _path;
}
set
{
_path = value;
}
}
public bool IsLocal
{
get
{
if (!_isLocal.HasValue)
{
_isLocal = LocalEndPoint.GetIPAddressString().Equals(RemoteEndPoint.GetIPAddressString());
}
return _isLocal.Value;
}
set
{
_isLocal = value;
} }
} }
@ -254,7 +373,7 @@ namespace Microsoft.AspNet.Server.WebListener
return _sslStatus != SslStatus.Insecure; return _sslStatus != SslStatus.Insecure;
} }
} }
/*
internal string RawUrl internal string RawUrl
{ {
get get
@ -262,7 +381,7 @@ namespace Microsoft.AspNet.Server.WebListener
return _rawUrl; return _rawUrl;
} }
} }
*/
internal Version ProtocolVersion internal Version ProtocolVersion
{ {
get get
@ -271,22 +390,34 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
internal string Protocol public string Protocol
{ {
get get
{
if (_httpProtocolVersion == null)
{ {
if (_httpVersion.Major == 1) if (_httpVersion.Major == 1)
{ {
if (_httpVersion.Minor == 1) if (_httpVersion.Minor == 1)
{ {
return "HTTP/1.1"; _httpProtocolVersion = "HTTP/1.1";
} }
else if (_httpVersion.Minor == 0) else if (_httpVersion.Minor == 0)
{ {
return "HTTP/1.0"; _httpProtocolVersion = "HTTP/1.0";
} }
} }
return "HTTP/" + _httpVersion.ToString(2); else
{
_httpProtocolVersion = "HTTP/" + _httpVersion.ToString(2);
}
}
return _httpProtocolVersion;
}
set
{
// TODO: Set _httpVersion?
_httpProtocolVersion = value;
} }
} }
@ -326,15 +457,87 @@ namespace Microsoft.AspNet.Server.WebListener
return _localEndPoint; return _localEndPoint;
} }
} }
#if NET45
internal string RequestScheme public IPAddress RemoteIpAddress
{ {
get get
{ {
return IsSecureConnection ? Constants.HttpsScheme : Constants.HttpScheme; if (_remoteIpAddress == null)
{
_remoteIpAddress = IPAddress.Parse(RemoteEndPoint.GetIPAddressString()); // TODO: Create directly from bytes
}
return _remoteIpAddress;
}
set
{
_remoteIpAddress = value;
} }
} }
public IPAddress LocalIpAddress
{
get
{
if (_localIpAddress == null)
{
_localIpAddress = IPAddress.Parse(LocalEndPoint.GetIPAddressString()); // TODO: Create directly from bytes
}
return _localIpAddress;
}
set
{
_localIpAddress = value;
}
}
#endif
public int RemotePort
{
get
{
if (!_remotePort.HasValue)
{
_remotePort = RemoteEndPoint.GetPort();
}
return _remotePort.Value;
}
set
{
_remotePort = value;
}
}
public int LocalPort
{
get
{
if (!_localPort.HasValue)
{
_localPort = LocalEndPoint.GetPort();
}
return _localPort.Value;
}
set
{
_localPort = value;
}
}
public string Scheme
{
get
{
if (_scheme == null)
{
_scheme = IsSecureConnection ? Constants.HttpsScheme : Constants.HttpScheme;
}
return _scheme;
}
set
{
_scheme = value;
}
}
/*
internal Uri RequestUri internal Uri RequestUri
{ {
get get
@ -348,7 +551,7 @@ namespace Microsoft.AspNet.Server.WebListener
return _requestUri; return _requestUri;
} }
} }
*/
internal string RequestPath internal string RequestPath
{ {
get get
@ -391,6 +594,47 @@ namespace Microsoft.AspNet.Server.WebListener
#endif #endif
} }
// Populates the client certificate. The result may be null if there is no client cert.
// TODO: Does it make sense for this to be invoked multiple times (e.g. renegotiate)? Client and server code appear to
// enable this, but it's unclear what Http.Sys would do.
async Task IHttpTransportLayerSecurity.LoadAsync()
{
if (SslStatus == SslStatus.Insecure)
{
// Non-SSL
return;
}
// TODO: Verbose log
#if NET45
if (_clientCert != null)
{
return;
}
ClientCertLoader certLoader = new ClientCertLoader(RequestContext);
try
{
await certLoader.LoadClientCertificateAsync().SupressContext();
// Populate the environment.
if (certLoader.ClientCert != null)
{
_clientCert = certLoader.ClientCert;
}
// TODO: Expose errors and exceptions?
}
catch (Exception)
{
if (certLoader != null)
{
certLoader.Dispose();
}
throw;
}
#else
throw new NotImplementedException();
#endif
}
// Use this to save the blob from dispose if this object was never used (never given to a user) and is about to be // Use this to save the blob from dispose if this object was never used (never given to a user) and is about to be
// disposed. // disposed.
internal void DetachBlob(NativeRequestContext memoryBlob) internal void DetachBlob(NativeRequestContext memoryBlob)
@ -419,9 +663,9 @@ namespace Microsoft.AspNet.Server.WebListener
_nativeRequestContext = null; _nativeRequestContext = null;
} }
_disconnectRegistration.Dispose(); _disconnectRegistration.Dispose();
if (_requestStream != null) if (_nativeStream != null)
{ {
_requestStream.Dispose(); _nativeStream.Dispose();
} }
} }
@ -435,9 +679,9 @@ namespace Microsoft.AspNet.Server.WebListener
internal void SwitchToOpaqueMode() internal void SwitchToOpaqueMode()
{ {
if (_requestStream == null || _requestStream == Stream.Null) if (_nativeStream == null || _nativeStream == Stream.Null)
{ {
_requestStream = new RequestStream(RequestContext); _nativeStream = new RequestStream(RequestContext);
} }
} }

Просмотреть файл

@ -15,17 +15,19 @@ using System.Runtime.InteropServices;
using System.Security.Authentication.ExtendedProtection; using System.Security.Authentication.ExtendedProtection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.HttpFeature;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>; using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>;
using OpaqueFunc = Func<IDictionary<string, object>, Task>; using OpaqueFunc = Func<IDictionary<string, object>, Task>;
internal sealed class RequestContext : IDisposable, CallEnvironment.IPropertySource internal sealed class RequestContext : IDisposable
{ {
private static readonly string[] ZeroContentLength = new[] { "0" }; private static readonly string[] ZeroContentLength = new[] { "0" };
private CallEnvironment _environment; private FeatureCollection _features;
private OwinWebListener _server; private OwinWebListener _server;
private Request _request; private Request _request;
private Response _response; private Response _response;
@ -41,17 +43,17 @@ namespace Microsoft.AspNet.Server.WebListener
_memoryBlob = memoryBlob; _memoryBlob = memoryBlob;
_request = new Request(this, _memoryBlob); _request = new Request(this, _memoryBlob);
_response = new Response(this); _response = new Response(this);
_environment = new CallEnvironment(this);
_cts = new CancellationTokenSource(); _cts = new CancellationTokenSource();
PopulateEnvironment(); _features = new FeatureCollection();
PopulateFeatures();
_request.ReleasePins(); _request.ReleasePins();
} }
internal CallEnvironment Environment internal IFeatureCollection Features
{ {
get { return _environment; } get { return _features; }
} }
internal Request Request internal Request Request
@ -99,105 +101,31 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
private void PopulateEnvironment() private void PopulateFeatures()
{ {
// General _features.Add(typeof(IHttpRequestInformation), Request);
_environment.OwinVersion = Constants.OwinVersion; _features.Add(typeof(IHttpConnection), Request);
_environment.CallCancelled = _cts.Token; if (Request.IsSecureConnection)
{
// TODO: Should this feature be conditional? Should we add this for HTTP requests?
_features.Add(typeof(IHttpTransportLayerSecurity), Request);
}
_features.Add(typeof(IHttpResponseInformation), Response);
_features.Add(typeof(IHttpSendFile), Response);
// TODO:
// _environment.CallCancelled = _cts.Token;
// _environment.User = _request.User;
// Opaque/WebSockets
// Channel binding
/*
// Server // Server
_environment.ServerCapabilities = _server.Capabilities;
_environment.Listener = _server; _environment.Listener = _server;
// 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; _environment.ConnectionId = _request.ConnectionId;
*/
if (_request.IsSecureConnection)
{
_environment.LoadClientCert = LoadClientCertificateAsync;
} }
/*
if (_request.User != null)
{
_environment.User = _request.User;
}
// Response
_environment.ResponseStatusCode = 200;
_environment.ResponseHeaders = _response.Headers;
_environment.ResponseBody = _response.OutputStream;
_environment.SendFileAsync = _response.SendFileAsync;
_environment.OnSendingHeaders = _response.RegisterForOnSendingHeaders;
Contract.Assert(!_environment.IsExtraDictionaryCreated,
"All server keys should have a reserved slot in the environment.");
}
// Find the closest matching prefix and use it to separate the request path in to path and base path.
// Scheme and port must match. Path will use a longest match. Host names are more complicated due to
// wildcards, IP addresses, etc.
private void SetPaths()
{
Prefix prefix = _server.UriPrefixes[(int)Request.ContextId];
string orriginalPath = _request.RequestPath;
// These paths are both unescaped already.
if (orriginalPath.Length == prefix.Path.Length - 1)
{
// They matched exactly except for the trailing slash.
_environment.RequestPathBase = orriginalPath;
_environment.RequestPath = string.Empty;
}
else
{
// url: /base/path, prefix: /base/, base: /base, path: /path
// url: /, prefix: /, base: , path: /
_environment.RequestPathBase = orriginalPath.Substring(0, prefix.Path.Length - 1);
_environment.RequestPath = orriginalPath.Substring(prefix.Path.Length - 1);
}
}
// Lazy environment init
public Stream GetRequestBody()
{
return _request.InputStream;
}
public string GetRemoteIpAddress()
{
return _request.RemoteEndPoint.GetIPAddressString();
}
public string GetRemotePort()
{
return _request.RemoteEndPoint.GetPort().ToString(CultureInfo.InvariantCulture.NumberFormat);
}
public string GetLocalIpAddress()
{
return _request.LocalEndPoint.GetIPAddressString();
}
public string GetLocalPort()
{
return _request.LocalEndPoint.GetPort().ToString(CultureInfo.InvariantCulture.NumberFormat);
}
public bool GetIsLocal()
{
return _request.IsLocal;
}
public bool TryGetOpaqueUpgrade(ref Action<IDictionary<string, object>, OpaqueFunc> value) public bool TryGetOpaqueUpgrade(ref Action<IDictionary<string, object>, OpaqueFunc> value)
{ {
if (_request.IsUpgradable) if (_request.IsUpgradable)
@ -213,6 +141,7 @@ namespace Microsoft.AspNet.Server.WebListener
value = Server.GetChannelBinding(Request.ConnectionId, Request.IsSecureConnection); value = Server.GetChannelBinding(Request.ConnectionId, Request.IsSecureConnection);
return value != null; return value != null;
} }
*/
public void Dispose() public void Dispose()
{ {
@ -290,42 +219,6 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
// Populates the environment ClicentCertificate. The result may be null if there is no client cert.
// TODO: Does it make sense for this to be invoked multiple times (e.g. renegotiate)? Client and server code appear to
// enable this, but it's unclear what Http.Sys would do.
private async Task LoadClientCertificateAsync()
{
if (Request.SslStatus == SslStatus.Insecure)
{
// Non-SSL
return;
}
// TODO: Verbose log
#if NET45
ClientCertLoader certLoader = new ClientCertLoader(this);
try
{
await certLoader.LoadClientCertificateAsync().SupressContext();
// Populate the environment.
if (certLoader.ClientCert != null)
{
Environment.ClientCert = certLoader.ClientCert;
}
// TODO: Expose errors and exceptions?
}
catch (Exception)
{
if (certLoader != null)
{
certLoader.Dispose();
}
throw;
}
#else
throw new NotImplementedException();
#endif
}
internal void OpaqueUpgrade(IDictionary<string, object> parameters, OpaqueFunc callback) internal void OpaqueUpgrade(IDictionary<string, object> parameters, OpaqueFunc callback)
{ {
// Parameters are ignored for now // Parameters are ignored for now
@ -339,8 +232,8 @@ namespace Microsoft.AspNet.Server.WebListener
} }
// Set the status code and reason phrase // Set the status code and reason phrase
Environment.ResponseStatusCode = (int)HttpStatusCode.SwitchingProtocols; Response.StatusCode = (int)HttpStatusCode.SwitchingProtocols;
Environment.ResponseReasonPhrase = HttpReasonPhrase.Get(HttpStatusCode.SwitchingProtocols); Response.ReasonPhrase = HttpReasonPhrase.Get(HttpStatusCode.SwitchingProtocols);
// Store the callback and process it after the stack unwind. // Store the callback and process it after the stack unwind.
_opaqueCallback = callback; _opaqueCallback = callback;
@ -351,7 +244,7 @@ namespace Microsoft.AspNet.Server.WebListener
{ {
// If an upgrade was requested, perform it // If an upgrade was requested, perform it
if (!Response.SentHeaders && _opaqueCallback != null if (!Response.SentHeaders && _opaqueCallback != null
&& Environment.ResponseStatusCode == (int)HttpStatusCode.SwitchingProtocols) && Response.StatusCode == (int)HttpStatusCode.SwitchingProtocols)
{ {
Response.SendOpaqueUpgrade(); Response.SendOpaqueUpgrade();
@ -368,21 +261,21 @@ namespace Microsoft.AspNet.Server.WebListener
opaqueEnv[Constants.OpaqueVersionKey] = Constants.OpaqueVersion; opaqueEnv[Constants.OpaqueVersionKey] = Constants.OpaqueVersion;
// TODO: Separate CT? // TODO: Separate CT?
opaqueEnv[Constants.OpaqueCallCancelledKey] = Environment.CallCancelled; // opaqueEnv[Constants.OpaqueCallCancelledKey] = Environment.CallCancelled;
Request.SwitchToOpaqueMode(); Request.SwitchToOpaqueMode();
Response.SwitchToOpaqueMode(); Response.SwitchToOpaqueMode();
opaqueEnv[Constants.OpaqueStreamKey] = new OpaqueStream(Request.InputStream, Response.OutputStream); opaqueEnv[Constants.OpaqueStreamKey] = new OpaqueStream(Request.NativeStream, Response.NativeStream);
return opaqueEnv; return opaqueEnv;
} }
internal void SetFatalResponse() internal void SetFatalResponse()
{ {
Environment.ResponseStatusCode = 500; Response.StatusCode = 500;
Environment.ResponseReasonPhrase = string.Empty; Response.ReasonPhrase = string.Empty;
Environment.ResponseHeaders.Clear(); Response.Headers.Clear();
Environment.ResponseHeaders.Add(HttpKnownHeaderNames.ContentLength, ZeroContentLength); Response.Headers.Add(HttpKnownHeaderNames.ContentLength, ZeroContentLength);
} }
} }
} }

Просмотреть файл

@ -14,14 +14,17 @@ using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.HttpFeature;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
internal sealed unsafe class Response : IDisposable internal sealed unsafe class Response : IHttpResponseInformation, IHttpSendFile, IDisposable
{ {
private ResponseState _responseState; private ResponseState _responseState;
private IDictionary<string, string[]> _headers; private IDictionary<string, string[]> _headers;
private ResponseStream _responseStream; private string _reasonPhrase;
private ResponseStream _nativeStream;
private Stream _responseStream;
private long _contentLength; private long _contentLength;
private BoundaryType _boundaryType; private BoundaryType _boundaryType;
private UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE _nativeResponse; private UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE _nativeResponse;
@ -67,31 +70,55 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
internal Stream OutputStream public int StatusCode
{
get { return _nativeResponse.StatusCode; }
set
{
if (value <= 100 || 999 < value)
{
throw new ArgumentOutOfRangeException("value", value, string.Format(Resources.Exception_InvalidStatusCode, value));
}
_nativeResponse.StatusCode = (ushort)value;
}
}
public string ReasonPhrase
{
get { return _reasonPhrase; }
set { _reasonPhrase = value; }
}
internal ResponseStream NativeStream
{ {
get get
{ {
CheckDisposed(); CheckDisposed();
EnsureResponseStream(); EnsureResponseStream();
return _responseStream; return _nativeStream;
} }
} }
internal int GetStatusCode() public Stream Body
{ {
int statusCode = _requestContext.Environment.ResponseStatusCode ?? 200; get
if (statusCode <= 100 || statusCode > 999)
{ {
// TODO: Move this validation to the dictionary facade so it throws when the app sets it, rather than durring send? if (_responseStream == null)
throw new InvalidOperationException(string.Format(Resources.Exception_InvalidStatusCode, statusCode)); {
_responseStream = NativeStream;
}
return _responseStream;
}
set
{
_responseStream = value;
} }
return statusCode;
} }
internal string GetReasonPhrase(int statusCode) internal string GetReasonPhrase(int statusCode)
{ {
// TODO: Validate user input for illegal chars, length limit, etc.? // TODO: Validate user input for illegal chars, length limit, etc.?
string reasonPhrase = _requestContext.Environment.ResponseReasonPhrase; string reasonPhrase = ReasonPhrase;
if (string.IsNullOrWhiteSpace(reasonPhrase)) if (string.IsNullOrWhiteSpace(reasonPhrase))
{ {
// if the user hasn't set this, generated on the fly, if possible. // if the user hasn't set this, generated on the fly, if possible.
@ -124,11 +151,16 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
internal IDictionary<string, string[]> Headers public IDictionary<string, string[]> Headers
{ {
get get { return _headers; }
set
{ {
return _headers; if (value == null)
{
throw new ArgumentNullException("value");
}
_headers = value;
} }
} }
@ -142,6 +174,7 @@ namespace Microsoft.AspNet.Server.WebListener
private Version GetProtocolVersion() private Version GetProtocolVersion()
{ {
/*
Version requestVersion = Request.ProtocolVersion; Version requestVersion = Request.ProtocolVersion;
Version responseVersion = requestVersion; Version responseVersion = requestVersion;
string protocolVersion = RequestContext.Environment.Get<string>(Constants.HttpResponseProtocolKey); string protocolVersion = RequestContext.Environment.Get<string>(Constants.HttpResponseProtocolKey);
@ -170,7 +203,11 @@ namespace Microsoft.AspNet.Server.WebListener
} }
// Return the lesser of the two versions. There are only two, so it it will always be 1.0. // Return the lesser of the two versions. There are only two, so it it will always be 1.0.
return Constants.V1_0; return Constants.V1_0;*/
// TODO: IHttpResponseInformation does not define a response protocol version. Http.Sys doesn't let
// us send anything but 1.1 anyways, but we could at least use it to set things like the connection header.
return Request.ProtocolVersion;
} }
public void Dispose() public void Dispose()
@ -188,7 +225,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
// TODO: Verbose log // TODO: Verbose log
EnsureResponseStream(); EnsureResponseStream();
_responseStream.Dispose(); _nativeStream.Dispose();
_responseState = ResponseState.Closed; _responseState = ResponseState.Closed;
} }
} }
@ -221,9 +258,9 @@ namespace Microsoft.AspNet.Server.WebListener
private void EnsureResponseStream() private void EnsureResponseStream()
{ {
if (_responseStream == null) if (_nativeStream == null)
{ {
_responseStream = new ResponseStream(RequestContext); _nativeStream = new ResponseStream(RequestContext);
} }
} }
@ -266,8 +303,6 @@ namespace Microsoft.AspNet.Server.WebListener
// TODO: Verbose log headers // TODO: Verbose log headers
_responseState = ResponseState.SentHeaders; _responseState = ResponseState.SentHeaders;
_nativeResponse.StatusCode = (ushort)GetStatusCode();
string reasonPhrase = GetReasonPhrase(_nativeResponse.StatusCode); string reasonPhrase = GetReasonPhrase(_nativeResponse.StatusCode);
/* /*
@ -369,7 +404,7 @@ namespace Microsoft.AspNet.Server.WebListener
NotifyOnSendingHeaders(); NotifyOnSendingHeaders();
// 401 // 401
if (GetStatusCode() == (ushort)HttpStatusCode.Unauthorized) if (StatusCode == (ushort)HttpStatusCode.Unauthorized)
{ {
RequestContext.Server.AuthenticationManager.SetAuthenticationChallenge(this); RequestContext.Server.AuthenticationManager.SetAuthenticationChallenge(this);
} }
@ -468,7 +503,7 @@ namespace Microsoft.AspNet.Server.WebListener
_boundaryType = BoundaryType.Chunked; _boundaryType = BoundaryType.Chunked;
} }
if (CanSendResponseBody(_requestContext.Response.GetStatusCode())) if (CanSendResponseBody(_requestContext.Response.StatusCode))
{ {
_contentLength = -1; _contentLength = -1;
} }
@ -708,25 +743,25 @@ namespace Microsoft.AspNet.Server.WebListener
internal void CancelLastWrite(SafeHandle requestQueueHandle) internal void CancelLastWrite(SafeHandle requestQueueHandle)
{ {
if (_responseStream != null) if (_nativeStream != null)
{ {
_responseStream.CancelLastWrite(requestQueueHandle); _nativeStream.CancelLastWrite(requestQueueHandle);
} }
} }
internal Task SendFileAsync(string fileName, long offset, long? count, CancellationToken cancel) public Task SendFileAsync(string path, long offset, long? count, CancellationToken cancel)
{ {
EnsureResponseStream(); EnsureResponseStream();
return _responseStream.SendFileAsync(fileName, offset, count, cancel); return _nativeStream.SendFileAsync(path, offset, count, cancel);
} }
internal void SwitchToOpaqueMode() internal void SwitchToOpaqueMode()
{ {
EnsureResponseStream(); EnsureResponseStream();
_responseStream.SwitchToOpaqueMode(); _nativeStream.SwitchToOpaqueMode();
} }
internal void RegisterForOnSendingHeaders(Action<object> callback, object state) public void OnSendingHeaders(Action<object> callback, object state)
{ {
IList<Tuple<Action<object>, object>> actions = _onSendingHeadersActions; IList<Tuple<Action<object>, object>> actions = _onSendingHeadersActions;
if (actions == null) if (actions == null)

Просмотреть файл

@ -736,7 +736,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
uint statusCode = 0; uint statusCode = 0;
if ((_requestContext.Response.BoundaryType == BoundaryType.Chunked || _requestContext.Response.BoundaryType == BoundaryType.None) && (String.Compare(_requestContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) != 0)) if ((_requestContext.Response.BoundaryType == BoundaryType.Chunked || _requestContext.Response.BoundaryType == BoundaryType.None) && (String.Compare(_requestContext.Request.Method, "HEAD", StringComparison.OrdinalIgnoreCase) != 0))
{ {
if (_requestContext.Response.BoundaryType == BoundaryType.None) if (_requestContext.Response.BoundaryType == BoundaryType.None)
{ {

Просмотреть файл

@ -1,5 +1,10 @@
{ {
"version": "0.1-alpha-*", "version": "0.1-alpha-*",
"dependencies": {
"Microsoft.AspNet.Abstractions" : "0.1-alpha-*",
"Microsoft.AspNet.HttpFeature" : "0.1-alpha-*",
"Microsoft.AspNet.FeatureModel" : "0.1-alpha-*"
},
"compilationOptions" : { "allowUnsafe": true }, "compilationOptions" : { "allowUnsafe": true },
"configurations": { "configurations": {
"net45" : { }, "net45" : { },

Просмотреть файл

@ -9,12 +9,14 @@ using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.PipelineCore;
using Xunit; using Xunit;
using Xunit.Extensions; using Xunit.Extensions;
namespace Microsoft.AspNet.Server.WebListener.Tests namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
public class AuthenticationTests public class AuthenticationTests
{ {
@ -49,7 +51,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(authType, env => using (CreateServer(authType, env =>
{ {
env["owin.ResponseStatusCode"] = 401; new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -65,7 +67,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
// TODO: Not implemented - Digest // TODO: Not implemented - Digest
using (CreateServer(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | /*AuthenticationType.Digest |*/ AuthenticationType.Basic, env => using (CreateServer(AuthenticationType.Kerberos | AuthenticationType.Negotiate | AuthenticationType.Ntlm | /*AuthenticationType.Digest |*/ AuthenticationType.Basic, env =>
{ {
env["owin.ResponseStatusCode"] = 401; new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -74,7 +76,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
Assert.Equal("Kerberos, Negotiate, NTLM, basic", response.Headers.WwwAuthenticate.ToString(), StringComparer.OrdinalIgnoreCase); Assert.Equal("Kerberos, Negotiate, NTLM, basic", response.Headers.WwwAuthenticate.ToString(), StringComparer.OrdinalIgnoreCase);
} }
} }
/* TODO: User
[Theory] [Theory]
[InlineData(AuthenticationType.Kerberos)] [InlineData(AuthenticationType.Kerberos)]
[InlineData(AuthenticationType.Negotiate)] [InlineData(AuthenticationType.Negotiate)]
@ -88,12 +90,13 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
using (CreateServer(authType, env => using (CreateServer(authType, env =>
{ {
requestCount++; requestCount++;
/ * // TODO: Expose user as feature.
object obj; object obj;
if (env.TryGetValue("server.User", out obj) && obj != null) if (env.TryGetValue("server.User", out obj) && obj != null)
{ {
return Task.FromResult(0); return Task.FromResult(0);
} }* /
env["owin.ResponseStatusCode"] = 401; new DefaultHttpContext((IFeatureCollection)env).Response.StatusCode = 401;
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -101,7 +104,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
} }
} }
*/
private IDisposable CreateServer(AuthenticationType authType, AppFunc app) private IDisposable CreateServer(AuthenticationType authType, AppFunc app)
{ {
IDictionary<string, object> properties = new Dictionary<string, object>(); IDictionary<string, object> properties = new Dictionary<string, object>();

Просмотреть файл

@ -11,11 +11,14 @@ using System.Net.Http;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.PipelineCore;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Server.WebListener.Tests namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
public class HttpsTests public class HttpsTests
{ {
@ -39,10 +42,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
byte[] body = Encoding.UTF8.GetBytes("Hello World"); byte[] body = Encoding.UTF8.GetBytes("Hello World");
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); httpContext.Response.ContentLength = body.Length;
responseHeaders["Content-Length"] = new string[] { body.Length.ToString() }; return httpContext.Response.Body.WriteAsync(body, 0, body.Length);
return env.Get<Stream>("owin.ResponseBody").WriteAsync(body, 0, body.Length);
})) }))
{ {
string response = await SendRequestAsync(Address); string response = await SendRequestAsync(Address);
@ -55,12 +58,12 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
string input = new StreamReader(env.Get<Stream>("owin.RequestBody")).ReadToEnd(); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
string input = new StreamReader(httpContext.Request.Body).ReadToEnd();
Assert.Equal("Hello World", input); Assert.Equal("Hello World", input);
byte[] body = Encoding.UTF8.GetBytes("Hello World"); byte[] body = Encoding.UTF8.GetBytes("Hello World");
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); httpContext.Response.ContentLength = body.Length;
responseHeaders["Content-Length"] = new string[] { body.Length.ToString() }; httpContext.Response.Body.Write(body, 0, body.Length);
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -72,38 +75,36 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
[Fact] [Fact]
public async Task Https_ClientCertNotSent_ClientCertNotPresent() public async Task Https_ClientCertNotSent_ClientCertNotPresent()
{ {
X509Certificate clientCert = null; using (CreateServer(async env =>
using (CreateServer(env =>
{ {
var loadAsync = env.Get<Func<Task>>("ssl.LoadClientCertAsync"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
loadAsync().Wait(); var tls = httpContext.GetFeature<IHttpTransportLayerSecurity>();
clientCert = env.Get<X509Certificate>("ssl.ClientCertificate"); Assert.NotNull(tls);
return Task.FromResult(0); await tls.LoadAsync();
Assert.Null(tls.ClientCertificate);
})) }))
{ {
string response = await SendRequestAsync(Address); string response = await SendRequestAsync(Address);
Assert.Equal(string.Empty, response); Assert.Equal(string.Empty, response);
Assert.Null(clientCert);
} }
} }
[Fact] [Fact]
public async Task Https_ClientCertRequested_ClientCertPresent() public async Task Https_ClientCertRequested_ClientCertPresent()
{ {
X509Certificate clientCert = null; using (CreateServer(async env =>
using (CreateServer(env =>
{ {
var loadAsync = env.Get<Func<Task>>("ssl.LoadClientCertAsync"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
loadAsync().Wait(); var tls = httpContext.GetFeature<IHttpTransportLayerSecurity>();
clientCert = env.Get<X509Certificate>("ssl.ClientCertificate"); Assert.NotNull(tls);
return Task.FromResult(0); await tls.LoadAsync();
Assert.NotNull(tls.ClientCertificate);
})) }))
{ {
X509Certificate2 cert = FindClientCert(); X509Certificate2 cert = FindClientCert();
Assert.NotNull(cert); Assert.NotNull(cert);
string response = await SendRequestAsync(Address, cert); string response = await SendRequestAsync(Address, cert);
Assert.Equal(string.Empty, response); Assert.Equal(string.Empty, response);
Assert.NotNull(clientCert);
} }
} }

Просмотреть файл

@ -3,7 +3,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. // Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright> // </copyright>
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
/* TODO: Opaque
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -17,7 +17,7 @@ using Xunit.Extensions;
namespace Microsoft.AspNet.Server.WebListener.Tests namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
using OpaqueUpgrade = Action<IDictionary<string, object>, Func<IDictionary<string, object>, Task>>; using OpaqueUpgrade = Action<IDictionary<string, object>, Func<IDictionary<string, object>, Task>>;
public class OpaqueUpgradeTests public class OpaqueUpgradeTests
@ -322,3 +322,4 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
} }
} }
} }
*/

Просмотреть файл

@ -11,11 +11,13 @@ using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.PipelineCore;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Server.WebListener.Tests namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
public class RequestBodyTests public class RequestBodyTests
{ {
@ -26,12 +28,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
byte[] input = new byte[100]; byte[] input = new byte[100];
int read = env.Get<Stream>("owin.RequestBody").Read(input, 0, input.Length); int read = httpContext.Request.Body.Read(input, 0, input.Length);
httpContext.Response.ContentLength = read;
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); httpContext.Response.Body.Write(input, 0, read);
responseHeaders["Content-Length"] = new string[] { read.ToString() };
env.Get<Stream>("owin.ResponseBody").Write(input, 0, read);
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -45,32 +46,28 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(async env => using (CreateServer(async env =>
{ {
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
byte[] input = new byte[100]; byte[] input = new byte[100];
int read = await env.Get<Stream>("owin.RequestBody").ReadAsync(input, 0, input.Length); int read = await httpContext.Request.Body.ReadAsync(input, 0, input.Length);
httpContext.Response.ContentLength = read;
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); await httpContext.Response.Body.WriteAsync(input, 0, read);
responseHeaders["Content-Length"] = new string[] { read.ToString() };
await env.Get<Stream>("owin.ResponseBody").WriteAsync(input, 0, read);
})) }))
{ {
string response = await SendRequestAsync(Address, "Hello World"); string response = await SendRequestAsync(Address, "Hello World");
Assert.Equal("Hello World", response); Assert.Equal("Hello World", response);
} }
} }
#if NET45
[Fact] [Fact]
public async Task RequestBody_ReadBeginEnd_Success() public async Task RequestBody_ReadBeginEnd_Success()
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
Stream requestStream = env.Get<Stream>("owin.RequestBody"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
byte[] input = new byte[100]; byte[] input = new byte[100];
int read = requestStream.EndRead(requestStream.BeginRead(input, 0, input.Length, null, null)); int read = httpContext.Request.Body.EndRead(httpContext.Request.Body.BeginRead(input, 0, input.Length, null, null));
httpContext.Response.ContentLength = read;
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); httpContext.Response.Body.EndWrite(httpContext.Response.Body.BeginWrite(input, 0, read, null, null));
responseHeaders["Content-Length"] = new string[] { read.ToString() };
Stream responseStream = env.Get<Stream>("owin.ResponseBody");
responseStream.EndWrite(responseStream.BeginWrite(input, 0, read, null, null));
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -78,18 +75,19 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
Assert.Equal("Hello World", response); Assert.Equal("Hello World", response);
} }
} }
#endif
[Fact] [Fact]
public async Task RequestBody_ReadSyncPartialBody_Success() public async Task RequestBody_ReadSyncPartialBody_Success()
{ {
StaggardContent content = new StaggardContent(); StaggardContent content = new StaggardContent();
using (CreateServer(env => using (CreateServer(env =>
{ {
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
byte[] input = new byte[10]; byte[] input = new byte[10];
int read = env.Get<Stream>("owin.RequestBody").Read(input, 0, input.Length); int read = httpContext.Request.Body.Read(input, 0, input.Length);
Assert.Equal(5, read); Assert.Equal(5, read);
content.Block.Release(); content.Block.Release();
read = env.Get<Stream>("owin.RequestBody").Read(input, 0, input.Length); read = httpContext.Request.Body.Read(input, 0, input.Length);
Assert.Equal(5, read); Assert.Equal(5, read);
return Task.FromResult(0); return Task.FromResult(0);
})) }))
@ -105,11 +103,12 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
StaggardContent content = new StaggardContent(); StaggardContent content = new StaggardContent();
using (CreateServer(async env => using (CreateServer(async env =>
{ {
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
byte[] input = new byte[10]; byte[] input = new byte[10];
int read = await env.Get<Stream>("owin.RequestBody").ReadAsync(input, 0, input.Length); int read = await httpContext.Request.Body.ReadAsync(input, 0, input.Length);
Assert.Equal(5, read); Assert.Equal(5, read);
content.Block.Release(); content.Block.Release();
read = await env.Get<Stream>("owin.RequestBody").ReadAsync(input, 0, input.Length); read = await httpContext.Request.Body.ReadAsync(input, 0, input.Length);
Assert.Equal(5, read); Assert.Equal(5, read);
})) }))
{ {

Просмотреть файл

@ -10,11 +10,13 @@ using System.Net.Http;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.PipelineCore;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Server.WebListener.Tests namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
public class RequestHeaderTests public class RequestHeaderTests
{ {
@ -25,7 +27,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
var requestHeaders = env.Get<IDictionary<string, string[]>>("owin.RequestHeaders"); var requestHeaders = new DefaultHttpContext((IFeatureCollection)env).Request.Headers;
// NOTE: The System.Net client only sends the Connection: keep-alive header on the first connection per service-point. // NOTE: The System.Net client only sends the Connection: keep-alive header on the first connection per service-point.
// Assert.Equal(2, requestHeaders.Count); // Assert.Equal(2, requestHeaders.Count);
// Assert.Equal("Keep-Alive", requestHeaders.Get("Connection")); // Assert.Equal("Keep-Alive", requestHeaders.Get("Connection"));
@ -44,7 +46,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
var requestHeaders = env.Get<IDictionary<string, string[]>>("owin.RequestHeaders"); var requestHeaders = new DefaultHttpContext((IFeatureCollection)env).Request.Headers;
Assert.Equal(4, requestHeaders.Count); Assert.Equal(4, requestHeaders.Count);
Assert.Equal("localhost:8080", requestHeaders.Get("Host")); Assert.Equal("localhost:8080", requestHeaders.Get("Host"));
Assert.Equal("close", requestHeaders.Get("Connection")); Assert.Equal("close", requestHeaders.Get("Connection"));

Просмотреть файл

@ -11,12 +11,15 @@ using System.Net.Http;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.PipelineCore;
using Xunit; using Xunit;
using Xunit.Extensions; using Xunit.Extensions;
namespace Microsoft.AspNet.Server.WebListener.Tests namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
public class RequestTests public class RequestTests
{ {
@ -27,36 +30,40 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
try try
{ {
// General keys // General keys
Assert.Equal("1.0", env.Get<string>("owin.Version")); // TODO: Assert.True(env.Get<CancellationToken>("owin.CallCancelled").CanBeCanceled);
Assert.True(env.Get<CancellationToken>("owin.CallCancelled").CanBeCanceled);
var requestInfo = httpContext.GetFeature<IHttpRequestInformation>();
// Request Keys // Request Keys
Assert.Equal("GET", env.Get<string>("owin.RequestMethod")); Assert.Equal("GET", requestInfo.Method);
Assert.Equal(Stream.Null, env.Get<Stream>("owin.RequestBody")); Assert.Equal(Stream.Null, requestInfo.Body);
Assert.NotNull(env.Get<IDictionary<string, string[]>>("owin.RequestHeaders")); Assert.NotNull(requestInfo.Headers);
Assert.Equal("http", env.Get<string>("owin.RequestScheme")); Assert.Equal("http", requestInfo.Scheme);
Assert.Equal("/basepath", env.Get<string>("owin.RequestPathBase")); Assert.Equal("/basepath", requestInfo.PathBase);
Assert.Equal("/SomePath", env.Get<string>("owin.RequestPath")); Assert.Equal("/SomePath", requestInfo.Path);
Assert.Equal("SomeQuery", env.Get<string>("owin.RequestQueryString")); Assert.Equal("?SomeQuery", requestInfo.QueryString);
Assert.Equal("HTTP/1.1", env.Get<string>("owin.RequestProtocol")); Assert.Equal("HTTP/1.1", requestInfo.Protocol);
// Server Keys // Server Keys
Assert.NotNull(env.Get<IDictionary<string, object>>("server.Capabilities")); // TODO: Assert.NotNull(env.Get<IDictionary<string, object>>("server.Capabilities"));
Assert.Equal("::1", env.Get<string>("server.RemoteIpAddress"));
Assert.NotNull(env.Get<string>("server.RemotePort")); var connectionInfo = httpContext.GetFeature<IHttpConnection>();
Assert.Equal("::1", env.Get<string>("server.LocalIpAddress")); Assert.Equal("::1", connectionInfo.RemoteIpAddress.ToString());
Assert.Equal("8080", env.Get<string>("server.LocalPort")); Assert.NotEqual(0, connectionInfo.RemotePort);
Assert.True(env.Get<bool>("server.IsLocal")); Assert.Equal("::1", connectionInfo.LocalIpAddress.ToString());
Assert.NotEqual(0, connectionInfo.LocalPort);
Assert.True(connectionInfo.IsLocal);
// Note: Response keys are validated in the ResponseTests // Note: Response keys are validated in the ResponseTests
} }
catch (Exception ex) catch (Exception ex)
{ {
byte[] body = Encoding.ASCII.GetBytes(ex.ToString()); byte[] body = Encoding.ASCII.GetBytes(ex.ToString());
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length); httpContext.Response.Body.Write(body, 0, body.Length);
} }
return Task.FromResult(0); return Task.FromResult(0);
}, "http", "localhost", "8080", "/basepath")) }, "http", "localhost", "8080", "/basepath"))
@ -78,21 +85,23 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
try try
{ {
Uri uri = new Uri(requestUri); var requestInfo = httpContext.GetFeature<IHttpRequestInformation>();
string expectedQuery = uri.Query.Length > 0 ? uri.Query.Substring(1) : string.Empty; var connectionInfo = httpContext.GetFeature<IHttpConnection>();
// Request Keys // Request Keys
Assert.Equal(scheme, env.Get<string>("owin.RequestScheme")); Assert.Equal(scheme, requestInfo.Scheme);
Assert.Equal(expectedPath, env.Get<string>("owin.RequestPath")); Assert.Equal(expectedPath, requestInfo.Path);
Assert.Equal(expectedPathBase, env.Get<string>("owin.RequestPathBase")); Assert.Equal(expectedPathBase, requestInfo.PathBase);
Assert.Equal(expectedQuery, env.Get<string>("owin.RequestQueryString")); Assert.Equal(string.Empty, requestInfo.QueryString);
Assert.Equal(port, env.Get<string>("server.LocalPort")); Assert.Equal(port, connectionInfo.LocalPort.ToString());
} }
catch (Exception ex) catch (Exception ex)
{ {
byte[] body = Encoding.ASCII.GetBytes(ex.ToString()); byte[] body = Encoding.ASCII.GetBytes(ex.ToString());
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length); httpContext.Response.Body.Write(body, 0, body.Length);
} }
return Task.FromResult(0); return Task.FromResult(0);
}, scheme, host, port, pathBase)) }, scheme, host, port, pathBase))
@ -119,15 +128,17 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
var requestInfo = httpContext.GetFeature<IHttpRequestInformation>();
try try
{ {
Assert.Equal(expectedPath, env.Get<string>("owin.RequestPath")); Assert.Equal(expectedPath, requestInfo.Path);
Assert.Equal(expectedPathBase, env.Get<string>("owin.RequestPathBase")); Assert.Equal(expectedPathBase, requestInfo.PathBase);
} }
catch (Exception ex) catch (Exception ex)
{ {
byte[] body = Encoding.ASCII.GetBytes(ex.ToString()); byte[] body = Encoding.ASCII.GetBytes(ex.ToString());
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length); httpContext.Response.Body.Write(body, 0, body.Length);
} }
return Task.FromResult(0); return Task.FromResult(0);
})) }))

Просмотреть файл

@ -11,11 +11,14 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.PipelineCore;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Server.WebListener.Tests namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
public class ResponseBodyTests public class ResponseBodyTests
{ {
@ -26,8 +29,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env.Get<Stream>("owin.ResponseBody").Write(new byte[10], 0, 10); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
return env.Get<Stream>("owin.ResponseBody").WriteAsync(new byte[10], 0, 10); httpContext.Response.Body.Write(new byte[10], 0, 10);
return httpContext.Response.Body.WriteAsync(new byte[10], 0, 10);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);
@ -45,8 +49,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["transfeR-Encoding"] = new string[] { " CHunked " }; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
Stream stream = env.Get<Stream>("owin.ResponseBody"); httpContext.Request.Headers["transfeR-Encoding"] = " CHunked ";
Stream stream = httpContext.Response.Body;
stream.EndWrite(stream.BeginWrite(new byte[10], 0, 10, null, null)); stream.EndWrite(stream.BeginWrite(new byte[10], 0, 10, null, null));
stream.Write(new byte[10], 0, 10); stream.Write(new byte[10], 0, 10);
return stream.WriteAsync(new byte[10], 0, 10); return stream.WriteAsync(new byte[10], 0, 10);
@ -67,8 +72,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 30 " }; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
Stream stream = env.Get<Stream>("owin.ResponseBody"); httpContext.Response.Headers["Content-lenGth"] = " 30 ";
Stream stream = httpContext.Response.Body;
stream.EndWrite(stream.BeginWrite(new byte[10], 0, 10, null, null)); stream.EndWrite(stream.BeginWrite(new byte[10], 0, 10, null, null));
stream.Write(new byte[10], 0, 10); stream.Write(new byte[10], 0, 10);
return stream.WriteAsync(new byte[10], 0, 10); return stream.WriteAsync(new byte[10], 0, 10);
@ -84,7 +90,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
Assert.Equal(new byte[30], await response.Content.ReadAsByteArrayAsync()); Assert.Equal(new byte[30], await response.Content.ReadAsByteArrayAsync());
} }
} }
/* TODO: response protocol
[Fact] [Fact]
public async Task ResponseBody_Http10WriteNoHeaders_DefaultsConnectionClose() public async Task ResponseBody_Http10WriteNoHeaders_DefaultsConnectionClose()
{ {
@ -104,13 +110,14 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
Assert.Equal(new byte[20], await response.Content.ReadAsByteArrayAsync()); Assert.Equal(new byte[20], await response.Content.ReadAsByteArrayAsync());
} }
} }
*/
[Fact] [Fact]
public void ResponseBody_WriteContentLengthNoneWritten_Throws() public void ResponseBody_WriteContentLengthNoneWritten_Throws()
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 20 " }; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
httpContext.Response.Headers["Content-lenGth"] = " 20 ";
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -123,8 +130,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 20 " }; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
env.Get<Stream>("owin.ResponseBody").Write(new byte[5], 0, 5); httpContext.Response.Headers["Content-lenGth"] = " 20 ";
httpContext.Response.Body.Write(new byte[5], 0, 5);
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -137,9 +145,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 10 " }; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
env.Get<Stream>("owin.ResponseBody").Write(new byte[5], 0, 5); httpContext.Response.Headers["Content-lenGth"] = " 10 ";
env.Get<Stream>("owin.ResponseBody").Write(new byte[6], 0, 6); httpContext.Response.Body.Write(new byte[5], 0, 5);
httpContext.Response.Body.Write(new byte[6], 0, 6);
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -156,9 +165,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
try try
{ {
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { " 10 " }; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
env.Get<Stream>("owin.ResponseBody").Write(new byte[10], 0, 10); httpContext.Response.Headers["Content-lenGth"] = " 10 ";
env.Get<Stream>("owin.ResponseBody").Write(new byte[9], 0, 9); httpContext.Response.Body.Write(new byte[10], 0, 10);
httpContext.Response.Body.Write(new byte[9], 0, 9);
appThrew = false; appThrew = false;
} }
catch (Exception) catch (Exception)

Просмотреть файл

@ -10,11 +10,14 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.PipelineCore;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Server.WebListener.Tests namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
public class ResponseHeaderTests public class ResponseHeaderTests
{ {
@ -44,7 +47,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
var responseInfo = httpContext.GetFeature<IHttpResponseInformation>();
var responseHeaders = responseInfo.Headers;
responseHeaders["Custom-Header1"] = new string[] { "custom1, and custom2", "custom3" }; responseHeaders["Custom-Header1"] = new string[] { "custom1, and custom2", "custom3" };
return Task.FromResult(0); return Task.FromResult(0);
})) }))
@ -66,7 +71,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
var responseInfo = httpContext.GetFeature<IHttpResponseInformation>();
var responseHeaders = responseInfo.Headers;
responseHeaders["Connection"] = new string[] { "Close" }; responseHeaders["Connection"] = new string[] { "Close" };
return Task.FromResult(0); return Task.FromResult(0);
})) }))
@ -77,7 +84,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
Assert.Equal(new string[] { "close" }, response.Headers.GetValues("Connection")); Assert.Equal(new string[] { "close" }, response.Headers.GetValues("Connection"));
} }
} }
/* TODO:
[Fact] [Fact]
public async Task ResponseHeaders_SendsHttp10_Gets11Close() public async Task ResponseHeaders_SendsHttp10_Gets11Close()
{ {
@ -113,6 +120,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
Assert.Equal(new string[] { "close" }, response.Headers.GetValues("Connection")); Assert.Equal(new string[] { "close" }, response.Headers.GetValues("Connection"));
} }
} }
*/
[Fact] [Fact]
public async Task ResponseHeaders_HTTP10Request_Gets11Close() public async Task ResponseHeaders_HTTP10Request_Gets11Close()
@ -140,9 +148,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
var responseInfo = httpContext.GetFeature<IHttpResponseInformation>();
var responseHeaders = responseInfo.Headers;
responseHeaders["Transfer-Encoding"] = new string[] { "chunked" }; responseHeaders["Transfer-Encoding"] = new string[] { "chunked" };
return env.Get<Stream>("owin.ResponseBody").WriteAsync(new byte[10], 0, 10); return responseInfo.Body.WriteAsync(new byte[10], 0, 10);
})) }))
{ {
using (HttpClient client = new HttpClient()) using (HttpClient client = new HttpClient())
@ -166,12 +176,14 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
using (CreateServer( using (CreateServer(
env => env =>
{ {
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
var responseInfo = httpContext.GetFeature<IHttpResponseInformation>();
var responseHeaders = responseInfo.Headers;
responseHeaders.Add("Custom1", new string[] { "value1a", "value1b" }); responseHeaders.Add("Custom1", new string[] { "value1a", "value1b" });
responseHeaders.Add("Custom2", new string[] { "value2a, value2b" }); responseHeaders.Add("Custom2", new string[] { "value2a, value2b" });
var body = env.Get<Stream>("owin.ResponseBody"); var body = responseInfo.Body;
body.Flush(); body.Flush();
env["owin.ResponseStatusCode"] = 404; // Ignored responseInfo.StatusCode = 404; // Ignored
responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored
return Task.FromResult(0); return Task.FromResult(0);
})) }))
@ -194,12 +206,14 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
using (CreateServer( using (CreateServer(
async env => async env =>
{ {
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
var responseInfo = httpContext.GetFeature<IHttpResponseInformation>();
var responseHeaders = responseInfo.Headers;
responseHeaders.Add("Custom1", new string[] { "value1a", "value1b" }); responseHeaders.Add("Custom1", new string[] { "value1a", "value1b" });
responseHeaders.Add("Custom2", new string[] { "value2a, value2b" }); responseHeaders.Add("Custom2", new string[] { "value2a, value2b" });
var body = env.Get<Stream>("owin.ResponseBody"); var body = responseInfo.Body;
await body.FlushAsync(); await body.FlushAsync();
env["owin.ResponseStatusCode"] = 404; // Ignored responseInfo.StatusCode = 404; // Ignored
responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored responseHeaders.Add("Custom3", new string[] { "value3a, value3b", "value3c" }); // Ignored
})) }))
{ {

Просмотреть файл

@ -13,12 +13,14 @@ using System.Net.Http;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.PipelineCore;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Server.WebListener.Tests namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
using SendFileFunc = Func<string, long, long?, CancellationToken, Task>;
public class ResponseSendFileTests public class ResponseSendFileTests
{ {
@ -32,8 +34,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
var httpContext = new DefaultHttpContext((IFeatureCollection)env);
try try
{ {
/* TODO:
IDictionary<string, object> capabilities = env.Get<IDictionary<string, object>>("server.Capabilities"); IDictionary<string, object> capabilities = env.Get<IDictionary<string, object>>("server.Capabilities");
Assert.NotNull(capabilities); Assert.NotNull(capabilities);
@ -43,14 +47,15 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
Assert.NotNull(support); Assert.NotNull(support);
Assert.Equal("Overlapped", support.Get<string>("sendfile.Concurrency")); Assert.Equal("Overlapped", support.Get<string>("sendfile.Concurrency"));
*/
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var sendFile = httpContext.GetFeature<IHttpSendFile>();
Assert.NotNull(sendFileAsync); Assert.NotNull(sendFile);
} }
catch (Exception ex) catch (Exception ex)
{ {
byte[] body = Encoding.UTF8.GetBytes(ex.ToString()); byte[] body = Encoding.UTF8.GetBytes(ex.ToString());
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length); httpContext.Response.Body.Write(body, 0, body.Length);
} }
return Task.FromResult(0); return Task.FromResult(0);
})) }))
@ -72,10 +77,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
bool? appThrew = null; bool? appThrew = null;
using (CreateServer(env => using (CreateServer(env =>
{ {
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
var sendFile = httpContext.GetFeature<IHttpSendFile>();
try try
{ {
sendFileAsync(string.Empty, 0, null, CancellationToken.None).Wait(); sendFile.SendFileAsync(string.Empty, 0, null, CancellationToken.None).Wait();
appThrew = false; appThrew = false;
} }
catch (Exception) catch (Exception)
@ -103,8 +109,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
return sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None); var sendFile = httpContext.GetFeature<IHttpSendFile>();
return sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);
@ -121,8 +128,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
return sendFileAsync(RelativeFilePath, 0, null, CancellationToken.None); var sendFile = httpContext.GetFeature<IHttpSendFile>();
return sendFile.SendFileAsync(RelativeFilePath, 0, null, CancellationToken.None);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);
@ -139,9 +147,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Transfer-EncodinG"] = new string[] { "CHUNKED" }; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var sendFile = httpContext.GetFeature<IHttpSendFile>();
return sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None); httpContext.Response.Headers["Transfer-EncodinG"] = "CHUNKED";
return sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);
@ -158,10 +167,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Transfer-EncodinG"] = new string[] { "CHUNKED" }; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var sendFile = httpContext.GetFeature<IHttpSendFile>();
sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None).Wait(); httpContext.Response.Headers["Transfer-EncodinG"] = "CHUNKED";
return sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None); sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None).Wait();
return sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);
@ -178,8 +188,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
return sendFileAsync(AbsoluteFilePath, 0, FileLength / 2, CancellationToken.None); var sendFile = httpContext.GetFeature<IHttpSendFile>();
return sendFile.SendFileAsync(AbsoluteFilePath, 0, FileLength / 2, CancellationToken.None);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);
@ -196,8 +207,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
return sendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None); var sendFile = httpContext.GetFeature<IHttpSendFile>();
return sendFile.SendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);
@ -210,8 +222,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
return sendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None); var sendFile = httpContext.GetFeature<IHttpSendFile>();
return sendFile.SendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);
@ -224,8 +237,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
return sendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None); var sendFile = httpContext.GetFeature<IHttpSendFile>();
return sendFile.SendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);
@ -242,9 +256,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { FileLength.ToString() }; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var sendFile = httpContext.GetFeature<IHttpSendFile>();
return sendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None); httpContext.Response.Headers["Content-lenGth"] = FileLength.ToString();
return sendFile.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);
@ -262,9 +277,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { "10" }; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var sendFile = httpContext.GetFeature<IHttpSendFile>();
return sendFileAsync(AbsoluteFilePath, 0, 10, CancellationToken.None); httpContext.Response.Headers["Content-lenGth"] = "10";
return sendFile.SendFileAsync(AbsoluteFilePath, 0, 10, CancellationToken.None);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);
@ -282,9 +298,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders")["Content-lenGth"] = new string[] { "0" }; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
SendFileFunc sendFileAsync = env.Get<SendFileFunc>("sendfile.SendAsync"); var sendFile = httpContext.GetFeature<IHttpSendFile>();
return sendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None); httpContext.Response.Headers["Content-lenGth"] = "0";
return sendFile.SendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(Address); HttpResponseMessage response = await SendRequestAsync(Address);

Просмотреть файл

@ -6,13 +6,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.PipelineCore;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Server.WebListener.Tests namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
public class ResponseTests public class ResponseTests
{ {
@ -23,7 +27,8 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
Assert.Equal(200, env["owin.ResponseStatusCode"]); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
Assert.Equal(200, httpContext.Response.StatusCode);
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -40,8 +45,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env["owin.ResponseStatusCode"] = 201; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
env["owin.ResponseProtocol"] = "HTTP/1.0"; // Http.Sys ignores this value httpContext.Response.StatusCode = 201;
// TODO: env["owin.ResponseProtocol"] = "HTTP/1.0"; // Http.Sys ignores this value
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -58,9 +64,10 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env["owin.ResponseStatusCode"] = 201; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
env["owin.ResponseReasonPhrase"] = "CustomReasonPhrase"; httpContext.Response.StatusCode = 201;
env["owin.ResponseProtocol"] = "HTTP/1.0"; // Http.Sys ignores this value httpContext.GetFeature<IHttpResponseInformation>().ReasonPhrase = "CustomReasonPhrase"; // TODO?
// TODO: env["owin.ResponseProtocol"] = "HTTP/1.0"; // Http.Sys ignores this value
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -77,7 +84,8 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env["owin.ResponseStatusCode"] = 901; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
httpContext.Response.StatusCode = 901;
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
@ -89,28 +97,32 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
} }
[Fact] [Fact]
public void Response_100_Throws() public async Task Response_100_Throws()
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env["owin.ResponseStatusCode"] = 100; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
httpContext.Response.StatusCode = 100;
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
Assert.Throws<AggregateException>(() => SendRequestAsync(Address).Result); HttpResponseMessage response = await SendRequestAsync(Address);
Assert.Equal(500, (int)response.StatusCode);
} }
} }
[Fact] [Fact]
public void Response_0_Throws() public async Task Response_0_Throws()
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
env["owin.ResponseStatusCode"] = 0; var httpContext = new DefaultHttpContext((IFeatureCollection)env);
httpContext.Response.StatusCode = 0;
return Task.FromResult(0); return Task.FromResult(0);
})) }))
{ {
Assert.Throws<AggregateException>(() => SendRequestAsync(Address).Result); HttpResponseMessage response = await SendRequestAsync(Address);
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
} }
} }

Просмотреть файл

@ -13,11 +13,13 @@ using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.PipelineCore;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Server.WebListener.Tests namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using AppFunc = Func<IDictionary<string, object>, Task>; using AppFunc = Func<object, Task>;
public class ServerTests public class ServerTests
{ {
@ -41,11 +43,9 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
byte[] body = Encoding.UTF8.GetBytes("Hello World"); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); httpContext.Response.ContentLength = 11;
responseHeaders["Content-Length"] = new string[] { body.Length.ToString() }; return httpContext.Response.WriteAsync("Hello World");
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
return Task.FromResult(0);
})) }))
{ {
string response = await SendRequestAsync(Address); string response = await SendRequestAsync(Address);
@ -58,13 +58,11 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
{ {
using (CreateServer(env => using (CreateServer(env =>
{ {
string input = new StreamReader(env.Get<Stream>("owin.RequestBody")).ReadToEnd(); var httpContext = new DefaultHttpContext((IFeatureCollection)env);
string input = new StreamReader(httpContext.Request.Body).ReadToEnd();
Assert.Equal("Hello World", input); Assert.Equal("Hello World", input);
byte[] body = Encoding.UTF8.GetBytes("Hello World"); httpContext.Response.ContentLength = 11;
var responseHeaders = env.Get<IDictionary<string, string[]>>("owin.ResponseHeaders"); return httpContext.Response.WriteAsync("Hello World");
responseHeaders["Content-Length"] = new string[] { body.Length.ToString() };
env.Get<Stream>("owin.ResponseBody").Write(body, 0, body.Length);
return Task.FromResult(0);
})) }))
{ {
string response = await SendRequestAsync(Address, "Hello World"); string response = await SendRequestAsync(Address, "Hello World");
@ -154,7 +152,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
Assert.True(Task.WaitAll(requestTasks.ToArray(), TimeSpan.FromSeconds(2)), "Timed out"); Assert.True(Task.WaitAll(requestTasks.ToArray(), TimeSpan.FromSeconds(2)), "Timed out");
} }
} }
/* TODO:
[Fact] [Fact]
public async Task Server_ClientDisconnects_CallCancelled() public async Task Server_ClientDisconnects_CallCancelled()
{ {
@ -204,7 +202,7 @@ namespace Microsoft.AspNet.Server.WebListener.Tests
Assert.Equal(string.Empty, response); Assert.Equal(string.Empty, response);
} }
} }
*/
private IDisposable CreateServer(AppFunc app) private IDisposable CreateServer(AppFunc app)
{ {
IDictionary<string, object> properties = new Dictionary<string, object>(); IDictionary<string, object> properties = new Dictionary<string, object>();

Просмотреть файл

@ -1,7 +1,11 @@
{ {
"version" : "0.1-alpha-*", "version" : "0.1-alpha-*",
"dependencies": { "dependencies": {
"Microsoft.AspNet.Server.WebListener" : "" "Microsoft.AspNet.Server.WebListener" : "",
"Microsoft.AspNet.Abstractions" : "0.1-alpha-*",
"Microsoft.AspNet.HttpFeature" : "0.1-alpha-*",
"Microsoft.AspNet.FeatureModel" : "0.1-alpha-*",
"Microsoft.AspNet.PipelineCore" : "0.1-alpha-*"
}, },
"configurations": { "configurations": {
"net45": { "net45": {