diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs index 823ba518c..252451618 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Threading; using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.AspNet.Server.Kestrel.Networking; using Microsoft.Framework.Logging; @@ -13,6 +14,8 @@ public class Connection : ConnectionContext, IConnectionControl private static readonly Action _readCallback = ReadCallback; private static readonly Func _allocCallback = AllocCallback; + private static long _lastConnectionId; + private readonly UvStreamHandle _socket; private Frame _frame; private long _connectionId = 0; @@ -24,6 +27,8 @@ public Connection(ListenerContext context, UvStreamHandle socket) : base(context { _socket = socket; ConnectionControl = this; + + _connectionId = Interlocked.Increment(ref _lastConnectionId); } public void Start() @@ -31,7 +36,7 @@ public void Start() Log.ConnectionStart(_connectionId); SocketInput = new SocketInput(Memory); - SocketOutput = new SocketOutput(Thread, _socket, Log); + SocketOutput = new SocketOutput(Thread, _socket, _connectionId, Log); _frame = new Frame(this); _socket.ReadStart(_allocCallback, _readCallback, this); } @@ -67,7 +72,6 @@ private void OnRead(UvStreamHandle handle, int status, Exception error) } else if (normalDone || errorDone) { - Log.ConnectionReadFin(_connectionId); SocketInput.RemoteIntakeFin = true; _socket.ReadStop(); @@ -75,6 +79,10 @@ private void OnRead(UvStreamHandle handle, int status, Exception error) { Log.LogError("Connection.OnRead", error); } + else + { + Log.ConnectionReadFin(_connectionId); + } } @@ -113,19 +121,19 @@ void IConnectionControl.End(ProduceEndType endType) } _connectionState = ConnectionState.Shutdown; - Log.ConnectionWriteFin(_connectionId, 0); + Log.ConnectionWriteFin(_connectionId); Thread.Post( state => { - Log.ConnectionWriteFin(_connectionId, 1); var self = (Connection)state; - var shutdown = new UvShutdownReq(Log); + var shutdown = new UvShutdownReq(self.Log); shutdown.Init(self.Thread.Loop); - shutdown.Shutdown(self._socket, (req, status, _) => + shutdown.Shutdown(self._socket, (req, status, state2) => { - Log.ConnectionWriteFin(_connectionId, 1); + var self2 = (Connection)state2; + self2.Log.ConnectionWroteFin(_connectionId, status); req.Dispose(); - }, null); + }, this); }, this); break; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 821e3788c..da75dc1d6 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -221,9 +221,10 @@ private void FireOnCompleted() private async Task ExecuteAsync() { Exception error = null; + IDisposable disposable = null; try { - await Application.Invoke(this).ConfigureAwait(false); + disposable = await Application.Invoke(this).ConfigureAwait(false); // Trigger FireOnStarting if ProduceStart hasn't been called yet. // We call it here, so it can go through our normal error handling @@ -241,6 +242,7 @@ private async Task ExecuteAsync() { FireOnCompleted(); ProduceEnd(error); + disposable?.Dispose(); } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs index 5b87d873a..603a9e544 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs @@ -24,7 +24,7 @@ public Task StartAsync( string host, int port, KestrelThread thread, - Func application) + Func> application) { Thread = thread; Application = application; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs index c0f042b4c..c8a6673cf 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs @@ -27,7 +27,7 @@ public ListenerContext(ListenerContext listenerContext) public KestrelThread Thread { get; set; } - public Func Application { get; set; } + public Func> Application { get; set; } public IMemoryPool Memory { get; set; } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs index 0010c2d60..f5ac736a8 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs @@ -34,7 +34,7 @@ public async Task StartAsync( string host, int port, KestrelThread thread, - Func application) + Func> application) { await StartAsync(scheme, host, port, thread, application).ConfigureAwait(false); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs index 3a0d2e667..e2081c599 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs @@ -25,7 +25,7 @@ protected ListenerSecondary(ServiceContext serviceContext) : base(serviceContext public Task StartAsync( string pipeName, KestrelThread thread, - Func application) + Func> application) { Thread = thread; Application = application; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index af490063f..3c836df3c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -17,6 +17,7 @@ public class SocketOutput : ISocketOutput private readonly KestrelThread _thread; private readonly UvStreamHandle _socket; + private readonly long _connectionId; private readonly IKestrelTrace _log; // This locks access to to all of the below fields @@ -31,10 +32,11 @@ public class SocketOutput : ISocketOutput private WriteContext _nextWriteContext; private readonly Queue _callbacksPending; - public SocketOutput(KestrelThread thread, UvStreamHandle socket, IKestrelTrace log) + public SocketOutput(KestrelThread thread, UvStreamHandle socket, long connectionId, IKestrelTrace log) { _thread = thread; _socket = socket; + _connectionId = connectionId; _log = log; _callbacksPending = new Queue(); } @@ -46,7 +48,7 @@ public void Write(ArraySegment buffer, Action callback, Array.Copy(buffer.Array, buffer.Offset, copy, 0, buffer.Count); buffer = new ArraySegment(copy); - _log.ConnectionWrite(0, buffer.Count); + _log.ConnectionWrite(_connectionId, buffer.Count); bool triggerCallbackNow = false; @@ -155,7 +157,7 @@ private void WriteAllPending() // This is called on the libuv event loop private void OnWriteCompleted(Queue> writtenBuffers, UvWriteReq req, int status, Exception error) { - _log.ConnectionWriteCallback(0, status); + _log.ConnectionWriteCallback(_connectionId, status); lock (_lockObj) { diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/IKestrelTrace.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/IKestrelTrace.cs index 478b573fe..cb825bac3 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/IKestrelTrace.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/IKestrelTrace.cs @@ -16,7 +16,9 @@ public interface IKestrelTrace : ILogger void ConnectionReadFin(long connectionId); - void ConnectionWriteFin(long connectionId, int step); + void ConnectionWriteFin(long connectionId); + + void ConnectionWroteFin(long connectionId, int status); void ConnectionKeepAlive(long connectionId); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelTrace.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelTrace.cs index c9ce6bb82..5fba4c7f0 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelTrace.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelTrace.cs @@ -21,57 +21,65 @@ public KestrelTrace(ILogger logger) public void ConnectionStart(long connectionId) { - _logger.LogDebug(13, $"{nameof(ConnectionStart)} -> Id: {connectionId}"); + _logger.LogDebug(1, @"Connection id ""{ConnectionId}"" started.", connectionId); } public void ConnectionStop(long connectionId) { - _logger.LogDebug(14, $"{nameof(ConnectionStop)} -> Id: {connectionId}"); + _logger.LogDebug(2, @"Connection id ""{ConnectionId}"" stopped.", connectionId); } - public void ConnectionRead(long connectionId, int status) + public void ConnectionRead(long connectionId, int count) { - _logger.LogDebug(4, $"{nameof(ConnectionRead)} -> Id: {connectionId}, Status: {status}"); + // Don't log for now since this could be *too* verbose. + // Reserved: Event ID 3 } public void ConnectionPause(long connectionId) { - _logger.LogDebug(5, $"{nameof(ConnectionPause)} -> Id: {connectionId}"); + _logger.LogDebug(4, @"Connection id ""{ConnectionId}"" paused.", connectionId); } public void ConnectionResume(long connectionId) { - _logger.LogDebug(6, $"{nameof(ConnectionResume)} -> Id: {connectionId}"); + _logger.LogDebug(5, @"Connection id ""{ConnectionId}"" resumed.", connectionId); } public void ConnectionReadFin(long connectionId) { - _logger.LogDebug(7, $"{nameof(ConnectionReadFin)} -> Id: {connectionId}"); + _logger.LogDebug(6, @"Connection id ""{ConnectionId}"" received FIN.", connectionId); } - public void ConnectionWriteFin(long connectionId, int step) + public void ConnectionWriteFin(long connectionId) { - _logger.LogDebug(8, $"{nameof(ConnectionWriteFin)} -> Id: {connectionId}, Step: {step}"); + _logger.LogDebug(7, @"Connection id ""{ConnectionId}"" sending FIN.", connectionId); + } + + public void ConnectionWroteFin(long connectionId, int status) + { + _logger.LogDebug(8, @"Connection id ""{ConnectionId}"" sent FIN with status ""{Status}"".", connectionId, status); } public void ConnectionKeepAlive(long connectionId) { - _logger.LogDebug(9, $"{nameof(ConnectionKeepAlive)} -> Id: {connectionId}"); + _logger.LogDebug(9, @"Connection id ""{ConnectionId}"" completed keep alive response.", connectionId); } public void ConnectionDisconnect(long connectionId) { - _logger.LogDebug(10, $"{nameof(ConnectionDisconnect)} -> Id: {connectionId}"); + _logger.LogDebug(10, @"Connection id ""{ConnectionId}"" disconnected.", connectionId); } public void ConnectionWrite(long connectionId, int count) { - _logger.LogDebug(11, $"{nameof(ConnectionWrite)} -> Id: {connectionId}, Count: {count}"); + // Don't log for now since this could be *too* verbose. + // Reserved: Event ID 11 } public void ConnectionWriteCallback(long connectionId, int status) { - _logger.LogDebug(12, $"{nameof(ConnectionWriteCallback)} -> Id: {connectionId}, Status: {status}"); + // Don't log for now since this could be *too* verbose. + // Reserved: Event ID 12 } public void Log(LogLevel logLevel, int eventId, object state, Exception exception, Func formatter) diff --git a/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs b/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs index 893bdf94b..1a99b5581 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs @@ -106,7 +106,7 @@ public void Dispose() Threads.Clear(); } - public IDisposable CreateServer(string scheme, string host, int port, Func application) + public IDisposable CreateServer(string scheme, string host, int port, Func> application) { var listeners = new List(); var usingPipes = host.StartsWith(Constants.UnixPipeHostPrefix); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Properties/AssemblyInfo.cs b/src/Microsoft.AspNet.Server.Kestrel/Properties/AssemblyInfo.cs index 573f5c363..7799e552b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Properties/AssemblyInfo.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Properties/AssemblyInfo.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; +using System.Resources; using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.AspNet.Server.KestrelTests")] -[assembly: AssemblyMetadata("Serviceable", "True")] \ No newline at end of file +[assembly: AssemblyMetadata("Serviceable", "True")] +[assembly: NeutralResourcesLanguage("en-us")] diff --git a/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs b/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs index d1025b13b..8123349be 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs @@ -37,7 +37,7 @@ public IFeatureCollection Initialize(IConfiguration configuration) return serverFeatures; } - public IDisposable Start(IFeatureCollection serverFeatures, Func application) + public IDisposable Start(IFeatureCollection serverFeatures, Func> application) { var disposables = new Stack(); var disposer = new Disposable(() => @@ -73,7 +73,8 @@ public IDisposable Start(IFeatureCollection serverFeatures, Func { var request = new ServerRequest(frame); - await application.Invoke(request.Features).ConfigureAwait(false); + return await application.Invoke(request.Features).ConfigureAwait(false); + })); } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/ChunkedResponseTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/ChunkedResponseTests.cs index 162a29263..7e2c85b7a 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/ChunkedResponseTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/ChunkedResponseTests.cs @@ -18,6 +18,7 @@ public async Task ResponsesAreChunkedAutomatically() frame.ResponseHeaders.Clear(); await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6); await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6); + return null; })) { using (var connection = new TestConnection()) @@ -50,6 +51,7 @@ public async Task ZeroLengthWritesAreIgnored() await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6); await frame.ResponseBody.WriteAsync(new byte[0], 0, 0); await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6); + return null; })) { using (var connection = new TestConnection()) @@ -80,6 +82,7 @@ public async Task EmptyResponseBodyHandledCorrectlyWithZeroLengthWrite() { frame.ResponseHeaders.Clear(); await frame.ResponseBody.WriteAsync(new byte[0], 0, 0); + return null; })) { using (var connection = new TestConnection()) diff --git a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs index 0a49cb3f8..0b0de085a 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs @@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Server.KestrelTests /// public class EngineTests { - private async Task App(Frame frame) + private async Task App(Frame frame) { frame.ResponseHeaders.Clear(); for (; ;) @@ -33,6 +33,7 @@ private async Task App(Frame frame) } await frame.ResponseBody.WriteAsync(buffer, 0, count); } + return null; } ILibraryManager LibraryManager @@ -58,7 +59,7 @@ ILibraryManager LibraryManager } } - private async Task AppChunked(Frame frame) + private async Task AppChunked(Frame frame) { frame.ResponseHeaders.Clear(); var data = new MemoryStream(); @@ -75,12 +76,13 @@ private async Task AppChunked(Frame frame) var bytes = data.ToArray(); frame.ResponseHeaders["Content-Length"] = new[] { bytes.Length.ToString() }; await frame.ResponseBody.WriteAsync(bytes, 0, bytes.Length); + return null; } - private Task EmptyApp(Frame frame) + private Task EmptyApp(Frame frame) { frame.ResponseHeaders.Clear(); - return Task.FromResult(null); + return Task.FromResult(null); } [Fact] @@ -472,6 +474,8 @@ public async Task ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes() var statusString = await reader.ReadLineAsync(); frame.StatusCode = int.Parse(statusString); } + + return null; })) { using (var connection = new TestConnection()) @@ -715,7 +719,7 @@ public async Task ThrowingInOnStartingResultsIn500Response() // If we write to the response stream, we will not get a 500. - return Task.FromResult(null); + return Task.FromResult(null); })) { using (var connection = new TestConnection()) @@ -770,6 +774,8 @@ public async Task ThrowingInOnStartingResultsInFailedWrites() // The second write should succeed since the OnStarting callback will not be called again await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("Exception!!"), 0, 11); + + return null; })) { using (var connection = new TestConnection()) diff --git a/test/Microsoft.AspNet.Server.KestrelTests/Microsoft.AspNet.Server.KestrelTests.xproj b/test/Microsoft.AspNet.Server.KestrelTests/Microsoft.AspNet.Server.KestrelTests.xproj index 591969973..59c2109f9 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/Microsoft.AspNet.Server.KestrelTests.xproj +++ b/test/Microsoft.AspNet.Server.KestrelTests/Microsoft.AspNet.Server.KestrelTests.xproj @@ -13,5 +13,8 @@ 2.0 + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Server.KestrelTests/Program.cs b/test/Microsoft.AspNet.Server.KestrelTests/Program.cs index 1baf869b8..858790c68 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/Program.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/Program.cs @@ -13,16 +13,25 @@ public class Program { private readonly IApplicationEnvironment env; private readonly IServiceProvider sp; + private readonly ILibraryManager _libraryManager; + private readonly IApplicationShutdown _shutdown; - public Program(IApplicationEnvironment env, IServiceProvider sp) + public Program( + IApplicationEnvironment env, + IServiceProvider sp, + ILibraryManager libraryManager, + IApplicationShutdown shutown) { this.env = env; this.sp = sp; + _libraryManager = libraryManager; + _shutdown = shutown; } public int Main() { - return new Xunit.Runner.AspNet.Program(env, sp).Main(new string[] { + return new Xunit.Runner.Dnx.Program(env, sp, _libraryManager, _shutdown).Main(new string[] + { "-class", typeof(MultipleLoopTests).FullName }); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs index 7577fa25d..6253b0651 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs @@ -39,7 +39,7 @@ public void CanWrite1MB() var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new KestrelTrace(new TestLogger())); var trace = new KestrelTrace(new TestLogger()); - var socketOutput = new SocketOutput(kestrelThread, socket, trace); + var socketOutput = new SocketOutput(kestrelThread, socket, 0, trace); // I doubt _maxBytesPreCompleted will ever be over a MB. If it is, we should change this test. var bufferSize = 1048576; @@ -84,7 +84,7 @@ public void WritesDontCompleteImmediatelyWhenTooManyBytesAreAlreadyPreCompleted( var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new KestrelTrace(new TestLogger())); var trace = new KestrelTrace(new TestLogger()); - var socketOutput = new SocketOutput(kestrelThread, socket, trace); + var socketOutput = new SocketOutput(kestrelThread, socket, 0, trace); var bufferSize = maxBytesPreCompleted; var buffer = new ArraySegment(new byte[bufferSize], 0, bufferSize); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs b/test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs index 3ada706a9..8f3359b8f 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs @@ -18,7 +18,7 @@ public class TestServer : IDisposable private KestrelEngine _engine; private IDisposable _server; - public TestServer(Func app) + public TestServer(Func> app) { Create(app); } @@ -43,7 +43,7 @@ ILibraryManager LibraryManager } } - public void Create(Func app) + public void Create(Func> app) { _engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented(), new TestLogger()); _engine.Start(1);