public async Task StartAsync(Uri url, IDuplexPipe application, TransferMode requestedTransferMode, IConnection connection) { if (url == null) { throw new ArgumentNullException(nameof(url)); } if (application == null) { throw new ArgumentNullException(nameof(application)); } if (requestedTransferMode != TransferMode.Binary && requestedTransferMode != TransferMode.Text) { throw new ArgumentException("Invalid transfer mode.", nameof(requestedTransferMode)); } _application = application; Mode = requestedTransferMode; _logger.StartTransport(Mode.Value); await Connect(url); var sendTask = SendMessages(); var receiveTask = ReceiveMessages(); // TODO: Handle TCP connection errors // https://github.com/SignalR/SignalR/blob/1fba14fa3437e24c204dfaf8a18db3fce8acad3c/src/Microsoft.AspNet.SignalR.Core/Owin/WebSockets/WebSocketHandler.cs#L248-L251 Running = Task.WhenAll(sendTask, receiveTask).ContinueWith(t => { _webSocket.Dispose(); _logger.TransportStopped(t.Exception?.InnerException); _application.Output.Complete(t.Exception?.InnerException); _application.Input.Complete(); return(t); }).Unwrap(); }
public async Task StartAsync(Uri url, TransferFormat transferFormat) { if (url == null) { throw new ArgumentNullException(nameof(url)); } if (transferFormat != TransferFormat.Binary && transferFormat != TransferFormat.Text) { throw new ArgumentException($"The '{transferFormat}' transfer format is not supported by this transport.", nameof(transferFormat)); } _webSocketMessageType = transferFormat == TransferFormat.Binary ? WebSocketMessageType.Binary : WebSocketMessageType.Text; var resolvedUrl = ResolveWebSocketsUrl(url); Log.StartTransport(_logger, transferFormat, resolvedUrl); if (_accessTokenFactory != null) { var accessToken = await _accessTokenFactory(); _webSocket.Options.SetRequestHeader("Authorization", $"Bearer {accessToken}"); } await _webSocket.ConnectAsync(resolvedUrl, CancellationToken.None); // Create the pipe pair (Application's writer is connected to Transport's reader, and vice versa) var options = ClientPipeOptions.DefaultOptions; var pair = DuplexPipe.CreateConnectionPair(options, options); _transport = pair.Transport; _application = pair.Application; // TODO: Handle TCP connection errors // https://github.com/SignalR/SignalR/blob/1fba14fa3437e24c204dfaf8a18db3fce8acad3c/src/Microsoft.AspNet.SignalR.Core/Owin/WebSockets/WebSocketHandler.cs#L248-L251 Running = ProcessSocketAsync(_webSocket); }
private async Task ProcessEventStream(string url, IDuplexPipe application, CancellationToken transportCtsToken) { Log.StartReceive(_logger); try { // Creates a task to represent the SSE js processing TaskCompletionSource <object> task = new TaskCompletionSource <object>(); _jsTask = task; // Create connection ((IJSInProcessRuntime)JSRuntime.Current).Invoke <object>( "BlazorSignalR.ServerSentEventsTransport.CreateConnection", url, new DotNetObjectRef(this)); // If canceled, stop fake processing transportCtsToken.Register(() => { task.SetCanceled(); }); // Wait until js side stops await task.Task; if (task.Task.IsCanceled) { Log.ReceiveCanceled(_logger); } } catch (Exception ex) { _logger.LogDebug($"SSE JS Side error {ex.Message}"); _error = ex; } finally { _application.Output.Complete(_error); Log.ReceiveStopped(_logger); // Close JS side SSE CloseSSE(); } }
public async Task ConvertUplink(IDuplexPipe client, IDuplexPipe server) { using var up = cryptoParameter.GetCrypto(); var pmp = new ProtocolMessagePipe(server); var salt = new SaltMessage(16, true); await pmp.WriteAsync(salt); var key = new byte[cryptoParameter.KeySize]; HKDF.DeriveKey(HashAlgorithmName.SHA1, mainKey, key, salt.Salt.Span, _ssSubKeyInfo); up.Init(key, null); Memory <byte> nonce = new byte[cryptoParameter.NonceSize]; nonce.Span.Fill(0); // TODO write salt with data while (true) { var result = await client.Input.ReadAsync(); if (result.IsCanceled || result.IsCompleted) { return; } // TODO compress into one chunk when possible foreach (var item in result.Buffer) { foreach (var i in SplitBigChunk(item)) { await pmp.WriteAsync(new AeadBlockMessage(up, nonce, cryptoParameter) { // in send routine, Data is readonly Data = MemoryMarshal.AsMemory(i), }); } } client.Input.AdvanceTo(result.Buffer.End); } }
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); _originalTransport = pair.Transport; Application = pair.Application; Transport = new SocketDuplexPipe(this); InitializeFeatures(); }
public async Task UsePipe_Stream_PropagatesException() { var stream = new MockInterruptedFullDuplexStream(); IDuplexPipe pipe = stream.UsePipe(cancellationToken: this.TimeoutToken); await Assert.ThrowsAsync <IOException>(async() => { while (!this.TimeoutToken.IsCancellationRequested) { var readResult = await pipe.Input.ReadAsync(this.TimeoutToken); pipe.Input.AdvanceTo(readResult.Buffer.End); } }); await Assert.ThrowsAsync <IOException>(async() => { while (!this.TimeoutToken.IsCancellationRequested) { await pipe.Output.WriteAsync(new byte[1], this.TimeoutToken); } }); }
public async Task <bool> IsMyClient(IDuplexPipe pipe) { var result = await pipe.Input.ReadAsync(); pipe.Input.AdvanceTo(result.Buffer.Start); var buffer = result.Buffer; if (buffer.Length < 3) { return(false); } if (buffer.First.Span[0] != 5) { return(false); } if (buffer.First.Span[1] == 0) { return(false); } // ver 5, has auth method return(true); }
internal TService Create( IDuplexPipe pipe, IServiceProvider hostProvidedServices, ServiceActivationOptions serviceActivationOptions, IServiceBroker serviceBroker) { // Register this service broker globally (if it's the first we encounter) so it can be used by other // global services that need it. GlobalServiceBroker.RegisterServiceBroker(serviceBroker); var descriptor = ServiceDescriptors.Instance.GetServiceDescriptorForServiceFactory(typeof(TService)); var serviceHubTraceSource = (TraceSource)hostProvidedServices.GetService(typeof(TraceSource)); var serverConnection = descriptor.WithTraceSource(serviceHubTraceSource).ConstructRpcConnection(pipe); var args = new ServiceConstructionArguments(hostProvidedServices, serviceBroker); var service = CreateService(args, descriptor, serverConnection, serviceActivationOptions.ClientRpcTarget); serverConnection.AddLocalRpcTarget(service); serverConnection.StartListening(); return(service); }
public WebSocketsTransport(WebSocketOptions options, IDuplexPipe application, HttpConnectionContext connection, ILoggerFactory loggerFactory) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (application == null) { throw new ArgumentNullException(nameof(application)); } if (loggerFactory == null) { throw new ArgumentNullException(nameof(loggerFactory)); } _options = options; _application = application; _connection = connection; _logger = loggerFactory.CreateLogger <WebSocketsTransport>(); }
public async Task StartAsync(Uri url, TransferFormat transferFormat) { if (transferFormat != TransferFormat.Text) { throw new ArgumentException($"The '{transferFormat}' transfer format is not supported by this transport.", nameof(transferFormat)); } Log.StartTransport(_logger, transferFormat); var request = new HttpRequestMessage(HttpMethod.Get, url); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream")); HttpResponseMessage response = null; try { response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None); response.EnsureSuccessStatusCode(); } catch { response?.Dispose(); Log.TransportStopping(_logger); throw; } // Create the pipe pair (Application's writer is connected to Transport's reader, and vice versa) var options = ClientPipeOptions.DefaultOptions; var pair = DuplexPipe.CreateConnectionPair(options, options); _transport = pair.Transport; _application = pair.Application; Running = ProcessAsync(url, response); }
public async Task StartAsync(Uri url, TransferFormat transferFormat) { if (url == null) { throw new ArgumentNullException(nameof(url)); } if (transferFormat != TransferFormat.Binary && transferFormat != TransferFormat.Text) { throw new ArgumentException( $"The '{transferFormat}' transfer format is not supported by this transport.", nameof(transferFormat)); } Log.StartTransport(_logger, transferFormat); // Create connection _startTask = new TaskCompletionSource <object>(); await _jsRuntime.InvokeAsync <object>( "BlazorSignalR.WebSocketsTransport.CreateConnection", url.ToString(), transferFormat == TransferFormat.Binary, new DotNetObjectRef(this)); await _startTask.Task; _startTask = null; Log.StartedTransport(_logger); // Create the pipe pair (Application's writer is connected to Transport's reader, and vice versa) PipeOptions options = ClientPipeOptions.DefaultOptions; DuplexPipe.DuplexPipePair pair = DuplexPipe.CreateConnectionPair(options, options); _transport = pair.Transport; _application = pair.Application; Running = ProcessSocketAsync(); }
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(); }
/// <inheritdoc /> public void Disconnect() { using var scope = this.logger.BeginScope(this.remoteEndPoint); if (this.disconnected) { this.logger.LogDebug("Connection already disconnected."); return; } this.logger.LogDebug("Disconnecting..."); if (this.duplexPipe != null) { this.Source.Complete(); this.Output.Complete(); (this.duplexPipe as IDisposable)?.Dispose(); this.duplexPipe = null; } this.logger.LogDebug("Disconnected"); this.disconnected = true; this.Disconnected?.Invoke(this, System.EventArgs.Empty); }
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 ConnectAsync() { switch (_endPoint) { case IPEndPoint ipEndPoint: await _client.ConnectAsync(ipEndPoint.Address, ipEndPoint.Port); break; case DnsEndPoint dnsEndPoint: await _client.ConnectAsync(dnsEndPoint.Host, dnsEndPoint.Port); break; default: throw new NotSupportedException($"Unsupported endpoint type: {_endPoint.GetType().FullName}"); } // Start processing _pipe = _client.GetStream().CreatePipe(); _ = ReceiveLoop(_pipe.Input); }
public async Task UsePipe_Stream_OneDirectionDoesNotDispose(bool completeOutput) { var ms = new HalfDuplexStream(); IDuplexPipe pipe = ms.UsePipe(cancellationToken: this.TimeoutToken); if (completeOutput) { pipe.Output.Complete(); } else { pipe.Input.Complete(); } var timeout = ExpectedTimeoutToken; while (!ms.IsDisposed && !timeout.IsCancellationRequested) { await Task.Yield(); } Assert.False(ms.IsDisposed); }
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; }
private static async Task PongServer(IDuplexPipe connection) { ArraySegment <byte> _ping = new ArraySegment <byte>(Encoding.ASCII.GetBytes("PING")); ArraySegment <byte> _pong = new ArraySegment <byte>(Encoding.ASCII.GetBytes("PING")); while (true) { var result = await connection.Input.ReadAsync(); var inputBuffer = result.Buffer; if (inputBuffer.IsEmpty && result.IsCompleted) { connection.Input.AdvanceTo(inputBuffer.End); break; } if (inputBuffer.Length < 4) { connection.Input.AdvanceTo(inputBuffer.Start, inputBuffer.End); } else { bool isPing = inputBuffer.EqualsTo(_ping); if (isPing) { await connection.Output.WriteAsync(_pong); } else { break; } connection.Input.AdvanceTo(inputBuffer.End); } } }
public async Task AcceptWritablePipe(IDuplexPipe content, int lengthToWrite, CancellationToken cancellationToken) { // Assert that the pipe is not readable. ReadResult readResult = await content.Input.ReadAsync(cancellationToken); Assert.Equal(0, readResult.Buffer.Length); Assert.True(readResult.IsCompleted); const int ChunkSize = 5; int writtenBytes = 0; while (writtenBytes < lengthToWrite) { // Write in small chunks to verify that it needn't be written all at once. int bytesToWrite = Math.Min(lengthToWrite - writtenBytes, ChunkSize); await content.Output.WriteAsync(MemoryBuffer.AsMemory(writtenBytes, bytesToWrite), cancellationToken); await content.Output.FlushAsync(cancellationToken); writtenBytes += bytesToWrite; } content.Output.Complete(); }
public async Task WrappedStream_Success() { var bytesA = Encoding.ASCII.GetBytes("foo"); var bytesB = Encoding.ASCII.GetBytes("bar"); var stream = new MemoryStream(); stream.Write(bytesA); stream.Position = 0; var con = new MockConnection(); con.OnCreateStream = () => stream; IDuplexPipe pipe = con.Pipe; ReadResult res = await pipe.Input.ReadAsync(); Assert.Equal(bytesA, res.Buffer.ToArray()); await pipe.Output.WriteAsync(bytesB); Assert.Equal(bytesA.Concat(bytesB).ToArray(), stream.ToArray()); }
public WebSocketsServerTransport(WebSocketOptions options, IDuplexPipe application, HttpConnectionContext connection, ILoggerFactory loggerFactory) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (application == null) { throw new ArgumentNullException(nameof(application)); } if (loggerFactory == null) { throw new ArgumentNullException(nameof(loggerFactory)); } _options = options; _application = application; _connection = connection; // We create the logger with a string to preserve the logging namespace after the server side transport renames. _logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport"); }
public Task StartAsync(Uri url, TransferFormat transferFormat, CancellationToken cancellationToken) { if (transferFormat != TransferFormat.Text) { throw new ArgumentException( $"The '{transferFormat}' transfer format is not supported by this transport.", nameof(transferFormat)); } Log.StartTransport(_logger, transferFormat); // Create pipe PipeOptions options = ClientPipeOptions.DefaultOptions; DuplexPipe.DuplexPipePair pair = DuplexPipe.CreateConnectionPair(options, options); _transport = pair.Transport; _application = pair.Application; // Start streams Running = ProcessAsync(url, default); return(Task.CompletedTask); }
/// <inheritdoc /> public void Disconnect() { using (ThreadContext.Stacks["Connection"].Push(this.ToString())) { if (this.disconnected) { this.log.Debug("Connection already disconnected."); return; } this.log.Debug("Disconnecting..."); if (this.duplexPipe != null) { this.Source.Complete(); (this.duplexPipe as IDisposable)?.Dispose(); this.duplexPipe = null; } this.log.Debug("Disconnected"); this.disconnected = true; this.Disconnected?.Invoke(this, System.EventArgs.Empty); } }
private async Task Echo(IDuplexPipe connection) { while (true) { var result = await connection.Input.ReadAsync(); var request = result.Buffer; if (request.IsEmpty && result.IsCompleted) { connection.Input.AdvanceTo(request.End); break; } var response = connection.Output; foreach (var memory in request) { response.Write(memory.Span); } await response.FlushAsync(); connection.Input.AdvanceTo(request.End); } }
public Task StartAsync(Uri url, IDuplexPipe application, TransferFormat transferFormat, IConnection connection) { _application = application; tries++; Assert.True(QueryHelpers.ParseQuery(url.Query.ToString()).TryGetValue("id", out var id)); if (prevConnectionId == null) { prevConnectionId = id; } else { Assert.True(prevConnectionId != id); prevConnectionId = id; } if (tries < 3) { throw new Exception(); } else { return(Task.CompletedTask); } }
public SslDuplexPipe(IDuplexPipe transport, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions, Func <Stream, SslStream> factory) : base(transport, readerOptions, writerOptions, factory) { }
public SslDuplexPipe(IDuplexPipe transport, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions) : this(transport, readerOptions, writerOptions, s => new SslStream(s)) { }
internal ClientConnection(IDuplexPipe transport, EndPoint remoteEndPoint) { Transport = transport; RemoteEndPoint = remoteEndPoint; }
public MockConnectionContext(IDuplexPipe transport) { _transport = transport; }
public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application) { Transport = transport; Application = application; }