public async Task SSEWritesMessages() { using (StartVerifiableLog()) { var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, new PipeOptions(readerScheduler: PipeScheduler.Inline)); var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application); var context = new DefaultHttpContext(); var ms = new MemoryStream(); context.Response.Body = ms; var sse = new ServerSentEventsServerTransport(connection.Application.Input, connectionId: string.Empty, LoggerFactory); var task = sse.ProcessRequestAsync(context, context.RequestAborted); await connection.Transport.Output.WriteAsync(Encoding.ASCII.GetBytes("Hello")); connection.Transport.Output.Complete(); await task.OrTimeout(); Assert.Equal(":\r\ndata: Hello\r\n\r\n", Encoding.ASCII.GetString(ms.ToArray())); } }
public async Task ClientJoinedPacketSent() { var manager = new ChatRoomManager(); var roomId = manager.CreateChatRoom(); var room = manager.GetChatRoom(roomId); var duplexPipe1 = new DuplexPipe(); var connection1 = new Connection(duplexPipe1, null, null); var client1 = new ChatClient(connection1, manager); room.RegisterClient(new ChatServerAuthenticationInfo(room.GetNextClientIndex(), roomId, "Bob", "128450673")); room.RegisterClient(new ChatServerAuthenticationInfo(room.GetNextClientIndex(), roomId, "Alice", "94371960")); var authenticationPacket1 = new byte[] { 0xC1, 0x10, 0x00, 0x00, (byte)roomId, (byte)(roomId >> 8), 0xCD, 0xFD, 0x93, 0xC8, 0xFA, 0x9B, 0xCA, 0xF8, 0x98, 0xFC }; await duplexPipe1.ReceivePipe.Writer.WriteAsync(authenticationPacket1); await duplexPipe1.ReceivePipe.Writer.FlushAsync(); var duplexPipe2 = new DuplexPipe(); var connection2 = new Connection(duplexPipe2, null, null); var client2 = new ChatClient(connection2, manager); var authenticationPacket2 = new byte[] { 0xC1, 0x10, 0x00, 0x00, (byte)roomId, (byte)(roomId >> 8), 0xC5, 0xFB, 0x98, 0xCB, 0xFE, 0x92, 0xCA, 0xFF, 0xAB, 0xFC }; await duplexPipe2.ReceivePipe.Writer.WriteAsync(authenticationPacket2); await duplexPipe2.ReceivePipe.Writer.FlushAsync(); var expectedPacket = new byte[] { 0xC1, 0x0F, 0x01, 0x00, 0x01, 0x41, 0x6C, 0x69, 0x63, 0x65, 0, 0, 0, 0, 0, }; var readResult = await duplexPipe1.SendPipe.Reader.ReadAsync(); var result = readResult.Buffer.ToArray().TakeLast(expectedPacket.Length).ToArray(); Assert.That(result, Is.EquivalentTo(expectedPacket)); Assert.That(client1.Nickname, Is.EqualTo("Bob")); Assert.That(client2.Nickname, Is.EqualTo("Alice")); }
public async Task TransportFailsOnTimeoutWithErrorWhenApplicationFailsAndClientDoesNotSendCloseFrame() { using (StartVerifiableLog()) { var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); var connection = new HttpConnectionContext("foo", connectionToken: null, LoggerFactory.CreateLogger(nameof(HttpConnectionContext))) { Transport = pair.Transport, Application = pair.Application, }; using (var feature = new TestWebSocketConnectionFeature()) { var options = new WebSocketOptions { CloseTimeout = TimeSpan.FromSeconds(1) }; var connectionContext = new HttpConnectionContext(string.Empty, null, null); var ws = new WebSocketsServerTransport(options, connection.Application, connectionContext, LoggerFactory); var serverSocket = await feature.AcceptAsync(); // Give the server socket to the transport and run it var transport = ws.ProcessSocketAsync(serverSocket); // Run the client socket var client = feature.Client.ExecuteAndCaptureFramesAsync(); // fail the client to server channel connection.Transport.Output.Complete(new Exception()); await transport.OrTimeout(); Assert.Equal(WebSocketState.Aborted, serverSocket.State); } } }
public async Task SSETransportStopsWithErrorIfServerSendsIncompleteResults() { var mockHttpHandler = new Mock <HttpMessageHandler>(); mockHttpHandler.Protected() .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>()) .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) => { await Task.Yield(); var mockStream = new Mock <Stream>(); mockStream .Setup(s => s.CopyToAsync(It.IsAny <Stream>(), It.IsAny <int>(), It.IsAny <CancellationToken>())) .Returns <Stream, int, CancellationToken>(async(stream, bufferSize, t) => { var buffer = Encoding.ASCII.GetBytes("data: 3:a"); await stream.WriteAsync(buffer, 0, buffer.Length); }); mockStream.Setup(s => s.CanRead).Returns(true); return(new HttpResponseMessage { Content = new StreamContent(mockStream.Object) }); }); using (var httpClient = new HttpClient(mockHttpHandler.Object)) { var sseTransport = new ServerSentEventsTransport(httpClient); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); await sseTransport.StartAsync( new Uri("http://fakeuri.org"), pair.Application, TransferMode.Text, connection : Mock.Of <IConnection>()).OrTimeout(); var exception = await Assert.ThrowsAsync <FormatException>(() => sseTransport.Running.OrTimeout()); Assert.Equal("Incomplete message.", exception.Message); } }
public async Task ServerGracefullyClosesWhenClientSendsCloseFrameThenApplicationEnds() { using (StartLog(out var loggerFactory, LogLevel.Debug)) { var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application); using (var feature = new TestWebSocketConnectionFeature()) { var options = new WebSocketOptions { // We want to verify behavior without timeout affecting it CloseTimeout = TimeSpan.FromSeconds(20) }; var connectionContext = new DefaultConnectionContext(string.Empty, null, null); var ws = new WebSocketsTransport(options, connection.Application, connectionContext, loggerFactory); var serverSocket = await feature.AcceptAsync(); // Give the server socket to the transport and run it var transport = ws.ProcessSocketAsync(serverSocket); // Run the client socket var client = feature.Client.ExecuteAndCaptureFramesAsync(); await feature.Client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None).OrTimeout(); // close the client to server channel connection.Transport.Output.Complete(); _ = await client.OrTimeout(); await transport.OrTimeout(); Assert.Equal(WebSocketCloseStatus.NormalClosure, serverSocket.CloseStatus); } } }
public async Task TransportClosesOnCloseTimeoutIfClientDoesNotSendCloseFrame() { using (StartVerifiableLog()) { var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); var connection = new HttpConnectionContext("foo", connectionToken: null, LoggerFactory.CreateLogger(nameof(HttpConnectionContext))) { Transport = pair.Transport, Application = pair.Application, }; using (var feature = new TestWebSocketConnectionFeature()) { var options = new WebSocketOptions() { CloseTimeout = TimeSpan.FromSeconds(1) }; var connectionContext = new HttpConnectionContext(string.Empty, null, null); var ws = new WebSocketsServerTransport(options, connection.Application, connectionContext, LoggerFactory); var serverSocket = await feature.AcceptAsync(); // Give the server socket to the transport and run it var transport = ws.ProcessSocketAsync(serverSocket); // End the app connection.Transport.Output.Complete(); await transport.OrTimeout(TimeSpan.FromSeconds(10)); // Now we're closed Assert.Equal(WebSocketState.Aborted, serverSocket.State); serverSocket.Dispose(); } } }
public void GlobalSetup() { var serviceCollection = new ServiceCollection(); serviceCollection.AddSignalRCore(); var provider = serviceCollection.BuildServiceProvider(); var serviceScopeFactory = provider.GetService<IServiceScopeFactory>(); _dispatcher = new DefaultHubDispatcher<TestHub>( serviceScopeFactory, new HubContext<TestHub>(new DefaultHubLifetimeManager<TestHub>(NullLogger<DefaultHubLifetimeManager<TestHub>>.Instance)), Options.Create(new HubOptions<TestHub>()), Options.Create(new HubOptions()), new Logger<DefaultHubDispatcher<TestHub>>(NullLoggerFactory.Instance)); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); var connection = new DefaultConnectionContext(Guid.NewGuid().ToString(), pair.Application, pair.Transport); _connectionContext = new NoErrorHubConnectionContext(connection, TimeSpan.Zero, NullLoggerFactory.Instance); _connectionContext.Protocol = new FakeHubProtocol(); }
public HttpConnectionTests() { _memoryPool = KestrelMemoryPool.Create(); var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); _httpConnectionContext = new HttpConnectionContext { ConnectionId = "0123456789", ConnectionAdapters = new List <IConnectionAdapter>(), ConnectionFeatures = new FeatureCollection(), MemoryPool = _memoryPool, HttpConnectionId = long.MinValue, Application = pair.Application, Transport = pair.Transport, ServiceContext = new TestServiceContext { SystemClock = new SystemClock() } }; _httpConnection = new HttpConnection(_httpConnectionContext); }
public HttpProtocolFeatureCollectionTests() { _memoryPool = KestrelMemoryPool.Create(); var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); _transport = pair.Transport; _application = pair.Application; _serviceContext = new TestServiceContext(); _http1ConnectionContext = new HttpConnectionContext { ServiceContext = _serviceContext, ConnectionFeatures = new FeatureCollection(), MemoryPool = _memoryPool, TimeoutControl = Mock.Of <ITimeoutControl>(), Transport = pair.Transport }; _http1Connection = new TestHttp1Connection(_http1ConnectionContext); _http1Connection.Reset(); _collection = _http1Connection; }
public async Task SSEWritesVeryLargeMessages() { var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, new PipeOptions(readerScheduler: PipeScheduler.Inline)); var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application); var context = new DefaultHttpContext(); var ms = new MemoryStream(); context.Response.Body = ms; var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: string.Empty, loggerFactory: new LoggerFactory()); var task = sse.ProcessRequestAsync(context, context.RequestAborted); string hText = new string('H', 60000); string wText = new string('W', 60000); await connection.Transport.Output.WriteAsync(Encoding.ASCII.GetBytes(hText + wText)); connection.Transport.Output.Complete(); await task.OrTimeout(); Assert.Equal(":\r\ndata: " + hText + wText + "\r\n\r\n", Encoding.ASCII.GetString(ms.ToArray())); }
private async ValueTask TestException(Action <InvalidPacketHeaderException> check) { bool thrown = false; var duplexPipe = new DuplexPipe(); using var connection = new Connection(duplexPipe, null, new Xor.PipelinedXor32Encryptor(duplexPipe.Output), new NullLogger <Connection>()); _ = connection.BeginReceive(); try { await duplexPipe.ReceivePipe.Writer.WriteAsync(this.malformedData); } catch (InvalidPacketHeaderException e) { thrown = true; check(e); } catch (Exception e) { Assert.Fail($"Wrong exception type {e}", e); } Assert.That(thrown); }
public void InitialDictionaryIsEmpty() { using (var memoryPool = PinnedBlockMemoryPoolFactory.Create()) { var options = new PipeOptions(memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); var http1ConnectionContext = TestContextFactory.CreateHttpConnectionContext( serviceContext: new TestServiceContext(), connectionContext: Mock.Of <ConnectionContext>(), transport: pair.Transport, memoryPool: memoryPool, connectionFeatures: new FeatureCollection()); var http1Connection = new Http1Connection(http1ConnectionContext); http1Connection.Reset(); IDictionary <string, StringValues> headers = http1Connection.ResponseHeaders; Assert.Equal(0, headers.Count); Assert.False(headers.IsReadOnly); } }
public Http1ConnectionTests() { _pipelineFactory = new MemoryPool(); var pair = DuplexPipe.CreateConnectionPair(_pipelineFactory); _transport = pair.Transport; _application = pair.Application; _serviceContext = new TestServiceContext(); _timeoutControl = new Mock <ITimeoutControl>(); _http1ConnectionContext = new Http1ConnectionContext { ServiceContext = _serviceContext, ConnectionFeatures = new FeatureCollection(), MemoryPool = _pipelineFactory, TimeoutControl = _timeoutControl.Object, Application = pair.Application, Transport = pair.Transport }; _http1Connection = new TestHttp1Connection(_http1ConnectionContext); _http1Connection.Reset(); }
public async Task WebSocketTransportSetsMessageTypeBasedOnTransferFormatFeature(TransferFormat transferFormat, WebSocketMessageType expectedMessageType) { using (StartLog(out var loggerFactory, LogLevel.Debug)) { var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application); using (var feature = new TestWebSocketConnectionFeature()) { var connectionContext = new DefaultConnectionContext(string.Empty, null, null); connectionContext.ActiveFormat = transferFormat; var ws = new WebSocketsTransport(new WebSocketOptions(), connection.Application, connectionContext, loggerFactory); // Give the server socket to the transport and run it var transport = ws.ProcessSocketAsync(await feature.AcceptAsync()); // Run the client socket var client = feature.Client.ExecuteAndCaptureFramesAsync(); // Write to the output channel, and then complete it await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello")); connection.Transport.Output.Complete(); // The client should finish now, as should the server var clientSummary = await client; await feature.Client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); await transport; Assert.Equal(1, clientSummary.Received.Count); Assert.True(clientSummary.Received[0].EndOfMessage); Assert.Equal(expectedMessageType, clientSummary.Received[0].MessageType); Assert.Equal("Hello", Encoding.UTF8.GetString(clientSummary.Received[0].Buffer)); } } }
public PipeSocket(Socket socket, MemoryPool <byte> memoryPool, PipeScheduler scheduler, long?maxReadBufferSize = null, long?maxWriteBufferSize = null, bool waitForData = true) { _socket = socket; MemoryPool = memoryPool; _waitForData = waitForData; // On *nix platforms, Sockets already dispatches to the ThreadPool. // Yes, the IOQueues are still used for the PipeSchedulers. This is intentional. // https://github.com/aspnet/KestrelHttpServer/issues/2573 // var awaiterScheduler = IsWindows ? scheduler : PipeScheduler.Inline; maxReadBufferSize ??= 0; maxWriteBufferSize ??= 0; var inputOptions = new PipeOptions(MemoryPool, PipeScheduler.ThreadPool, scheduler, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false); var outputOptions = new PipeOptions(MemoryPool, scheduler, PipeScheduler.ThreadPool, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions); Transport = pair.Transport; Application = pair.Application; var awaiterScheduler = IsWindows ? scheduler : PipeScheduler.Inline; _receiver = new SocketReceiver(_socket, awaiterScheduler); _sender = new SocketSender(_socket, awaiterScheduler); maxReadBufferSize ??= 0; maxWriteBufferSize ??= 0; }
public async Task ExistingPipe_Send_Accept_Recv_Send() { var packets = new byte[][] { new byte[] { 1, 2, 3 }, new byte[] { 4, 5, 6 }, new byte[] { 7, 8, 9 }, }; var channel2OutboundPipe = new Pipe(); var channel2InboundPipe = new Pipe(); var channel2Stream = new DuplexPipe(channel2InboundPipe.Reader, channel2OutboundPipe.Writer).AsStream(); var channel2Options = new MultiplexingStream.ChannelOptions { ExistingPipe = new DuplexPipe(channel2OutboundPipe.Reader, channel2InboundPipe.Writer) }; // Create the channel and transmit before it is accepted. var channel1 = this.mx1.CreateChannel(); var channel1Stream = channel1.AsStream(); await channel1Stream.WriteAsync(packets[0], 0, packets[0].Length, this.TimeoutToken); await channel1Stream.FlushAsync(this.TimeoutToken); // Accept the channel and read the bytes await this.WaitForEphemeralChannelOfferToPropagate(); var channel2 = this.mx2.AcceptChannel(channel1.Id, channel2Options); await this.VerifyReceivedDataAsync(channel2Stream, packets[0]); // Verify we can transmit via the ExistingPipe. await this.TransmitAndVerifyAsync(channel2Stream, channel1Stream, packets[1]); // Verify we can receive more bytes via the ExistingPipe. // MANUALLY VERIFY with debugger that received bytes are read DIRECTLY into channel2InboundPipe.Writer (no intermediary buffer copying). await this.TransmitAndVerifyAsync(channel1Stream, channel2Stream, packets[2]); }
internal SocketConnection(Socket socket, MemoryPool <byte> memoryPool, PipeScheduler transportScheduler, ISocketsTrace trace, SocketSenderPool socketSenderPool, PipeOptions inputOptions, PipeOptions outputOptions, bool waitForData = true) { Debug.Assert(socket != null); Debug.Assert(memoryPool != null); Debug.Assert(trace != null); _socket = socket; MemoryPool = memoryPool; _trace = trace; _waitForData = waitForData; _socketSenderPool = socketSenderPool; LocalEndPoint = _socket.LocalEndPoint; RemoteEndPoint = _socket.RemoteEndPoint; ConnectionClosed = _connectionClosedTokenSource.Token; // On *nix platforms, Sockets already dispatches to the ThreadPool. // Yes, the IOQueues are still used for the PipeSchedulers. This is intentional. // https://github.com/aspnet/KestrelHttpServer/issues/2573 var awaiterScheduler = OperatingSystem.IsWindows() ? transportScheduler : PipeScheduler.Inline; _receiver = new SocketReceiver(awaiterScheduler); var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions); // Set the transport and connection id Transport = _originalTransport = pair.Transport; Application = pair.Application; }
public async Task LongPollingTransportStopsWhenSendRequestFails() { var mockHttpHandler = new Mock <HttpMessageHandler>(); mockHttpHandler.Protected() .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>()) .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) => { await Task.Yield(); var statusCode = request.Method == HttpMethod.Post ? HttpStatusCode.InternalServerError : HttpStatusCode.OK; return(ResponseUtils.CreateResponse(statusCode)); }); using (var httpClient = new HttpClient(mockHttpHandler.Object)) { var longPollingTransport = new LongPollingTransport(httpClient); try { var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), pair.Application, TransferFormat.Binary, connection : new TestConnection()); await pair.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello World")); await Assert.ThrowsAsync <HttpRequestException>(async() => await longPollingTransport.Running.OrTimeout()); var exception = await Assert.ThrowsAsync <HttpRequestException>(async() => await pair.Transport.Input.ReadAllAsync().OrTimeout()); Assert.Contains(" 500 ", exception.Message); } finally { await longPollingTransport.StopAsync(); } } }
public HttpProtocolFeatureCollection() { var memoryPool = PinnedBlockMemoryPoolFactory.Create(); var options = new PipeOptions(memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); var serviceContext = TestContextFactory.CreateServiceContext( serverOptions: new KestrelServerOptions(), httpParser: new HttpParser <Http1ParsingHandler>(), dateHeaderValueManager: new DateHeaderValueManager()); var connectionContext = TestContextFactory.CreateHttpConnectionContext( serviceContext: serviceContext, connectionContext: null, transport: pair.Transport, memoryPool: memoryPool, connectionFeatures: new FeatureCollection()); var http1Connection = new Http1Connection(connectionContext); http1Connection.Reset(); _collection = http1Connection; }
public async Task SSETransportDoesNotSupportBinary() { var mockHttpHandler = new Mock <HttpMessageHandler>(); mockHttpHandler.Protected() .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>()) .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) => { await Task.Yield(); return(new HttpResponseMessage { Content = new StringContent(string.Empty) }); }); using (var httpClient = new HttpClient(mockHttpHandler.Object)) { var sseTransport = new ServerSentEventsTransport(httpClient); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); var ex = await Assert.ThrowsAsync <ArgumentException>(() => sseTransport.StartAsync(new Uri("http://fakeuri.org"), pair.Application, TransferFormat.Binary, connection : Mock.Of <IConnection>()).OrTimeout()); Assert.Equal($"The 'Binary' transfer format is not supported by this transport.{Environment.NewLine}Parameter name: transferFormat", ex.Message); } }
public virtual void GlobalSetup() { _memoryPool = PinnedBlockMemoryPoolFactory.Create(); _httpFrame = new Http2Frame(); var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); _connectionPair = DuplexPipe.CreateConnectionPair(options, options); _httpRequestHeaders = new HttpRequestHeaders(); _httpRequestHeaders[HeaderNames.Method] = new StringValues("GET"); _httpRequestHeaders[HeaderNames.Path] = new StringValues("/"); _httpRequestHeaders[HeaderNames.Scheme] = new StringValues("http"); _httpRequestHeaders[HeaderNames.Authority] = new StringValues("localhost:80"); _headersBuffer = new byte[1024 * 16]; _hpackEncoder = new DynamicHPackEncoder(); var serviceContext = TestContextFactory.CreateServiceContext( serverOptions: new KestrelServerOptions(), dateHeaderValueManager: new DateHeaderValueManager(), systemClock: new MockSystemClock()); serviceContext.DateHeaderValueManager.OnHeartbeat(default);
public void Setup() { var memoryPool = SlabMemoryPoolFactory.Create(); var options = new PipeOptions(memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); var serviceContext = TestContextFactory.CreateServiceContext( serverOptions: new KestrelServerOptions(), httpParser: new HttpParser <Http1ParsingHandler>()); var connectionContext = TestContextFactory.CreateHttpConnectionContext( serviceContext: serviceContext, connectionContext: null, transport: pair.Transport, timeoutControl: new TimeoutControl(timeoutHandler: null), memoryPool: memoryPool, connectionFeatures: new FeatureCollection()); var http1Connection = new Http1Connection(connectionContext); http1Connection.Reset(); Connection = http1Connection; }
public async Task ReaderOnly() { var pipe = new Pipe(); var duplex = new DuplexPipe(pipe.Reader); // Our assert strategy is to verify our no-op writer behaves the same way as a completed writer. pipe.Writer.Complete(); Assert.Throws <InvalidOperationException>(() => pipe.Writer.GetMemory()); Assert.Throws <InvalidOperationException>(() => duplex.Output.GetMemory()); Assert.Throws <InvalidOperationException>(() => pipe.Writer.GetSpan()); Assert.Throws <InvalidOperationException>(() => duplex.Output.GetSpan()); // System.IO.Pipelines stopped throwing when Advance(0) is called after completion, // But we still feel it's useful to throw since it's a read-only pipe. pipe.Writer.Advance(0); Assert.Throws <InvalidOperationException>(() => duplex.Output.Advance(0)); FlushResult flushResult = await pipe.Writer.FlushAsync(); Assert.False(flushResult.IsCompleted); flushResult = await duplex.Output.FlushAsync(); Assert.False(flushResult.IsCompleted); pipe.Writer.CancelPendingFlush(); duplex.Output.CancelPendingFlush(); #pragma warning disable CS0618 // Type or member is obsolete pipe.Writer.OnReaderCompleted((ex, s) => { }, null); duplex.Output.OnReaderCompleted((ex, s) => { }, null); #pragma warning restore CS0618 // Type or member is obsolete pipe.Writer.Complete(); duplex.Output.Complete(); }
public async Task RoomClientListSentAfterJoin() { var manager = new ChatRoomManager(); var roomId = manager.CreateChatRoom(); var room = manager.GetChatRoom(roomId); var duplexPipe = new DuplexPipe(); var connection = new Connection(duplexPipe, null, null); var client = new ChatClient(connection, manager); room.RegisterClient(new ChatServerAuthenticationInfo(room.GetNextClientIndex(), roomId, "Bob", "128450673")); var authenticationPacket = new byte[] { 0xC1, 0x10, 0x00, 0x00, (byte)roomId, (byte)(roomId >> 8), 0xCD, 0xFD, 0x93, 0xC8, 0xFA, 0x9B, 0xCA, 0xF8, 0x98, 0xFC }; duplexPipe.ReceivePipe.Writer.Write(authenticationPacket); await duplexPipe.ReceivePipe.Writer.FlushAsync(); var expectedPacket = new byte[] { 0xC2, 0x00, 0x13, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x42, 0x6F, 0x62, 0, 0, 0, 0, 0, 0, 0 }; var readResult = await duplexPipe.SendPipe.Reader.ReadAsync(); var result = readResult.Buffer.ToArray(); Assert.That(result, Is.EquivalentTo(expectedPacket)); Assert.That(client.Nickname, Is.EqualTo("Bob")); }
internal async Task <(ConnectionLoop, ConnectionLoop)> ConnectTo(TestNode other) { var pipe = DuplexPipe.CreateConnection(PipeOptions.Default, PipeOptions.Default); var(conn, descriptor) = this.CreateConnection(pipe.Application); var(conn2, descriptor2) = other.CreateConnection(pipe.Transport); // manually start outbound connection from node A. var initialSend = PeerManager.NewOutboundConnection(descriptor, other.NodeConfig.KeysManager.GetNodeSecret().PubKey.ToBytes()); await pipe.Application.Output.WriteAsync(initialSend); var flushResult = pipe.Application.Output.FlushAsync(); if (!flushResult.IsCompleted) { await flushResult.ConfigureAwait(false); } // manually start inbound connection for node B. other.PeerManager.NewInboundConnection(descriptor2); conn.Start(); conn2.Start(); return(conn, conn2); }
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 OnConnection(IFeatureCollection features) { var connectionContext = new DefaultConnectionContext(features); var transportFeature = connectionContext.Features.Get <IConnectionTransportFeature>(); // REVIEW: Unfortunately, we still need to use the service context to create the pipes since the settings // for the scheduler and limits are specified here var inputOptions = GetInputPipeOptions(_serviceContext, transportFeature.MemoryPool, transportFeature.InputWriterScheduler); var outputOptions = GetOutputPipeOptions(_serviceContext, transportFeature.MemoryPool, transportFeature.OutputReaderScheduler); var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions); // Set the transport and connection id connectionContext.ConnectionId = CorrelationIdGenerator.GetNextId(); connectionContext.Transport = pair.Transport; // This *must* be set before returning from OnConnection transportFeature.Application = pair.Application; // REVIEW: This task should be tracked by the server for graceful shutdown // Today it's handled specifically for http but not for aribitrary middleware _ = Execute(connectionContext); }
public void Launch(string url, string username) { if (Running) { Destroy(); isClosing = false; } lastUrl = url; lastUsername = username; try{ currentPipe = DuplexPipe.CreateServer(); currentPipe.DataIn += currentPipe_DataIn; if ((currentProcess = Process.Start(new ProcessStartInfo { FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"), Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"", UseShellExecute = false, RedirectStandardOutput = true })) != null) { currentProcess.EnableRaisingEvents = true; currentProcess.Exited += process_Exited; #if DEBUG currentProcess.BeginOutputReadLine(); currentProcess.OutputDataReceived += (sender, args) => Debug.WriteLine("VideoPlayer: " + args.Data); #endif } currentPipe.DisposeToken(); }catch (Exception e) { Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e); } }
public async Task CopyToAsyncDoesNotCopyBlocks() { var writeCount = 0; var writeTcs = new TaskCompletionSource <(byte[], int, int)>(); var mockDestination = new Mock <Stream>() { CallBase = true }; mockDestination .Setup(m => m.WriteAsync(It.IsAny <byte[]>(), It.IsAny <int>(), It.IsAny <int>(), CancellationToken.None)) .Callback((byte[] buffer, int offset, int count, CancellationToken cancellationToken) => { writeTcs.SetResult((buffer, offset, count)); writeCount++; }) .Returns(Task.CompletedTask); using (var memoryPool = KestrelMemoryPool.Create()) { var options = new PipeOptions(pool: memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); var transport = pair.Transport; var application = pair.Application; var http1ConnectionContext = new Http1ConnectionContext { ServiceContext = new TestServiceContext(), ConnectionFeatures = new FeatureCollection(), Application = application, Transport = transport, MemoryPool = memoryPool, TimeoutControl = Mock.Of <ITimeoutControl>() }; var http1Connection = new Http1Connection(http1ConnectionContext) { HasStartedConsumingRequestBody = true }; var headers = new HttpRequestHeaders { HeaderContentLength = "12" }; var body = Http1MessageBody.For(HttpVersion.Http11, headers, http1Connection); var copyToAsyncTask = body.CopyToAsync(mockDestination.Object); var bytes = Encoding.ASCII.GetBytes("Hello "); var buffer = http1Connection.RequestBodyPipe.Writer.GetMemory(2048); ArraySegment <byte> segment; Assert.True(MemoryMarshal.TryGetArray(buffer, out segment)); Buffer.BlockCopy(bytes, 0, segment.Array, segment.Offset, bytes.Length); http1Connection.RequestBodyPipe.Writer.Advance(bytes.Length); await http1Connection.RequestBodyPipe.Writer.FlushAsync(); // Verify the block passed to Stream.WriteAsync() is the same one incoming data was written into. Assert.Equal((segment.Array, segment.Offset, bytes.Length), await writeTcs.Task); // Verify the again when GetMemory returns the tail space of the same block. writeTcs = new TaskCompletionSource <(byte[], int, int)>(); bytes = Encoding.ASCII.GetBytes("World!"); buffer = http1Connection.RequestBodyPipe.Writer.GetMemory(2048); Assert.True(MemoryMarshal.TryGetArray(buffer, out segment)); Buffer.BlockCopy(bytes, 0, segment.Array, segment.Offset, bytes.Length); http1Connection.RequestBodyPipe.Writer.Advance(bytes.Length); await http1Connection.RequestBodyPipe.Writer.FlushAsync(); Assert.Equal((segment.Array, segment.Offset, bytes.Length), await writeTcs.Task); http1Connection.RequestBodyPipe.Writer.Complete(); await copyToAsyncTask; Assert.Equal(2, writeCount); // Don't call body.StopAsync() because PumpAsync() was never called. http1Connection.RequestBodyPipe.Reader.Complete(); } }
internal async Task ConnectInternalAsync(Func <WebSocketTransport, Task> connectFunc, CancellationToken cancellationToken) { CheckDisposed(); TimerAwaitable timer = null; Task timerTask = null; try { // Pipes _duplexPipePair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); // Transport var transport = new WebSocketTransport(_duplexPipePair.Application, _logger); // Application _transportHandler = new TransportHandler(_duplexPipePair.Transport, _logger); // Session _session = new StreamingSession(_requestHandler, _transportHandler, _logger, cancellationToken); // Set up cancellation _disconnectCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); // Start transport and application var transportTask = connectFunc(transport); var applicationTask = _transportHandler.ListenAsync(_disconnectCts.Token); var combinedTask = Task.WhenAll(transportTask, applicationTask); Log.ClientStarted(_logger, _url ?? string.Empty); // Periodic task: keep alive // Disposed with `timer.Stop()` in the finally block below if (_keepAlive.HasValue) { timer = new TimerAwaitable(_keepAlive.Value, _keepAlive.Value); timerTask = TimerLoopAsync(timer); } // We are connected! IsConnected = true; // Block until transport or application ends. await combinedTask.ConfigureAwait(false); // Signal that we're done _disconnectCts.Cancel(); Log.ClientTransportApplicationCompleted(_logger, _url); } finally { timer?.Stop(); if (timerTask != null) { await timerTask.ConfigureAwait(false); } } Log.ClientCompleted(_logger, _url ?? string.Empty); }