Make lower level public API. Layering.

This commit is contained in:
Chris Ross 2014-03-31 12:47:28 -07:00
parent af1a97cd7c
commit 60f09fbc93
13 changed files with 236 additions and 321 deletions

View File

@ -1,55 +1,69 @@
using System; using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Server.WebListener; using Microsoft.AspNet.Server.WebListener;
using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>; namespace HelloWorld
public class Program
{ {
public static void Main(string[] args) public class Program
{ {
using (CreateServer(new AppFunc(HelloWorldApp))) public static void Main(string[] args)
{ {
Console.WriteLine("Running, press enter to exit..."); using (OwinWebListener listener = new OwinWebListener())
Console.ReadLine(); {
listener.UrlPrefixes.Add(UrlPrefix.Create("http://localhost:8080"));
listener.Start();
Console.WriteLine("Running...");
while (true)
{
RequestContext context = listener.GetContextAsync().Result;
Console.WriteLine("Accepted");
// Context:
// context.User;
// context.DisconnectToken
// context.Dispose()
// context.Abort();
// Request
// context.Request.ProtocolVersion
// context.Request.IsLocal
// context.Request.Headers // TODO: Header helpers?
// context.Request.Method
// context.Request.Body
// Content-Length - long?
// Content-Type - string
// IsSecureConnection
// HasEntityBody
// TODO: Request fields
// Content-Encoding - Encoding
// Host
// Client certs - GetCertAsync, CertErrors
// Cookies
// KeepAlive
// QueryString (parsed)
// RequestTraceIdentifier
// RawUrl
// URI
// IsWebSocketRequest
// LocalEndpoint vs LocalIP & LocalPort
// RemoteEndpoint vs RemoteIP & RemotePort
// AcceptTypes string[]
// ServiceName
// TransportContext
// Response
byte[] bytes = Encoding.ASCII.GetBytes("Hello World: " + DateTime.Now);
context.Response.ContentLength = bytes.Length;
context.Response.ContentType = "text/plain";
context.Response.Body.Write(bytes, 0, bytes.Length);
context.Dispose();
}
}
} }
} }
}
private static IDisposable CreateServer(AppFunc app)
{
IDictionary<string, object> properties = new Dictionary<string, object>();
IList<IDictionary<string, object>> addresses = new List<IDictionary<string, object>>();
properties["host.Addresses"] = addresses;
IDictionary<string, object> address = new Dictionary<string, object>();
addresses.Add(address);
address["scheme"] = "http";
address["host"] = "localhost";
address["port"] = "8080";
address["path"] = string.Empty;
return OwinServerFactory.Create(app, properties);
}
public static Task HelloWorldApp(IDictionary<string, object> environment)
{
string responseText = "Hello World";
byte[] responseBytes = Encoding.UTF8.GetBytes(responseText);
// See http://owin.org/spec/owin-1.0.0.html for standard environment keys.
Stream responseStream = (Stream)environment["owin.ResponseBody"];
IDictionary<string, string[]> responseHeaders =
(IDictionary<string, string[]>)environment["owin.ResponseHeaders"];
responseHeaders["Content-Length"] = new string[] { responseBytes.Length.ToString(CultureInfo.InvariantCulture) };
responseHeaders["Content-Type"] = new string[] { "text/plain" };
return responseStream.WriteAsync(responseBytes, 0, responseBytes.Length);
}
}

View File

