/// <summary> /// Opens the <see cref="StreamingConnection"/> and listens for incoming requests, which will /// be assembled and sent to the provided <see cref="RequestHandler"/>. /// </summary> /// <param name="requestHandler"><see cref="RequestHandler"/> to which incoming requests will be sent.</param> /// <param name="cancellationToken"><see cref="CancellationToken"/> that signals the need to stop the connection. /// Once the token is cancelled, the connection will be gracefully shut down, finishing pending sends and receives.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> public virtual async Task ListenAsync(RequestHandler requestHandler, CancellationToken cancellationToken = default) { if (requestHandler == null) { throw new ArgumentNullException(nameof(requestHandler)); } var duplexPipePair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); // Create transport and application _transport = CreateStreamingTransport(duplexPipePair.Application); var application = new TransportHandler(duplexPipePair.Transport, Logger); // Create session _session = new StreamingSession(requestHandler, application, Logger, cancellationToken); // Start transport and application var transportTask = _transport.ConnectAsync(cancellationToken); var applicationTask = application.ListenAsync(cancellationToken); var tasks = new List <Task> { transportTask, applicationTask }; // Signal that session is ready to be used _sessionInitializedTask.SetResult(true); // Let application and transport run await Task.WhenAll(tasks).ConfigureAwait(false); }
private async Task ListenImplAsync(Func <WebSocketTransport, Task> socketConnectFunc, RequestHandler requestHandler, CancellationToken cancellationToken = default(CancellationToken)) { var duplexPipePair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); // Create transport and application var transport = new WebSocketTransport(duplexPipePair.Application, _logger); var application = new TransportHandler(duplexPipePair.Transport, _logger); // Create session _session = new StreamingSession(requestHandler, application, _logger, cancellationToken); // Start transport and application var transportTask = socketConnectFunc(transport); var applicationTask = application.ListenAsync(cancellationToken); var tasks = new List <Task>() { transportTask, applicationTask }; // Signal that session is ready to be used _sessionInitializedTask.SetResult(true); // Let application and transport run await Task.WhenAll(tasks).ConfigureAwait(false); }
public void StreamingSession_Receivestream_ParameterValidation(Header header, byte[] payload, Type exceptionType) { // Arrange var transportHandler = new Mock <TransportHandler>(new Mock <IDuplexPipe>().Object, NullLogger.Instance); var session = new StreamingSession(new Mock <RequestHandler>().Object, transportHandler.Object, NullLogger.Instance); // Act + Assert Assert.Throws(exceptionType, () => session.ReceiveStream(header, new ArraySegment <byte>(payload))); }
public void StreamingSession_ReceiveResponse_ParameterValidation(Header header, ReceiveResponse response, Type exceptionType) { // Arrange var transportHandler = new Mock <TransportHandler>(new Mock <IDuplexPipe>().Object, NullLogger.Instance); var session = new StreamingSession(new Mock <RequestHandler>().Object, transportHandler.Object, NullLogger.Instance); // Act + Assert Assert.Throws(exceptionType, () => session.ReceiveResponse(header, response)); }
public async Task StreamingSession_SendResponse_ParameterValidation(Header header, StreamingResponse response, Type exceptionType) { // Arrange var transportHandler = new Mock <TransportHandler>(new Mock <IDuplexPipe>().Object, NullLogger.Instance); var session = new StreamingSession(new Mock <RequestHandler>().Object, transportHandler.Object, NullLogger.Instance); // Act + Assert await Assert.ThrowsAsync(exceptionType, () => session.SendResponseAsync(header, response, CancellationToken.None)); }
public async Task StreamingSession_SendRequest_ParameterValidation() { // Arrange var transportHandler = new Mock <TransportHandler>(new Mock <IDuplexPipe>().Object, NullLogger.Instance); var session = new StreamingSession(new Mock <RequestHandler>().Object, transportHandler.Object, NullLogger.Instance); // Act + Assert await Assert.ThrowsAsync <ArgumentNullException>(() => session.SendRequestAsync(null, CancellationToken.None)); }
/// <summary> /// Starts or continues previous media straiming session. /// </summary> /// <param name="userId">User identifier</param> /// <param name="ip">Ip address</param> /// <returns>Streaming session info object, containing playing media and milliseconds playing at.</returns> public SessionInfo StartSession(Guid userId, string ip) { var session = (from ss in _dataContext.Get <StreamingSession>() where ss.UserId == userId select ss).FirstOrDefault(); if (session == null) { session = new StreamingSession { DateStarted = DateTime.UtcNow, IsActive = true, StateInfoJson = string.Empty, UserId = userId, UserIp = ip }; _dataContext.Add(session); _dataContext.SaveChanges(); } else if (!session.IsActive) { session.IsActive = true; session.DateStarted = DateTime.UtcNow; session.DateFinished = null; _dataContext.Update(session); _dataContext.SaveChanges(); } return(new SessionInfo { SessionKey = session.Id.ToString(), PlayingMediaId = session.PlayingMediaId?.ToString() ?? string.Empty, PlayingAtMSec = (session.PlayingMediaId.HasValue ? session.PlayingAtMSec : 0) }); }
public async Task Get2() { Response.StatusCode = 206; Response.ContentType = "multipart/x-mixed-replace; boundary=frame"; Response.Headers.Add("Connection", "Keep-Alive"); StreamingSession session = this.cam.StreamOn(data => { if (Request.HttpContext.RequestAborted.IsCancellationRequested) { throw new Exception(); } Response.Body.Write(this.CreateHeader(data.Length)); Response.Body.Write(data); Response.Body.Write(this.CreateFooter()); Response.Body.Flush(); }); await Response.StartAsync(); await session.WaitAsync(); }
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); }
public async Task StreamingSession_SendRequest_ReceiveResponse(int streamLength, int streamCount, int chunkCount) { // Arrange var request = new StreamingRequest() { Verb = "GET", Path = "api/version", Streams = new List <ResponseMessageStream>() }; request.AddStream(new StringContent("Hello human, I'm Bender!")); var requestHandler = new Mock <RequestHandler>(); var requestCompletionSource = new TaskCompletionSource <bool>(); requestHandler .Setup(r => r.ProcessRequestAsync(It.IsAny <ReceiveRequest>(), null, null, CancellationToken.None)) .ReturnsAsync(() => new StreamingResponse() { StatusCode = 200 }) .Callback(() => requestCompletionSource.SetResult(true)); var transportHandler = new Mock <TransportHandler>(new Mock <IDuplexPipe>().Object, NullLogger.Instance); var responseCompletionSource = new TaskCompletionSource <bool>(); var transportHandlerSetup = transportHandler.Setup(t => t.SendRequestAsync(It.IsAny <Guid>(), It.IsAny <RequestModel>(), CancellationToken.None)); var session = new StreamingSession(requestHandler.Object, transportHandler.Object, NullLogger.Instance); Header responseHeader = null; ReceiveResponse response = null; transportHandlerSetup.Callback( (Guid requestId, RequestModel requestPayload, CancellationToken cancellationToken) => { responseHeader = new Header() { Id = requestId, Type = PayloadTypes.Response }; response = new ReceiveResponse() { StatusCode = 200, Streams = StreamingDataGenerator.CreateStreams(requestId, streamLength, streamCount, chunkCount, PayloadTypes.Response) }; session.ReceiveResponse(responseHeader, response); foreach (AugmentedStreamDefinition definition in response.Streams) { var chunkList = definition.Chunks; for (int i = 0; i < chunkCount; i++) { bool isLast = i == chunkCount - 1; session.ReceiveStream( new Header() { End = isLast, Id = definition.Id, PayloadLength = chunkList[i].Length, Type = PayloadTypes.Stream }, chunkList[i]); } } }); // Act var responseTask = session.SendRequestAsync(request, CancellationToken.None); var responseWithTimeout = await Task.WhenAny(responseTask, Task.Delay(TimeSpan.FromSeconds(5))); // Assert Assert.Equal(responseTask, responseWithTimeout); var receivedResponse = await responseTask; Assert.Equal(response.StatusCode, receivedResponse.StatusCode); Assert.Equal(response.Streams.Count, receivedResponse.Streams.Count); Assert.True(response.Streams.SequenceEqual(receivedResponse.Streams)); }
public async Task StreamingSession_RequestWithStreams_SentToHandler(int streamLength, int streamCount, int chunkCount) { // Arrange var requestId = Guid.NewGuid(); var request = new ReceiveRequest() { Verb = "GET", Path = "api/version", Streams = new List <IContentStream>() }; request.Streams = StreamingDataGenerator.CreateStreams(requestId, streamLength, streamCount, chunkCount); var requestHandler = new Mock <RequestHandler>(); var requestCompletionSource = new TaskCompletionSource <bool>(); requestHandler .Setup(r => r.ProcessRequestAsync(It.IsAny <ReceiveRequest>(), null, null, CancellationToken.None)) .ReturnsAsync(() => new StreamingResponse() { StatusCode = 200 }) .Callback(() => requestCompletionSource.SetResult(true)); var transportHandler = new Mock <TransportHandler>(new Mock <IDuplexPipe>().Object, NullLogger.Instance); var responseCompletionSource = new TaskCompletionSource <bool>(); transportHandler .Setup(t => t.SendResponseAsync(It.IsAny <Guid>(), It.Is <ResponseModel>(r => r.StatusCode == 200), CancellationToken.None)) .Callback(() => responseCompletionSource.SetResult(true)); // Act var session = new StreamingSession(requestHandler.Object, transportHandler.Object, NullLogger.Instance); session.ReceiveRequest(new Header() { Id = requestId, Type = PayloadTypes.Request }, request); foreach (AugmentedStreamDefinition definition in request.Streams) { var chunkList = definition.Chunks; for (int i = 0; i < chunkList.Count; i++) { bool isLast = i == chunkList.Count - 1; session.ReceiveStream( new Header() { End = isLast, Id = definition.Id, PayloadLength = chunkList[i].Length, Type = PayloadTypes.Stream }, chunkList[i]); } } var roundtripTask = Task.WhenAll(requestCompletionSource.Task, responseCompletionSource.Task); var result = await Task.WhenAny(roundtripTask, Task.Delay(TimeSpan.FromSeconds(5))); // Assert Assert.Equal(result, roundtripTask); }