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())) { 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); var bufferSize = maxBytesPreCompleted; 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 // The first write should pre-complete since it is <= _maxBytesPreCompleted. Assert.True(completedWh.Wait(1000)); // Arrange completedWh.Reset(); // Act socketOutput.Write(buffer, onCompleted, null); // 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 TestInput() { var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var memory2 = new MemoryPool2(); FrameContext = new FrameContext { SocketInput = new SocketInput(memory2, ltp), ConnectionControl = this, FrameControl = this }; }
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())) 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>()); // 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).ContinueWith( (t) => { Assert.Null(t.Exception); completedWh.Set(); } ); // Assert Assert.True(completedWh.Wait(1000)); } }
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 KestrelTrace(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 void EmptyHeaderValuesCanBeParsed(string rawHeaders, int numHeaders) { var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketInput = new SocketInput(new MemoryPool2(), ltp); var headerCollection = new FrameRequestHeaders(); var headerArray = Encoding.ASCII.GetBytes(rawHeaders); var inputBuffer = socketInput.IncomingStart(headerArray.Length); Buffer.BlockCopy(headerArray, 0, inputBuffer.Data.Array, inputBuffer.Data.Offset, headerArray.Length); socketInput.IncomingComplete(headerArray.Length, null); var success = Frame.TakeMessageHeaders(socketInput, headerCollection); Assert.True(success); Assert.Equal(numHeaders, headerCollection.Count()); // Assert TakeMessageHeaders consumed all the input var scan = socketInput.ConsumingStart(); Assert.True(scan.IsEnd); }
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 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>()); // 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 MemoryPoolIterator2(block2, block2.End); socketOutput.ProducingComplete(end); // A call to Write is required to ensure a write is scheduled socketOutput.WriteAsync(default(ArraySegment<byte>)); Assert.True(nBufferWh.Wait(1000)); Assert.Equal(2, nBuffers); } }
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 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(); }; 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).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).ContinueWith(onCompleted); socketOutput.WriteAsync(buffer).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)); } }
public void WritesDontCompleteImmediatelyWhenTooManyBytesIncludingNonImmediateAreAlreadyPreCompleted() { // 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 data = new byte[bufferSize]; var fullBuffer = new ArraySegment<byte>(data, 0, bufferSize); var halfBuffer = new ArraySegment<byte>(data, 0, bufferSize / 2); var completedWh = new ManualResetEventSlim(); Action<Task> onCompleted = (Task t) => { Assert.Null(t.Exception); completedWh.Set(); }; // Act socketOutput.WriteAsync(halfBuffer, false).ContinueWith(onCompleted); // Assert // The first write should pre-complete since it is not immediate. Assert.True(completedWh.Wait(1000)); // Arrange completedWh.Reset(); // Act socketOutput.WriteAsync(halfBuffer).ContinueWith(onCompleted); // Assert // The second write should pre-complete since it is <= _maxBytesPreCompleted. Assert.True(completedWh.Wait(1000)); // Arrange completedWh.Reset(); // Act socketOutput.WriteAsync(halfBuffer, false).ContinueWith(onCompleted); // Assert // The third write should pre-complete since it is not immediate, even though too many. Assert.True(completedWh.Wait(1000)); // Arrange completedWh.Reset(); // Act socketOutput.WriteAsync(halfBuffer).ContinueWith(onCompleted); // Assert // Too many bytes are already pre-completed for the fourth write to pre-complete. Assert.False(completedWh.Wait(1000)); // Act while (completeQueue.Count > 0) { completeQueue.Dequeue()(0); } // Assert // Finishing the first write should allow the second write to pre-complete. Assert.True(completedWh.Wait(1000)); } }
public async Task ConcurrentReadsFailGracefully() { // Arrange var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); using (var memory2 = new MemoryPool2()) { var socketInput = new SocketInput(memory2, ltp); var task0Threw = false; var task1Threw = false; var task2Threw = false; var task0 = AwaitAsTaskAsync(socketInput); Assert.False(task0.IsFaulted); var task = task0.ContinueWith( (t) => { TestConcurrentFaultedTask(t); task0Threw = true; }, TaskContinuationOptions.OnlyOnFaulted); Assert.False(task0.IsFaulted); // Awaiting/continuing two tasks faults both var task1 = AwaitAsTaskAsync(socketInput); await task1.ContinueWith( (t) => { TestConcurrentFaultedTask(t); task1Threw = true; }, TaskContinuationOptions.OnlyOnFaulted); await task; Assert.True(task0.IsFaulted); Assert.True(task1.IsFaulted); Assert.True(task0Threw); Assert.True(task1Threw); // socket stays faulted var task2 = AwaitAsTaskAsync(socketInput); await task2.ContinueWith( (t) => { TestConcurrentFaultedTask(t); task2Threw = true; }, TaskContinuationOptions.OnlyOnFaulted); Assert.True(task2.IsFaulted); Assert.True(task2Threw); } }