public void EngineCanStartAndStop(ServiceContext testContext) { var engine = new KestrelEngine(testContext); engine.Start(1); engine.Dispose(); }
public async Task EngineCanStartAndStop() { var engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented()); engine.Start(1); engine.Dispose(); }
public void EngineCanStartAndStop() { var engine = new KestrelEngine(LibraryManager, new TestServiceContext()); engine.Start(1); engine.Dispose(); }
public void DoesNotEndConnectionOnZeroRead() { var mockLibuv = new MockLibuv(); using (var memory = new MemoryPool()) using (var engine = new KestrelEngine(mockLibuv, new TestServiceContext())) { engine.Start(count: 1); var trace = new TestKestrelTrace(); var context = new ListenerContext(new TestServiceContext()) { FrameFactory = connectionContext => new Frame <HttpContext>( new DummyApplication(httpContext => TaskUtilities.CompletedTask), connectionContext), Memory = memory, ServerAddress = ServerAddress.FromUrl($"http://localhost:{TestServer.GetNextPort()}"), Thread = engine.Threads[0] }; var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, trace); var connection = new Connection(context, socket); connection.Start(); Libuv.uv_buf_t ignored; mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out ignored); mockLibuv.ReadCallback(socket.InternalGetHandle(), 0, ref ignored); Assert.False(connection.SocketInput.RemoteIntakeFin); connection.ConnectionControl.End(ProduceEndType.SocketDisconnect); } }
public void ConnectionCanReadAndWrite(TestServiceContext testContext) { var port = TestServer.GetNextPort(); testContext.App = App; var engine = new KestrelEngine(testContext); engine.Start(1); var address = ServerAddress.FromUrl($"http://localhost:{port}/"); var started = engine.CreateServer(address); var socket = TestConnection.CreateConnectedLoopbackSocket(port); socket.Send(Encoding.ASCII.GetBytes("POST / HTTP/1.0\r\n\r\nHello World")); socket.Shutdown(SocketShutdown.Send); var buffer = new byte[8192]; while (true) { var length = socket.Receive(buffer); if (length == 0) { break; } var text = Encoding.ASCII.GetString(buffer, 0, length); } started.Dispose(); engine.Dispose(); }
public void ConnectionCanReadAndWrite(TestServiceContext testContext) { testContext.App = App; var engine = new KestrelEngine(testContext); engine.Start(1); var address = ServerAddress.FromUrl("http://localhost:54321/"); var started = engine.CreateServer(address); Console.WriteLine("Started"); var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Connect(new IPEndPoint(IPAddress.Loopback, 54321)); socket.Send(Encoding.ASCII.GetBytes("POST / HTTP/1.0\r\n\r\nHello World")); socket.Shutdown(SocketShutdown.Send); var buffer = new byte[8192]; while (true) { var length = socket.Receive(buffer); if (length == 0) { break; } var text = Encoding.ASCII.GetString(buffer, 0, length); } started.Dispose(); engine.Dispose(); }
public async Task EngineCanStartAndStop() { var engine = new KestrelEngine(LibraryManager); engine.Start(1); engine.Dispose(); }
public IDisposable Start(IServerInformation serverInformation, Func <IFeatureCollection, Task> application) { var disposables = new List <IDisposable>(); var information = (ServerInformation)serverInformation; var engine = new KestrelEngine(_libraryManager, _appShutdownService); engine.Start(1); foreach (var address in information.Addresses) { disposables.Add(engine.CreateServer( address.Scheme, address.Host, address.Port, async frame => { var request = new ServerRequest(frame); await application.Invoke(request.Features); })); } disposables.Add(engine); return(new Disposable(() => { foreach (var disposable in disposables) { disposable.Dispose(); } })); }
public void ProducingStartAndProducingCompleteCanBeCalledAfterConnectionClose() { var mockLibuv = new MockLibuv(); using (var memory = new MemoryPool()) using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var connection = new MockConnection(); var socketOutput = new SocketOutput(kestrelThread, socket, memory, connection, "0", trace, ltp, new Queue <UvWriteReq>()); // Close SocketOutput var cleanupTask = socketOutput.WriteAsync( default(ArraySegment <byte>), default(CancellationToken), socketDisconnect: true); Assert.True(connection.SocketClosed.Wait(1000)); var start = socketOutput.ProducingStart(); Assert.True(start.IsDefault); // ProducingComplete should not throw given a default iterator socketOutput.ProducingComplete(start); } }
public async Task ConnectionCanReadAndWrite() { var engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented()); engine.Start(1); var started = engine.CreateServer("http", "localhost", 54321, App); Console.WriteLine("Started"); var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Connect(new IPEndPoint(IPAddress.Loopback, 54321)); socket.Send(Encoding.ASCII.GetBytes("POST / HTTP/1.0\r\n\r\nHello World")); socket.Shutdown(SocketShutdown.Send); var buffer = new byte[8192]; for (; ;) { var length = socket.Receive(buffer); if (length == 0) { break; } var text = Encoding.ASCII.GetString(buffer, 0, length); } started.Dispose(); engine.Dispose(); }
public void Create(Func <IFeatureCollection, Task> app, ServiceContext context, string serverAddress) { _engine = new KestrelEngine(context); _engine.Start(1); _server = _engine.CreateServer( ServerAddress.FromUrl(serverAddress), app); }
public void OnlyAllowsUpToThreeConcurrentWrites() { var writeWh = new ManualResetEventSlim(); var completeQueue = new Queue <Action <int> >(); var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { writeWh.Set(); completeQueue.Enqueue(triggerCompleted); return(0); } }; using (var memory = new MemoryPool()) using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, new MockConnection(), "0", trace, ltp, new Queue <UvWriteReq>()); var buffer = new ArraySegment <byte>(new byte[1]); // First three writes trigger uv_write socketOutput.WriteAsync(buffer, CancellationToken.None); Assert.True(writeWh.Wait(1000)); writeWh.Reset(); socketOutput.WriteAsync(buffer, CancellationToken.None); Assert.True(writeWh.Wait(1000)); writeWh.Reset(); socketOutput.WriteAsync(buffer, CancellationToken.None); Assert.True(writeWh.Wait(1000)); writeWh.Reset(); // The fourth write won't trigger uv_write since the first three haven't completed socketOutput.WriteAsync(buffer, CancellationToken.None); Assert.False(writeWh.Wait(1000)); // Complete 1st write allowing uv_write to be triggered again completeQueue.Dequeue()(0); Assert.True(writeWh.Wait(1000)); // Cleanup var cleanupTask = socketOutput.WriteAsync( default(ArraySegment <byte>), default(CancellationToken), socketDisconnect: true); foreach (var triggerCompleted in completeQueue) { triggerCompleted(0); } } }
public void WritesAreAggregated() { var writeWh = new ManualResetEventSlim(); var writeCount = 0; var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { writeCount++; triggerCompleted(0); writeWh.Set(); return(0); } }; using (var memory = new MemoryPool()) using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, new MockConnection(), "0", trace, ltp, new Queue <UvWriteReq>()); var blockThreadWh = new ManualResetEventSlim(); kestrelThread.Post(_ => { blockThreadWh.Wait(); }, state: null); var buffer = new ArraySegment <byte>(new byte[1]); // Two calls to WriteAsync trigger uv_write once if both calls // are made before write is scheduled socketOutput.WriteAsync(buffer, CancellationToken.None); socketOutput.WriteAsync(buffer, CancellationToken.None); blockThreadWh.Set(); Assert.True(writeWh.Wait(1000)); writeWh.Reset(); // Write isn't called twice after the thread is unblocked Assert.False(writeWh.Wait(1000)); Assert.Equal(1, writeCount); // One call to ScheduleWrite + One call to Post to block the thread Assert.Equal(2, mockLibuv.PostCount); // Cleanup var cleanupTask = socketOutput.WriteAsync( default(ArraySegment <byte>), default(CancellationToken), socketDisconnect: true); } }
public void Create(Func <Frame, Task> app) { _engine = new KestrelEngine(LibraryManager); _engine.Start(1); _server = _engine.CreateServer( "http", "localhost", 54321, app); }
public async Task ListenerCanCreateAndDispose() { var engine = new KestrelEngine(LibraryManager, new ShutdownNotImplemented()); engine.Start(1); var started = engine.CreateServer("http", "localhost", 54321, App); started.Dispose(); engine.Dispose(); }
public void ListenerCanCreateAndDispose() { var engine = new KestrelEngine(LibraryManager, new TestServiceContext()); engine.Start(1); var started = engine.CreateServer("http", "localhost", 54321, App); started.Dispose(); engine.Dispose(); }
public void WritesDontCompleteImmediatelyWhenTooManyBytesAreAlreadyPreCompleted() { // This should match _maxBytesPreCompleted in SocketOutput var maxBytesPreCompleted = 65536; var completeQueue = new Queue <Action <int> >(); // Arrange var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { completeQueue.Enqueue(triggerCompleted); return(0); } }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) using (var memory = new MemoryPool2()) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, null, 0, trace, ltp, new Queue <UvWriteReq>()); var bufferSize = maxBytesPreCompleted; var buffer = new ArraySegment <byte>(new byte[bufferSize], 0, bufferSize); var completedWh = new ManualResetEventSlim(); Action <Task> onCompleted = (Task t) => { Assert.Null(t.Exception); completedWh.Set(); }; // Act socketOutput.WriteAsync(buffer).ContinueWith(onCompleted); // Assert // The first write should pre-complete since it is <= _maxBytesPreCompleted. Assert.True(completedWh.Wait(1000)); // Arrange completedWh.Reset(); // Act socketOutput.WriteAsync(buffer).ContinueWith(onCompleted); // Assert // Too many bytes are already pre-completed for the second write to pre-complete. Assert.False(completedWh.Wait(1000)); // Act completeQueue.Dequeue()(0); // Assert // Finishing the first write should allow the second write to pre-complete. Assert.True(completedWh.Wait(1000)); } }
public void ListenerCanCreateAndDispose(ServiceContext testContext) { var engine = new KestrelEngine(testContext); engine.Start(1); var address = ServerAddress.FromUrl("http://localhost:54321/"); var started = engine.CreateServer(address, App); started.Dispose(); engine.Dispose(); }
public void Create(RequestDelegate app, ServiceContext context, string serverAddress) { context.FrameFactory = (connectionContext, remoteEP, localEP, prepareRequest) => { return(new Frame <HttpContext>(new DummyApplication(app), connectionContext, remoteEP, localEP, prepareRequest)); }; _engine = new KestrelEngine(context); _engine.Start(1); _server = _engine.CreateServer( ServerAddress.FromUrl(serverAddress)); }
public void ListenerCanCreateAndDispose(TestServiceContext testContext) { testContext.App = App; var engine = new KestrelEngine(testContext); engine.Start(1); var address = ServerAddress.FromUrl($"http://localhost:{TestServer.GetNextPort()}/"); var started = engine.CreateServer(address); started.Dispose(); engine.Dispose(); }
public void ListenerCanCreateAndDispose(TestServiceContext testContext) { testContext.App = App; var engine = new KestrelEngine(testContext); engine.Start(1); var address = ServerAddress.FromUrl("http://127.0.0.1:0/"); var started = engine.CreateServer(address); started.Dispose(); engine.Dispose(); }
public void ProducingStartAndProducingCompleteCanBeUsedDirectly() { int nBuffers = 0; var nBufferWh = new ManualResetEventSlim(); var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { nBuffers = buffers; nBufferWh.Set(); triggerCompleted(0); return(0); } }; using (var memory = new MemoryPool()) using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, new MockConnection(), "0", trace, ltp, new Queue <UvWriteReq>()); // block 1 var start = socketOutput.ProducingStart(); start.Block.End = start.Block.Data.Offset + start.Block.Data.Count; // block 2 var block2 = memory.Lease(); block2.End = block2.Data.Offset + block2.Data.Count; start.Block.Next = block2; var end = new MemoryPoolIterator(block2, block2.End); socketOutput.ProducingComplete(end); // A call to Write is required to ensure a write is scheduled socketOutput.WriteAsync(default(ArraySegment <byte>), default(CancellationToken)); Assert.True(nBufferWh.Wait(1000)); Assert.Equal(2, nBuffers); // Cleanup var cleanupTask = socketOutput.WriteAsync( default(ArraySegment <byte>), default(CancellationToken), socketDisconnect: true); } }
public void CanWrite1MB() { // This test was added because when initially implementing write-behind buffering in // SocketOutput, the write callback would never be invoked for writes larger than // _maxBytesPreCompleted even after the write actually completed. // Arrange var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { triggerCompleted(0); return(0); } }; using (var memory = new MemoryPool()) using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, new MockConnection(), "0", trace, ltp, new Queue <UvWriteReq>()); // I doubt _maxBytesPreCompleted will ever be over a MB. If it is, we should change this test. var bufferSize = 1048576; var buffer = new ArraySegment <byte>(new byte[bufferSize], 0, bufferSize); var completedWh = new ManualResetEventSlim(); // Act socketOutput.WriteAsync(buffer, default(CancellationToken)).ContinueWith( (t) => { Assert.Null(t.Exception); completedWh.Set(); } ); // Assert Assert.True(completedWh.Wait(1000)); // Cleanup var cleanupTask = socketOutput.WriteAsync( default(ArraySegment <byte>), default(CancellationToken), socketDisconnect: true); } }
public void ConsumingCompleteCallsBufferSizeControlSubtract(Mock <IBufferSizeControl> mockBufferSizeControl) { using (var kestrelEngine = new KestrelEngine(new MockLibuv(), new TestServiceContext())) { kestrelEngine.Start(1); using (var memory = new MemoryPool()) using (var socketInput = new SocketInput(memory, null, mockBufferSizeControl?.Object)) { socketInput.IncomingData(new byte[20], 0, 20); var iterator = socketInput.ConsumingStart(); iterator.Skip(5); socketInput.ConsumingComplete(iterator, iterator); mockBufferSizeControl?.Verify(b => b.Subtract(5)); } } }
public void CanWrite1MB() { // This test was added because when initially implementing write-behind buffering in // SocketOutput, the write callback would never be invoked for writes larger than // _maxBytesPreCompleted even after the write actually completed. // Arrange var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { triggerCompleted(0); return(0); } }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); 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; var buffer = new ArraySegment <byte>(new byte[bufferSize], 0, bufferSize); var completedWh = new ManualResetEventSlim(); Action <Exception, object, bool> onCompleted = (ex, state, calledInline) => { Assert.Null(ex); Assert.Null(state); completedWh.Set(); }; // Act socketOutput.Write(buffer, onCompleted, null); // Assert Assert.True(completedWh.Wait(1000)); } }
public TestServer(RequestDelegate app, ServiceContext context, string serverAddress) { context.FrameFactory = connectionContext => { return(new Frame <HttpContext>(new DummyApplication(app), connectionContext)); }; try { _engine = new KestrelEngine(context); _engine.Start(1); _address = ServerAddress.FromUrl(serverAddress); _server = _engine.CreateServer(_address); } catch { _server?.Dispose(); _engine?.Dispose(); throw; } }
public async Task OnlyWritesRequestingCancellationAreErroredOnCancellation() { // This should match _maxBytesPreCompleted in SocketOutput var maxBytesPreCompleted = 65536; var completeQueue = new Queue <Action <int> >(); // Arrange var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { completeQueue.Enqueue(triggerCompleted); return(0); } }; using (var memory = new MemoryPool()) using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); using (var mockConnection = new MockConnection()) { ISocketOutput socketOutput = new SocketOutput(kestrelThread, socket, memory, mockConnection, "0", trace, ltp, new Queue <UvWriteReq>()); var bufferSize = maxBytesPreCompleted; var data = new byte[bufferSize]; var fullBuffer = new ArraySegment <byte>(data, 0, bufferSize); var cts = new CancellationTokenSource(); // Act var task1Success = socketOutput.WriteAsync(fullBuffer, cancellationToken: cts.Token); // task1 should complete successfully as < _maxBytesPreCompleted // First task is completed and successful Assert.True(task1Success.IsCompleted); Assert.False(task1Success.IsCanceled); Assert.False(task1Success.IsFaulted); // following tasks should wait. var task2Throw = socketOutput.WriteAsync(fullBuffer, cancellationToken: cts.Token); var task3Success = socketOutput.WriteAsync(fullBuffer, cancellationToken: default(CancellationToken)); // Give time for tasks to percolate await Task.Delay(1000); // Second task is not completed Assert.False(task2Throw.IsCompleted); Assert.False(task2Throw.IsCanceled); Assert.False(task2Throw.IsFaulted); // Third task is not completed Assert.False(task3Success.IsCompleted); Assert.False(task3Success.IsCanceled); Assert.False(task3Success.IsFaulted); cts.Cancel(); // Second task is now canceled await Assert.ThrowsAsync <TaskCanceledException>(() => task2Throw); Assert.True(task2Throw.IsCanceled); // Third task is now completed await task3Success; // Fourth task immediately cancels as the token is canceled var task4Throw = socketOutput.WriteAsync(fullBuffer, cancellationToken: cts.Token); Assert.True(task4Throw.IsCompleted); Assert.True(task4Throw.IsCanceled); Assert.False(task4Throw.IsFaulted); var task5Success = socketOutput.WriteAsync(fullBuffer, cancellationToken: default(CancellationToken)); // task5 should complete immediately Assert.True(task5Success.IsCompleted); Assert.False(task5Success.IsCanceled); Assert.False(task5Success.IsFaulted); cts = new CancellationTokenSource(); var task6Success = socketOutput.WriteAsync(fullBuffer, cancellationToken: cts.Token); // task6 should complete immediately but not cancel as its cancellation token isn't set Assert.True(task6Success.IsCompleted); Assert.False(task6Success.IsCanceled); Assert.False(task6Success.IsFaulted); Assert.True(true); // Cleanup var cleanupTask = ((SocketOutput)socketOutput).WriteAsync( default(ArraySegment <byte>), default(CancellationToken), socketDisconnect: true); foreach (var triggerCompleted in completeQueue) { triggerCompleted(0); } } } }
public async Task FailedWriteCompletesOrCancelsAllPendingTasks() { // This should match _maxBytesPreCompleted in SocketOutput var maxBytesPreCompleted = 65536; var completeQueue = new Queue <Action <int> >(); // Arrange var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { completeQueue.Enqueue(triggerCompleted); return(0); } }; using (var memory = new MemoryPool()) using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); using (var mockConnection = new MockConnection()) { var abortedSource = mockConnection.RequestAbortedSource; ISocketOutput socketOutput = new SocketOutput(kestrelThread, socket, memory, mockConnection, "0", trace, ltp, new Queue <UvWriteReq>()); var bufferSize = maxBytesPreCompleted; var data = new byte[bufferSize]; var fullBuffer = new ArraySegment <byte>(data, 0, bufferSize); // Act var task1Success = socketOutput.WriteAsync(fullBuffer, cancellationToken: abortedSource.Token); // task1 should complete successfully as < _maxBytesPreCompleted // First task is completed and successful Assert.True(task1Success.IsCompleted); Assert.False(task1Success.IsCanceled); Assert.False(task1Success.IsFaulted); // following tasks should wait. var task2Success = socketOutput.WriteAsync(fullBuffer, cancellationToken: default(CancellationToken)); var task3Canceled = socketOutput.WriteAsync(fullBuffer, cancellationToken: abortedSource.Token); // Give time for tasks to percolate await Task.Delay(1000); // Second task is not completed Assert.False(task2Success.IsCompleted); Assert.False(task2Success.IsCanceled); Assert.False(task2Success.IsFaulted); // Third task is not completed Assert.False(task3Canceled.IsCompleted); Assert.False(task3Canceled.IsCanceled); Assert.False(task3Canceled.IsFaulted); // Cause the first write to fail. completeQueue.Dequeue()(-1); // Second task is now completed await task2Success; // Third task is now canceled await Assert.ThrowsAsync <TaskCanceledException>(() => task3Canceled); Assert.True(task3Canceled.IsCanceled); // Cleanup var cleanupTask = ((SocketOutput)socketOutput).WriteAsync( default(ArraySegment <byte>), default(CancellationToken), socketDisconnect: true); foreach (var triggerCompleted in completeQueue) { triggerCompleted(0); } } } }
public void WritesDontCompleteImmediatelyWhenTooManyBytesIncludingNonImmediateAreAlreadyPreCompleted() { // This should match _maxBytesPreCompleted in SocketOutput var maxBytesPreCompleted = 65536; var completeQueue = new Queue <Action <int> >(); var writeRequestedWh = new ManualResetEventSlim(); // Arrange var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { completeQueue.Enqueue(triggerCompleted); writeRequestedWh.Set(); return(0); } }; using (var memory = new MemoryPool()) using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, new MockConnection(), "0", trace, ltp, new Queue <UvWriteReq>()); var bufferSize = maxBytesPreCompleted / 2; var data = new byte[bufferSize]; var halfWriteBehindBuffer = new ArraySegment <byte>(data, 0, bufferSize); // Act var writeTask1 = socketOutput.WriteAsync(halfWriteBehindBuffer, default(CancellationToken)); // Assert // The first write should pre-complete since it is <= _maxBytesPreCompleted. Assert.Equal(TaskStatus.RanToCompletion, writeTask1.Status); Assert.True(writeRequestedWh.Wait(1000)); writeRequestedWh.Reset(); // Add more bytes to the write-behind buffer to prevent the next write from var iter = socketOutput.ProducingStart(); iter.CopyFrom(halfWriteBehindBuffer); socketOutput.ProducingComplete(iter); // Act var writeTask2 = socketOutput.WriteAsync(halfWriteBehindBuffer, default(CancellationToken)); // Assert // Too many bytes are already pre-completed for the fourth write to pre-complete. Assert.True(writeRequestedWh.Wait(1000)); Assert.False(writeTask2.IsCompleted); // 2 calls have been made to uv_write Assert.Equal(2, completeQueue.Count); // Act completeQueue.Dequeue()(0); // Assert // Finishing the first write should allow the second write to pre-complete. Assert.True(writeTask2.Wait(1000)); // Cleanup var cleanupTask = socketOutput.WriteAsync( default(ArraySegment <byte>), default(CancellationToken), socketDisconnect: true); foreach (var triggerCompleted in completeQueue) { triggerCompleted(0); } } }
public void WritesDontGetCompletedTooQuickly() { // This should match _maxBytesPreCompleted in SocketOutput var maxBytesPreCompleted = 65536; var completeQueue = new Queue <Action <int> >(); var onWriteWh = new ManualResetEventSlim(); // Arrange var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { completeQueue.Enqueue(triggerCompleted); onWriteWh.Set(); return(0); } }; using (var memory = new MemoryPool()) using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, new MockConnection(), "0", trace, ltp, new Queue <UvWriteReq>()); var bufferSize = maxBytesPreCompleted; var buffer = new ArraySegment <byte>(new byte[bufferSize], 0, bufferSize); var completedWh = new ManualResetEventSlim(); Action <Task> onCompleted = (Task t) => { Assert.Null(t.Exception); completedWh.Set(); }; var completedWh2 = new ManualResetEventSlim(); Action <Task> onCompleted2 = (Task t) => { Assert.Null(t.Exception); completedWh2.Set(); }; // Act (Pre-complete the maximum number of bytes in preparation for the rest of the test) socketOutput.WriteAsync(buffer, default(CancellationToken)).ContinueWith(onCompleted); // Assert // The first write should pre-complete since it is <= _maxBytesPreCompleted. Assert.True(completedWh.Wait(1000)); Assert.True(onWriteWh.Wait(1000)); // Arrange completedWh.Reset(); onWriteWh.Reset(); // Act socketOutput.WriteAsync(buffer, default(CancellationToken)).ContinueWith(onCompleted); socketOutput.WriteAsync(buffer, default(CancellationToken)).ContinueWith(onCompleted2); Assert.True(onWriteWh.Wait(1000)); completeQueue.Dequeue()(0); // Assert // Too many bytes are already pre-completed for the third but not the second write to pre-complete. // https://github.com/aspnet/KestrelHttpServer/issues/356 Assert.True(completedWh.Wait(1000)); Assert.False(completedWh2.Wait(1000)); // Act completeQueue.Dequeue()(0); // Assert // Finishing the first write should allow the second write to pre-complete. Assert.True(completedWh2.Wait(1000)); // Cleanup var cleanupTask = ((SocketOutput)socketOutput).WriteAsync( default(ArraySegment <byte>), default(CancellationToken), socketDisconnect: true); foreach (var triggerCompleted in completeQueue) { triggerCompleted(0); } } }