public async Task ConnectionDoesNotResumeAfterSocketCloseIfBackpressureIsApplied() { var mockConnectionHandler = new MockConnectionHandler(); var mockLibuv = new MockLibuv(); var transportContext = new TestLibuvTransportContext() { ConnectionHandler = mockConnectionHandler }; var transport = new LibuvTransport(mockLibuv, transportContext, null); var thread = new LibuvThread(transport); mockConnectionHandler.InputOptions = pool => new PipeOptions( pool: pool, maximumSizeHigh: 3); // We don't set the output writer scheduler here since we want to run the callback inline mockConnectionHandler.OutputOptions = pool => new PipeOptions(pool: pool, readerScheduler: thread); Task connectionTask = null; try { await thread.StartAsync(); // Write enough to make sure back pressure will be applied await thread.PostAsync <object>(_ => { var listenerContext = new ListenerContext(transportContext) { Thread = thread }; var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log); var connection = new LibuvConnection(listenerContext, socket); connectionTask = connection.Start(); mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored); mockLibuv.ReadCallback(socket.InternalGetHandle(), 5, ref ignored); }, null); // Now assert that we removed the callback from libuv to stop reading Assert.Null(mockLibuv.AllocCallback); Assert.Null(mockLibuv.ReadCallback); // Now complete the output writer so that the connection closes mockConnectionHandler.Output.Writer.Complete(); await connectionTask.TimeoutAfter(TimeSpan.FromSeconds(10)); // Assert that we don't try to start reading Assert.Null(mockLibuv.AllocCallback); Assert.Null(mockLibuv.ReadCallback); } finally { await thread.StopAsync(TimeSpan.FromSeconds(1)); } }
public ISocket CreateSocket() { var socket = new MockSocket(); sockets.Add(socket); return(socket); }
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 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 async Task ConnectionDoesNotResumeAfterSocketCloseIfBackpressureIsApplied() { var mockLibuv = new MockLibuv(); var transportContext = new TestLibuvTransportContext(); var thread = new LibuvThread(mockLibuv, transportContext); var listenerContext = new ListenerContext(transportContext) { Thread = thread, InputOptions = new PipeOptions( pool: thread.MemoryPool, pauseWriterThreshold: 3, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false), // We don't set the output writer scheduler here since we want to run the callback inline OutputOptions = new PipeOptions( pool: thread.MemoryPool, readerScheduler: thread, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false) }; try { await thread.StartAsync(); // Write enough to make sure back pressure will be applied await thread.PostAsync <object>(_ => { var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log); listenerContext.HandleConnection(socket); mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored); mockLibuv.ReadCallback(socket.InternalGetHandle(), 5, ref ignored); }, null); var connection = await listenerContext.AcceptAsync(); // Now assert that we removed the callback from libuv to stop reading Assert.Null(mockLibuv.AllocCallback); Assert.Null(mockLibuv.ReadCallback); // Now complete the output writer so that the connection closes await connection.DisposeAsync(); // Assert that we don't try to start reading Assert.Null(mockLibuv.AllocCallback); Assert.Null(mockLibuv.ReadCallback); } finally { await thread.StopAsync(TimeSpan.FromSeconds(5)); } }
public ServerTest() { _tokenSource = new CancellationTokenSource(); _mockSocket = new MockSocket(); _mockListener = new MockListener(_mockSocket); _mockRequestProcessor = new MockRequestProcessor(); var mockRequestProcessorFactory = new MockRequestProcessorFactory(_mockRequestProcessor); var logger = new FileLogger(Path.Combine(Environment.CurrentDirectory, @"..\..\..\logs\testlog.txt")); _server = new Server(_mockListener, mockRequestProcessorFactory, logger); }
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 RequestProcessorTest() { _ioStream = new MemoryStream(new byte[1024]); _mockSocket = new MockSocket(_ioStream); _request = new Request(); _mockParser = new MockParser(_request); _mockResponse = new MockResponse(); _mockHandler = new MockHandler(_mockResponse); var mockRouter = new MockRouter(_mockHandler); _requestProcessor = new RequestProcessor(_mockSocket, _mockParser, mockRouter); }
public ISocket CreateSocket() { var socket = new MockSocket(); sockets.Add(new SocketMeta { Socket = socket, IsRegistrationSocket = IsRegistrationSocket() } ); return(socket); }
public MockSocket GetAsyncCompletionSocket() { MockSocket socket = null; var retries = socketWaitRetries; Func <bool> socketIsMissing = () => socket == null; while (retries-- > 0 && socketIsMissing()) { socket = sockets.FirstOrDefault(s => !s.IsRegistrationSocket && s.Socket.GetIdentity() == null)?.Socket; Wait(socketIsMissing); } return(socket); }
public MockSocket GetScaleoutBackendSocket() { MockSocket socket = null; var retries = socketWaitRetries; while (retries-- > 0 && socket == null) { socket = sockets.FirstOrDefault(IsScaleOutBackendSocket); Wait(socket); } return(socket); }
public MockSocket GetSendingSocket() { MockSocket socket = null; var retries = socketWaitRetries; Func <bool> socketIsMissing = () => socket == null; while (retries-- > 0 && socketIsMissing()) { socket = sockets.FirstOrDefault(s => s.GetIdentity() == null); Wait(socketIsMissing); } return(socket); }
public static async Task TestSendBehavior(string expectedVerb, RetrievalCommand command) { Assert.AreEqual(expectedVerb, command.Verb); var socket = new MockSocket(); await command.SendRequest(socket); using (var stream = socket.GetSentData()) { var line = stream.ReadLine(); Assert.AreEqual(expectedVerb + " " + command.Key, line); Assert.AreEqual(stream.Position, stream.Length); } }
private MockSocket GetSocket(SocketPurpose socketPurpose) { MockSocket socket = null; var retries = socketWaitRetries; Func <bool> socketIsMissing = () => socket == null; while (retries-- > 0 && socketIsMissing()) { socket = sockets.FirstOrDefault(s => s.Purpose == socketPurpose)?.Socket; Wait(socketIsMissing); } return(socket); }
public ISocket CreateSocket() { var socket = new MockSocket(); sockets.Add(new SocketMeta { Socket = socket, Purpose = GetSocketPurpose() } ); return(socket); }
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 SocketFactory_WithValidSetting_ReturnsSetSocket() { //arrange ISocket mockSocket = new MockSocket(); SocketFactory factory = new SocketFactory(); factory.setSocket(mockSocket); //act ISocket resultSocket = factory.CreateSocket(); //assert Assert.IsTrue(resultSocket == mockSocket); //cleanup factory.setSocket(null); }
private LibuvOuputProcessor CreateOutputProducer(PipeOptions pipeOptions, CancellationTokenSource cts = null) { var pair = DuplexPipe.CreateConnectionPair(pipeOptions, pipeOptions); var logger = new TestApplicationErrorLogger(); var serviceContext = new TestServiceContext { Log = new TestKestrelTrace(logger), Scheduler = PipeScheduler.Inline }; var transportContext = new TestLibuvTransportContext { Log = new LibuvTrace(logger) }; var socket = new MockSocket(_mockLibuv, _libuvThread.Loop.ThreadId, transportContext.Log); var consumer = new LibuvOutputConsumer(pair.Application.Input, _libuvThread, socket, "0", transportContext.Log); var connectionFeatures = new FeatureCollection(); connectionFeatures.Set(Mock.Of <IConnectionLifetimeFeature>()); var http1Connection = new Http1Connection(new HttpConnectionContext { ServiceContext = serviceContext, ConnectionContext = Mock.Of <ConnectionContext>(), ConnectionFeatures = connectionFeatures, MemoryPool = _memoryPool, TimeoutControl = Mock.Of <ITimeoutControl>(), Transport = pair.Transport }); if (cts != null) { http1Connection.RequestAborted.Register(cts.Cancel); } var outputTask = WriteOutputAsync(consumer, pair.Application.Input, http1Connection); var processor = new LibuvOuputProcessor { ProcessingTask = outputTask, OutputProducer = (Http1OutputProducer)http1Connection.Output, PipeWriter = pair.Transport.Output, }; return(processor); }
public async Task DoesNotThrowIfOnReadCallbackCalledWithEOFButAllocCallbackNotCalled() { var mockConnectionDispatcher = new MockConnectionDispatcher(); var mockLibuv = new MockLibuv(); var transportContext = new TestLibuvTransportContext() { ConnectionDispatcher = mockConnectionDispatcher }; var transport = new LibuvTransport(mockLibuv, transportContext, null); var thread = new LibuvThread(transport); Task connectionTask = null; try { await thread.StartAsync(); await thread.PostAsync(_ => { var listenerContext = new ListenerContext(transportContext) { Thread = thread }; var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log); var connection = new LibuvConnection(socket, listenerContext.TransportContext.Log, thread, null, null); listenerContext.TransportContext.ConnectionDispatcher.OnConnection(connection); connectionTask = connection.Start(); var ignored = new LibuvFunctions.uv_buf_t(); mockLibuv.ReadCallback(socket.InternalGetHandle(), TestConstants.EOF, ref ignored); }, (object)null); var readAwaitable = await mockConnectionDispatcher.Input.Reader.ReadAsync(); Assert.True(readAwaitable.IsCompleted); } finally { mockConnectionDispatcher.Input.Reader.Complete(); mockConnectionDispatcher.Output.Writer.Complete(); await connectionTask; await thread.StopAsync(TimeSpan.FromSeconds(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())) 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 static async Task TestSendBehavior(string expectedVerb, ArithmeticalCommand command) { var mockSocket = new MockSocket(); await command.SendRequest(mockSocket); using (var stream = mockSocket.GetSentData()) { // validate the command line var commandLine = stream.ReadLine(); Assert.IsNotNull(commandLine); var parts = commandLine.Split(' '); Assert.AreEqual(3, parts.Length); Assert.AreEqual(command.Verb, parts[0]); Assert.AreEqual(command.Key, parts[1]); Assert.AreEqual(command.Value.ToString(), parts[2]); Assert.AreEqual(stream.Length, stream.Position); } }
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 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 static async Task TestSendBehavior <T>(string expectedVerb, StorageCommand <T> command) { var mockSocket = new MockSocket(); await command.SendRequest(mockSocket); using (var stream = mockSocket.GetSentData()) { // validate the command line var commandLine = stream.ReadLine(); Assert.IsNotNull(commandLine); var parts = commandLine.Split(' '); Assert.AreEqual(5, parts.Length); Assert.AreEqual(command.Verb, parts[0]); Assert.AreEqual(command.Key, parts[1]); if (command.Options == null) { Assert.AreEqual("0", parts[2]); Assert.AreEqual("0", parts[3]); } else { Assert.AreEqual(command.Options.Flags.ToString(), parts[2]); var expectedExpires = command.Options.ExpirationTime.HasValue ? ((uint)(command.Options.ExpirationTime.Value.TotalSeconds)).ToString() : "0"; Assert.AreEqual(expectedExpires, parts[3]); } Assert.AreEqual(command.Data.Count.ToString(), parts[4]); // validate the data block var block = new byte[command.Data.Count]; var read = stream.Read(block, 0, block.Length); Assert.AreEqual(block.Length, read); var expected = new byte[command.Data.Count]; Buffer.BlockCopy(command.Data.Array, command.Data.Offset, expected, 0, command.Data.Count); CollectionAssert.AreEqual(expected, block); // read the last endline Assert.AreEqual('\r', stream.ReadByte()); Assert.AreEqual('\n', stream.ReadByte()); Assert.AreEqual(-1, stream.ReadByte()); } }
public async Task DoesNotEndConnectionOnZeroRead() { var mockConnectionDispatcher = new MockConnectionDispatcher(); var mockLibuv = new MockLibuv(); var transportContext = new TestLibuvTransportContext() { ConnectionDispatcher = mockConnectionDispatcher }; var transport = new LibuvTransport(mockLibuv, transportContext, null); var thread = new LibuvThread(transport); try { await thread.StartAsync(); await thread.PostAsync(_ => { var listenerContext = new ListenerContext(transportContext) { Thread = thread }; var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log); var connection = new LibuvConnection(socket, listenerContext.TransportContext.Log, thread, null, null); listenerContext.TransportContext.ConnectionDispatcher.OnConnection(connection); _ = connection.Start(); mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored); mockLibuv.ReadCallback(socket.InternalGetHandle(), 0, ref ignored); }, (object)null); var readAwaitable = mockConnectionDispatcher.Input.Reader.ReadAsync(); Assert.False(readAwaitable.IsCompleted); } finally { await thread.StopAsync(TimeSpan.FromSeconds(5)); } }
public void QueueTooManyClients() { var socket = new MockSocket(); var dispatcherInbox = new BoundedInbox(); var dispatcher = new DispatchController(dispatcherInbox, new BoundedInbox(), new BoundedInbox[64], new IdentityHash()); dispatcher.OnConnectionAccepted = (_) => { }; for (var i = 0; i < Constants.MaxActiveConnections; i++) { var client0 = new ConnectionController(dispatcherInbox, socket, ClientId.Next()); client0.OnRequestReceived = () => { }; dispatcher.AddConnection(client0); dispatcher.HandleNewConnection(); } var client1 = new ConnectionController(dispatcherInbox, socket, ClientId.Next()); client1.OnRequestReceived = () => { }; socket.ExpectSend(data => { Assert.Equal(ProtocolErrorResponse.SizeInBytes, data.Length); var errorMessage = new ProtocolErrorResponse(data); Assert.Equal(MessageKind.ProtocolErrorResponse, errorMessage.MessageHeader.MessageKind); Assert.Equal(ProtocolErrorStatus.TooManyActiveClients, errorMessage.Status); return(data.Length); }); dispatcher.AddConnection(client1); dispatcher.HandleNewConnection(); client1.HandleResponse(); socket.ExpectAllDone(); }
private OutputProducer CreateOutputProducer(PipeOptions pipeOptions, CancellationTokenSource cts = null) { var pipe = _pipeFactory.Create(pipeOptions); var logger = new TestApplicationErrorLogger(); var serviceContext = new TestServiceContext { Log = new TestKestrelTrace(logger), ThreadPool = new InlineLoggingThreadPool(new TestKestrelTrace(logger)) }; var transportContext = new TestLibuvTransportContext { Log = new LibuvTrace(logger) }; var socket = new MockSocket(_mockLibuv, _libuvThread.Loop.ThreadId, transportContext.Log); var consumer = new LibuvOutputConsumer(pipe.Reader, _libuvThread, socket, "0", transportContext.Log); var frame = new Frame <object>(null, new FrameContext { ServiceContext = serviceContext, ConnectionInformation = new MockConnectionInformation { PipeFactory = _pipeFactory }, TimeoutControl = Mock.Of <ITimeoutControl>(), Output = pipe }); if (cts != null) { frame.RequestAborted.Register(cts.Cancel); } var ignore = WriteOutputAsync(consumer, pipe.Reader, frame); return(frame.Output); }
private Http1OutputProducer CreateOutputProducer(PipeOptions pipeOptions, CancellationTokenSource cts = null) { var pair = DuplexPipe.CreateConnectionPair(pipeOptions, pipeOptions); var logger = new TestApplicationErrorLogger(); var serviceContext = new TestServiceContext { Log = new TestKestrelTrace(logger), ThreadPool = new InlineLoggingThreadPool(new TestKestrelTrace(logger)) }; var transportContext = new TestLibuvTransportContext { Log = new LibuvTrace(logger) }; var socket = new MockSocket(_mockLibuv, _libuvThread.Loop.ThreadId, transportContext.Log); var consumer = new LibuvOutputConsumer(pair.Application.Input, _libuvThread, socket, "0", transportContext.Log); var http1Connection = new Http1Connection(new Http1ConnectionContext { ServiceContext = serviceContext, ConnectionFeatures = new FeatureCollection(), MemoryPool = _memoryPool, TimeoutControl = Mock.Of <ITimeoutControl>(), Application = pair.Application, Transport = pair.Transport }); if (cts != null) { http1Connection.RequestAborted.Register(cts.Cancel); } var ignore = WriteOutputAsync(consumer, pair.Application.Input, http1Connection); return((Http1OutputProducer)http1Connection.Output); }
public void SetUp() { mockSocket = new MockSocket(); queue = new OutputSegmentQueue(mockSocket); mockProducers = new List<MockDataProducer>(); }
public async Task ConnectionDoesNotResumeAfterReadCallbackScheduledAndSocketCloseIfBackpressureIsApplied() { var mockConnectionDispatcher = new MockConnectionDispatcher(); var mockLibuv = new MockLibuv(); var transportContext = new TestLibuvTransportContext() { ConnectionDispatcher = mockConnectionDispatcher }; var transport = new LibuvTransport(mockLibuv, transportContext, null); var thread = new LibuvThread(transport); var mockScheduler = new Mock <PipeScheduler>(); Action backPressure = null; mockScheduler.Setup(m => m.Schedule(It.IsAny <Action <object> >(), It.IsAny <object>())).Callback <Action <object>, object>((a, o) => { backPressure = () => a(o); }); mockConnectionDispatcher.InputOptions = pool => new PipeOptions( pool: pool, pauseWriterThreshold: 3, resumeWriterThreshold: 3, writerScheduler: mockScheduler.Object, readerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); mockConnectionDispatcher.OutputOptions = pool => new PipeOptions(pool: pool, readerScheduler: thread, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); Task connectionTask = null; try { await thread.StartAsync(); // Write enough to make sure back pressure will be applied await thread.PostAsync <object>(_ => { var listenerContext = new ListenerContext(transportContext) { Thread = thread }; var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log); var connection = new LibuvConnection(socket, listenerContext.TransportContext.Log, thread, null, null); listenerContext.TransportContext.ConnectionDispatcher.OnConnection(connection); connectionTask = connection.Start(); mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored); mockLibuv.ReadCallback(socket.InternalGetHandle(), 5, ref ignored); }, null); // Now assert that we removed the callback from libuv to stop reading Assert.Null(mockLibuv.AllocCallback); Assert.Null(mockLibuv.ReadCallback); // Now release backpressure by reading the input var result = await mockConnectionDispatcher.Input.Reader.ReadAsync(); // Calling advance will call into our custom scheduler that captures the back pressure // callback mockConnectionDispatcher.Input.Reader.AdvanceTo(result.Buffer.End); // Cancel the current pending flush mockConnectionDispatcher.Input.Writer.CancelPendingFlush(); // Now release the back pressure await thread.PostAsync(a => a(), backPressure); // Assert that we don't try to start reading since the write was cancelled Assert.Null(mockLibuv.AllocCallback); Assert.Null(mockLibuv.ReadCallback); // Now complete the output writer and wait for the connection to close mockConnectionDispatcher.Output.Writer.Complete(); await connectionTask.DefaultTimeout(); // Assert that we don't try to start reading Assert.Null(mockLibuv.AllocCallback); Assert.Null(mockLibuv.ReadCallback); } finally { await thread.StopAsync(TimeSpan.FromSeconds(5)); } }
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 void FillChainControllerInbox() { var socket1 = new MockSocket(); var socket2 = new MockSocket(); var dispatcherInbox = new BoundedInbox(); var client1 = new ConnectionController(dispatcherInbox, socket1, ClientId.Next()); var client2 = new ConnectionController(dispatcherInbox, socket2, ClientId.Next()); client1.OnRequestReceived = () => { }; client2.OnRequestReceived = () => { }; Func <int, int, MockSocket.SpanToInt> func = (s1, s2) => { return(data => { data.Clear(); BinaryPrimitives.TryWriteInt32LittleEndian(data, s1); // TODO: [vermorel] PingChainController has been removed, logic need to be upgraded. //MessageKind.PingChainController.WriteTo(data.Slice(MessageHeaderHelper.MessageKindStart)); return s2; }); }; socket2.ExpectReceive(func(LargeMessageSize, MessageHeader.SizeInBytes)); socket2.ExpectReceive(func(LargeMessageSize, LargeMessageSize)); socket1.ExpectReceive(func(LargeMessageSize, MessageHeader.SizeInBytes)); socket1.ExpectReceive(func(LargeMessageSize, LargeMessageSize)); // request too short var bodyStart = sizeof(int) + RequestId.SizeInBytes + ClientId.SizeInBytes + sizeof(MessageKind); socket2.ExpectReceive(func(bodyStart, bodyStart)); socket2.ExpectSend(data => { Assert.Equal(ProtocolErrorResponse.SizeInBytes, data.Length); var message = new ProtocolErrorResponse(data); Assert.Equal(MessageKind.ProtocolErrorResponse, message.MessageHeader.MessageKind); Assert.Equal(ProtocolErrorStatus.RequestTooShort, message.Status); return(ProtocolErrorResponse.SizeInBytes); }); socket2.ExpectConnected(() => true); socket1.ExpectConnected(() => true); var dispatcher = new DispatchController(dispatcherInbox, new BoundedInbox(Constants.MaxResponseSize), Enumerable.Range(0, 32).Select(x => new BoundedInbox()).ToArray(), new IdentityHash()); // Nil handling of notifications dispatcher.OnBlockMessageDispatched = () => { }; for (var i = 0; i < dispatcher.OnCoinMessageDispatched.Length; i++) { dispatcher.OnCoinMessageDispatched[i] = () => { } } ; dispatcher.OnConnectionAccepted = (_) => { }; dispatcher.AddConnection(client1); dispatcher.AddConnection(client2); dispatcher.HandleNewConnection(); dispatcher.HandleNewConnection(); client1.HandleRequest(); client2.HandleRequest(); client2.HandleRequest(); client2.HandleResponse(); dispatcher.HandleRequest(); dispatcher.HandleRequest(); dispatcher.HandleRequest(); socket1.ExpectAllDone(); socket2.ExpectAllDone(); }
public void SendAnswers() { var dispatcherInbox = new BoundedInbox(); var socket1 = new MockSocket(); var socket2 = new MockSocket(); var client1 = new ConnectionController(dispatcherInbox, socket1, ClientId.Next()); var client2 = new ConnectionController(dispatcherInbox, socket2, ClientId.Next()); client1.OnRequestReceived = () => { }; client2.OnRequestReceived = () => { }; var dispatcher = new DispatchController(dispatcherInbox, new BoundedInbox(), new BoundedInbox[2], new IdentityHash()); // Nil handling of notifications dispatcher.OnBlockMessageDispatched = () => { }; for (var i = 0; i < dispatcher.OnCoinMessageDispatched.Length; i++) { dispatcher.OnCoinMessageDispatched[i] = () => { } } ; dispatcher.OnConnectionAccepted = (_) => { }; var firstMessage = new byte[Constants.MaxRequestSize]; var message = new Message(firstMessage); message.Header.ClientId = client2.ClientId; message.Header.MessageSizeInBytes = Constants.MaxRequestSize; message.Header.MessageKind = MessageKind.GetCoinResponse; var secondMessage = new byte[Constants.MaxRequestSize]; message = new Message(secondMessage); message.Header.ClientId = client1.ClientId; message.Header.MessageSizeInBytes = Constants.MaxRequestSize; message.Header.MessageKind = MessageKind.GetCoinResponse; var thirdMessage = new byte[50]; message = new Message(thirdMessage); message.Header.ClientId = client1.ClientId; message.Header.MessageSizeInBytes = 50; message.Header.MessageKind = MessageKind.GetCoinResponse; var fourthMessage = new byte[50]; message = new Message(fourthMessage); message.Header.ClientId = client2.ClientId; message.Header.MessageSizeInBytes = 50; message.Header.MessageKind = MessageKind.GetCoinResponse; dispatcher.AddConnection(client1); dispatcher.AddConnection(client2); dispatcher.HandleNewConnection(); dispatcher.HandleNewConnection(); // Write messages into dispatcher buffer for both clients for (var i = 0; i < Constants.SocketSendBufferSize / Constants.MaxRequestSize; i++) { socket1.ExpectConnected(() => true); socket2.ExpectConnected(() => true); Assert.True(dispatcherInbox.TryWrite(firstMessage)); Assert.True(dispatcherInbox.TryWrite(secondMessage)); // Try sending the answers, fails dispatcher.HandleRequest(); dispatcher.HandleRequest(); } // Try sending message to first client, socket gets closed socket1.ExpectConnected(() => true); socket1.ExpectClose(); // Try sending message to second client, socket gets closed socket2.ExpectConnected(() => true); socket2.ExpectClose(); Assert.True(dispatcherInbox.TryWrite(thirdMessage)); Assert.True(dispatcherInbox.TryWrite(fourthMessage)); dispatcher.HandleRequest(); dispatcher.HandleRequest(); socket1.ExpectAllDone(); socket2.ExpectAllDone(); } }
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); } }