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
{
using AppFunc = Func<IDictionary<string, object>, Task>;
using AppFunc = Func<object, Task>;
using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>;
/// <summary>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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