@ -75,7 +75,7 @@ namespace Microsoft.AspNet.Server.WebListener
// The native request queue // The native request queue
private long? _requestQueueLength; private long? _requestQueueLength;
internal OwinWebListener() public OwinWebListener()
{ {
if (!UnsafeNclNativeMethods.HttpApi.Supported) if (!UnsafeNclNativeMethods.HttpApi.Supported)
{ {
@ -149,7 +149,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
internal bool IsListening public bool IsListening
{ {
get get
{ {
@ -332,7 +332,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
internal void Start() public void Start()
{ {
CheckDisposed(); CheckDisposed();
@ -639,7 +639,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Disposed by callback")] [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Disposed by callback")]
internal Task<RequestContext> GetContextAsync() public Task<RequestContext> GetContextAsync()
{ {
AsyncAcceptContext asyncResult = null; AsyncAcceptContext asyncResult = null;
try try

View File

@ -8,11 +8,9 @@ namespace Microsoft.AspNet.Server.WebListener
{ {
internal enum BoundaryType internal enum BoundaryType
{ {
ContentLength = 0, // Content-Length: XXX None = 0,
Chunked = 1, // Transfer-Encoding: chunked Chunked = 1, // Transfer-Encoding: chunked
Raw = 2, // the app is responsible for sending the correct headers and body encoding ContentLength = 2, // Content-Length: XXX
Multipart = 3, Invalid = 3,
None = 4,
Invalid = 5,
} }
} }

View File

@ -1,17 +0,0 @@
//------------------------------------------------------------------------------
// <copyright file="EntitySendFormat.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace Microsoft.AspNet.Server.WebListener
{
internal enum EntitySendFormat
{
ContentLength = 0, // Content-Length: XXX
Chunked = 1, // Transfer-Encoding: chunked
/*
Raw = 2, // the app is responsible for sending the correct headers and body encoding
*/
}
}

View File

@ -163,7 +163,19 @@ namespace Microsoft.AspNet.Server.WebListener
{ {
if (_httpProtocolVersion == null) if (_httpProtocolVersion == null)
{ {
_httpProtocolVersion = Request.Protocol; if (Request.ProtocolVersion.Major == 1)
{
if (Request.ProtocolVersion.Minor == 1)
{
_httpProtocolVersion = "HTTP/1.1";
}
else if (Request.ProtocolVersion.Minor == 0)
{
_httpProtocolVersion = "HTTP/1.0";
}
}
_httpProtocolVersion = "HTTP/" + Request.ProtocolVersion.ToString(2);
} }
return _httpProtocolVersion; return _httpProtocolVersion;
} }

View File

@ -19,7 +19,7 @@ using System.Threading.Tasks;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
internal sealed class Request : IDisposable public sealed class Request
{ {
private RequestContext _requestContext; private RequestContext _requestContext;
private NativeRequestContext _nativeRequestContext; private NativeRequestContext _nativeRequestContext;
@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Server.WebListener
private IDictionary<string, string[]> _headers; private IDictionary<string, string[]> _headers;
private BoundaryType _contentBoundaryType; private BoundaryType _contentBoundaryType;
private long _contentLength; private long? _contentLength;
private Stream _nativeStream; private Stream _nativeStream;
private SocketAddress _localEndPoint; private SocketAddress _localEndPoint;
@ -202,32 +202,30 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
// TODO: Move this to the constructor, that's where it will be called. public long? ContentLength
internal long ContentLength64
{ {
get get
{ {
if (_contentBoundaryType == BoundaryType.None) if (_contentBoundaryType == BoundaryType.None)
{ {
string transferEncoding = Headers.Get(HttpKnownHeaderNames.TransferEncoding) ?? string.Empty; string transferEncoding = Headers.Get(HttpKnownHeaderNames.TransferEncoding) ?? string.Empty;
if ("chunked".Equals(transferEncoding.Trim(), StringComparison.OrdinalIgnoreCase)) if (string.Equals("chunked", transferEncoding.Trim(), StringComparison.OrdinalIgnoreCase))
{ {
_contentBoundaryType = BoundaryType.Chunked; _contentBoundaryType = BoundaryType.Chunked;
_contentLength = -1;
} }
else else
{ {
_contentLength = 0;
_contentBoundaryType = BoundaryType.ContentLength;
string length = Headers.Get(HttpKnownHeaderNames.ContentLength) ?? string.Empty; string length = Headers.Get(HttpKnownHeaderNames.ContentLength) ?? string.Empty;
if (length != null) long value;
if (long.TryParse(length.Trim(), NumberStyles.None,
CultureInfo.InvariantCulture.NumberFormat, out value))
{ {
if (!long.TryParse(length.Trim(), NumberStyles.None, _contentBoundaryType = BoundaryType.ContentLength;
CultureInfo.InvariantCulture.NumberFormat, out _contentLength)) _contentLength = value;
{ }
_contentLength = 0; else
_contentBoundaryType = BoundaryType.Invalid; {
} _contentBoundaryType = BoundaryType.Invalid;
} }
} }
} }
@ -252,7 +250,6 @@ namespace Microsoft.AspNet.Server.WebListener
{ {
if (_nativeStream == null) if (_nativeStream == null)
{ {
// TODO: Move this to the constructor (or a lazy Env dictionary)
_nativeStream = HasEntityBody ? new RequestStream(RequestContext) : Stream.Null; _nativeStream = HasEntityBody ? new RequestStream(RequestContext) : Stream.Null;
} }
return _nativeStream; return _nativeStream;
@ -281,7 +278,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
internal bool IsSecureConnection public bool IsSecureConnection
{ {
get get
{ {
@ -297,7 +294,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
*/ */
internal Version ProtocolVersion public Version ProtocolVersion
{ {
get get
{ {
@ -305,34 +302,13 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
public string Protocol public bool HasEntityBody
{ {
get get
{ {
if (_httpVersion.Major == 1) // accessing the ContentLength property delay creates _contentBoundaryType
{ return (ContentLength.HasValue && ContentLength.Value > 0 && _contentBoundaryType == BoundaryType.ContentLength)
if (_httpVersion.Minor == 1) || _contentBoundaryType == BoundaryType.Chunked;
{
return "HTTP/1.1";
}
else if (_httpVersion.Minor == 0)
{
return "HTTP/1.0";
}
}
return "HTTP/" + _httpVersion.ToString(2);
}
}
// TODO: Move this to the constructor
internal bool HasEntityBody
{
get
{
// accessing the ContentLength property delay creates m_BoundaryType
return (ContentLength64 > 0 && _contentBoundaryType == BoundaryType.ContentLength) ||
_contentBoundaryType == BoundaryType.Chunked || _contentBoundaryType == BoundaryType.Multipart;
} }
} }
@ -418,6 +394,14 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
public string ContentType
{
get
{
return Headers.Get(HttpKnownHeaderNames.ContentLength);
}
}
internal IPrincipal User internal IPrincipal User
{ {
get { return _user; } get { return _user; }
@ -443,6 +427,11 @@ namespace Microsoft.AspNet.Server.WebListener
#endif #endif
} }
internal UnsafeNclNativeMethods.HttpApi.HTTP_VERB GetKnownMethod()
{
return UnsafeNclNativeMethods.HttpApi.GetKnownVerb(RequestBuffer, OriginalBlobAddress);
}
#if NET45 #if NET45
// Populates the client certificate. The result may be null if there is no client cert. // 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 // TODO: Does it make sense for this to be invoked multiple times (e.g. renegotiate)? Client and server code appear to
@ -500,7 +489,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
// should only be called from RequestContext // should only be called from RequestContext
public void Dispose() internal void Dispose()
{ {
// TODO: Verbose log // TODO: Verbose log
_isDisposed = true; _isDisposed = true;

View File

@ -6,9 +6,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Logging; using Microsoft.AspNet.Logging;
@ -17,14 +17,11 @@ namespace Microsoft.AspNet.Server.WebListener
{ {
using OpaqueFunc = Func<IDictionary<string, object>, Task>; using OpaqueFunc = Func<IDictionary<string, object>, Task>;
internal sealed class RequestContext : IDisposable public sealed class RequestContext : IDisposable
{ {
private static readonly string[] ZeroContentLength = new[] { "0" };
private OwinWebListener _server; private OwinWebListener _server;
private Request _request; private Request _request;
private Response _response; private Response _response;
private CancellationTokenSource _cts;
private NativeRequestContext _memoryBlob; private NativeRequestContext _memoryBlob;
private OpaqueFunc _opaqueCallback; private OpaqueFunc _opaqueCallback;
private bool _disposed; private bool _disposed;
@ -38,11 +35,10 @@ 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);
_cts = new CancellationTokenSource();
_request.ReleasePins(); _request.ReleasePins();
} }
internal Request Request public Request Request
{ {
get get
{ {
@ -50,7 +46,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
internal Response Response public Response Response
{ {
get get
{ {
@ -58,7 +54,12 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
internal CancellationToken DisconnectToken public IPrincipal User
{
get { return _request.User; }
}
public CancellationToken DisconnectToken
{ {
get get
{ {
@ -146,32 +147,20 @@ namespace Microsoft.AspNet.Server.WebListener
finally finally
{ {
_request.Dispose(); _request.Dispose();
_cts.Dispose();
} }
} }
internal void Abort() public void Abort()
{ {
// May be called from Dispose() code path, don't check _disposed.
// TODO: Verbose log // TODO: Verbose log
_disposed = true; _disposed = true;
try if (_disconnectRegistration.HasValue)
{
_cts.Cancel();
}
catch (ObjectDisposedException)
{
}
catch (AggregateException)
{ {
_disconnectRegistration.Value.Dispose();
} }
ForceCancelRequest(RequestQueueHandle, _request.RequestId); ForceCancelRequest(RequestQueueHandle, _request.RequestId);
_request.Dispose(); _request.Dispose();
_cts.Dispose();
}
internal UnsafeNclNativeMethods.HttpApi.HTTP_VERB GetKnownMethod()
{
return UnsafeNclNativeMethods.HttpApi.GetKnownVerb(Request.RequestBuffer, Request.OriginalBlobAddress);
} }
// This is only called while processing incoming requests. We don't have to worry about cancelling // This is only called while processing incoming requests. We don't have to worry about cancelling
@ -256,13 +245,5 @@ namespace Microsoft.AspNet.Server.WebListener
return opaqueEnv; return opaqueEnv;
} }
internal void SetFatalResponse()
{
Response.StatusCode = 500;
Response.ReasonPhrase = string.Empty;
Response.Headers.Clear();
Response.Headers.Add(HttpKnownHeaderNames.ContentLength, ZeroContentLength);
}
} }
} }

View File

@ -8,6 +8,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
@ -15,8 +16,10 @@ using System.Threading.Tasks;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
internal sealed unsafe class Response : IDisposable public sealed unsafe class Response
{ {
private static readonly string[] ZeroContentLength = new[] { "0" };
private ResponseState _responseState; private ResponseState _responseState;
private IDictionary<string, string[]> _headers; private IDictionary<string, string[]> _headers;
private string _reasonPhrase; private string _reasonPhrase;
@ -99,7 +102,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
internal ResponseStream Body public Stream Body
{ {
get get
{ {
@ -136,14 +139,6 @@ namespace Microsoft.AspNet.Server.WebListener
return true; return true;
} }
internal EntitySendFormat EntitySendFormat
{
get
{
return (EntitySendFormat)_boundaryType;
}
}
public IDictionary<string, string[]> Headers public IDictionary<string, string[]> Headers
{ {
get { return _headers; } get { return _headers; }
@ -157,7 +152,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
internal long ContentLength64 internal long CalculatedLength
{ {
get get
{ {
@ -165,6 +160,73 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
// Header accessors
public long? ContentLength
{
get
{
string contentLengthString = Headers.Get(HttpKnownHeaderNames.ContentLength);
long contentLength;
if (!string.IsNullOrWhiteSpace(contentLengthString))
{
contentLengthString = contentLengthString.Trim();
if (string.Equals("0", contentLengthString, StringComparison.Ordinal))
{
return 0;
}
else if (long.TryParse(contentLengthString, NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out contentLength))
{
return contentLength;
}
}
return null;
}
set
{
CheckResponseStarted();
if (!value.HasValue)
{
Headers.Remove(HttpKnownHeaderNames.ContentLength);
}
else
{
if (value.Value < 0)
{
throw new ArgumentOutOfRangeException("value", value.Value, "Cannot be negative.");
}
if (value.Value == 0)
{
Headers[HttpKnownHeaderNames.ContentLength] = ZeroContentLength;
}
else
{
Headers[HttpKnownHeaderNames.ContentLength] = new[] { value.Value.ToString(CultureInfo.InvariantCulture) };
}
}
}
}
public string ContentType
{
get
{
return Headers.Get(HttpKnownHeaderNames.ContentLength);
}
set
{
CheckResponseStarted();
if (string.IsNullOrEmpty(value))
{
Headers.Remove(HttpKnownHeaderNames.ContentType);
}
else
{
Headers[HttpKnownHeaderNames.ContentType] = new[] { value };
}
}
}
private Version GetProtocolVersion() private Version GetProtocolVersion()
{ {
/* /*
@ -203,24 +265,17 @@ namespace Microsoft.AspNet.Server.WebListener
return Request.ProtocolVersion; return Request.ProtocolVersion;
} }
public void Dispose() // should only be called from RequestContext
internal void Dispose()
{ {
Dispose(true); if (_responseState >= ResponseState.Closed)
}
private void Dispose(bool disposing)
{
if (disposing)
{ {
if (_responseState >= ResponseState.Closed) return;
{
return;
}
// TODO: Verbose log
EnsureResponseStream();
_nativeStream.Dispose();
_responseState = ResponseState.Closed;
} }
// TODO: Verbose log
EnsureResponseStream();
_nativeStream.Dispose();
_responseState = ResponseState.Closed;
} }
// old API, now private, and helper methods // old API, now private, and helper methods
@ -434,7 +489,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
// Content-Length takes priority // Content-Length takes priority
string contentLengthString = Headers.Get(HttpKnownHeaderNames.ContentLength); long? contentLength = ContentLength;
string transferEncodingString = Headers.Get(HttpKnownHeaderNames.TransferEncoding); string transferEncodingString = Headers.Get(HttpKnownHeaderNames.TransferEncoding);
if (responseVersion == Constants.V1_0 && !string.IsNullOrEmpty(transferEncodingString) if (responseVersion == Constants.V1_0 && !string.IsNullOrEmpty(transferEncodingString)
@ -445,27 +500,14 @@ namespace Microsoft.AspNet.Server.WebListener
transferEncodingString = null; transferEncodingString = null;
} }
if (!string.IsNullOrWhiteSpace(contentLengthString)) if (contentLength.HasValue)
{ {
contentLengthString = contentLengthString.Trim(); _contentLength = contentLength.Value;
if (string.Equals("0", contentLengthString, StringComparison.Ordinal)) _boundaryType = BoundaryType.ContentLength;
if (_contentLength == 0)
{ {
_boundaryType = BoundaryType.ContentLength;
_contentLength = 0;
flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE;
} }
else if (long.TryParse(contentLengthString, NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out _contentLength))
{
_boundaryType = BoundaryType.ContentLength;
if (_contentLength == 0)
{
flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE;
}
}
else
{
_boundaryType = BoundaryType.Invalid;
}
} }
else if (!string.IsNullOrWhiteSpace(transferEncodingString) else if (!string.IsNullOrWhiteSpace(transferEncodingString)
&& string.Equals("chunked", transferEncodingString.Trim(), StringComparison.OrdinalIgnoreCase)) && string.Equals("chunked", transferEncodingString.Trim(), StringComparison.OrdinalIgnoreCase))
@ -509,7 +551,6 @@ namespace Microsoft.AspNet.Server.WebListener
} }
// Also, Keep-Alive vs Connection Close // Also, Keep-Alive vs Connection Close
if (!keepAlive) if (!keepAlive)
{ {
if (!closeSet) if (!closeSet)

View File

@ -195,14 +195,14 @@ namespace Microsoft.AspNet.Server.WebListener
} }
if (_leftToWrite == long.MinValue) if (_leftToWrite == long.MinValue)
{ {
UnsafeNclNativeMethods.HttpApi.HTTP_VERB method = _requestContext.GetKnownMethod(); UnsafeNclNativeMethods.HttpApi.HTTP_VERB method = _requestContext.Request.GetKnownMethod();
if (method == UnsafeNclNativeMethods.HttpApi.HTTP_VERB.HttpVerbHEAD) if (method == UnsafeNclNativeMethods.HttpApi.HTTP_VERB.HttpVerbHEAD)
{ {
_leftToWrite = 0; _leftToWrite = 0;
} }
else if (_requestContext.Response.EntitySendFormat == EntitySendFormat.ContentLength) else if (_requestContext.Response.BoundaryType == BoundaryType.ContentLength)
{ {
_leftToWrite = _requestContext.Response.ContentLength64; _leftToWrite = _requestContext.Response.CalculatedLength;
} }
else else
{ {

View File

@ -1,19 +1,18 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <copyright file="HttpListenerException.cs" company="Microsoft"> // <copyright company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved. // Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright> // </copyright>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
#if NET45 [SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable")] public class WebListenerException : Win32Exception
#endif
internal class WebListenerException : Win32Exception
{ {
internal WebListenerException() internal WebListenerException()
: base(Marshal.GetLastWin32Error()) : base(Marshal.GetLastWin32Error())
@ -29,12 +28,14 @@ namespace Microsoft.AspNet.Server.WebListener
: base(errorCode, message) : base(errorCode, message)
{ {
} }
#if NET45
// the base class returns the HResult with this property
// we need the Win32 Error Code, hence the override.
public override int ErrorCode public override int ErrorCode
#else
public int ErrorCode
#endif
{ {
// the base class returns the HResult with this property
// we need the Win32 Error Code, hence the override.
get get
{ {
return NativeErrorCode; return NativeErrorCode;

View File

@ -139,7 +139,7 @@ namespace Microsoft.AspNet.Server.WebListener
else else
{ {
// We haven't sent a response yet, try to send a 500 Internal Server Error // We haven't sent a response yet, try to send a 500 Internal Server Error
requestContext.SetFatalResponse(); SetFatalResponse(requestContext);
} }
} }
requestContext.Dispose(); requestContext.Dispose();
@ -148,10 +148,17 @@ namespace Microsoft.AspNet.Server.WebListener
{ {
LogHelper.LogException(_logger, "ProcessRequestAsync", ex); LogHelper.LogException(_logger, "ProcessRequestAsync", ex);
requestContext.Abort(); requestContext.Abort();
requestContext.Dispose();
} }
} }
private static void SetFatalResponse(RequestContext context)
{
context.Response.StatusCode = 500;
context.Response.ReasonPhrase = string.Empty;
context.Response.Headers.Clear();
context.Response.ContentLength = 0;
}
public void Dispose() public void Dispose()
{ {
_listener.Dispose(); _listener.Dispose();

View File

@ -1,112 +0,0 @@
//------------------------------------------------------------------------------
// <copyright file="HttpListenerException.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
#if !NET45
using System.Runtime.InteropServices;
using System.Text;
namespace System.ComponentModel
{
internal class Win32Exception : ExternalException
{
/// <devdoc>
/// <para>Represents the Win32 error code associated with this exception. This
/// field is read-only.</para>
/// </devdoc>
private readonly int nativeErrorCode;
/// <devdoc>
/// <para>Initializes a new instance of the <see cref='System.ComponentModel.Win32Exception'/> class with the last Win32 error
/// that occured.</para>
/// </devdoc>
public Win32Exception()
: this(Marshal.GetLastWin32Error())
{
}
/// <devdoc>
/// <para>Initializes a new instance of the <see cref='System.ComponentModel.Win32Exception'/> class with the specified error.</para>
/// </devdoc>
public Win32Exception(int error)
: this(error, GetErrorMessage(error))
{
}
/// <devdoc>
/// <para>Initializes a new instance of the <see cref='System.ComponentModel.Win32Exception'/> class with the specified error and the
/// specified detailed description.</para>
/// </devdoc>
public Win32Exception(int error, string message)
: base(message)
{
nativeErrorCode = error;
}
/// <devdoc>
/// Initializes a new instance of the Exception class with a specified error message.
/// FxCop CA1032: Multiple constructors are required to correctly implement a custom exception.
/// </devdoc>
public Win32Exception(string message)
: this(Marshal.GetLastWin32Error(), message)
{
}
/// <devdoc>
/// Initializes a new instance of the Exception class with a specified error message and a
/// reference to the inner exception that is the cause of this exception.
/// FxCop CA1032: Multiple constructors are required to correctly implement a custom exception.
/// </devdoc>
public Win32Exception(string message, Exception innerException)
: base(message, innerException)
{
nativeErrorCode = Marshal.GetLastWin32Error();
}
/// <devdoc>
/// <para>Represents the Win32 error code associated with this exception. This
/// field is read-only.</para>
/// </devdoc>
public int NativeErrorCode
{
get
{
return nativeErrorCode;
}
}
private static string GetErrorMessage(int error)
{
//get the system error message...
string errorMsg = "";
StringBuilder sb = new StringBuilder(256);
int result = SafeNativeMethods.FormatMessage(
SafeNativeMethods.FORMAT_MESSAGE_IGNORE_INSERTS |
SafeNativeMethods.FORMAT_MESSAGE_FROM_SYSTEM |
SafeNativeMethods.FORMAT_MESSAGE_ARGUMENT_ARRAY,
IntPtr.Zero, (uint)error, 0, sb, sb.Capacity + 1,
null);
if (result != 0)
{
int i = sb.Length;
while (i > 0)
{
char ch = sb[i - 1];
if (ch > 32 && ch != '.') break;
i--;
}
errorMsg = sb.ToString(0, i);
}
else
{
errorMsg = "Unknown error (0x" + Convert.ToString(error, 16) + ")";
}
return errorMsg;
}
}
}
#endif

View File

@ -37,7 +37,8 @@
"System.Threading": "4.0.0.0", "System.Threading": "4.0.0.0",
"System.Threading.Overlapped": "4.0.0.0", "System.Threading.Overlapped": "4.0.0.0",
"System.Threading.Tasks": "4.0.10.0", "System.Threading.Tasks": "4.0.10.0",
"System.Threading.ThreadPool": "4.0.10.0" "System.Threading.ThreadPool": "4.0.10.0",
"Microsoft.Win32.Primitives": "4.0.0.0"
} }
} }
} }