private async Task OpenConnection(IDuplexPipe application, Uri url, TaskCompletionSource <object> startTcs, CancellationToken cancellationToken) { Log.StartReceive(_logger); var request = new HttpRequestMessage(HttpMethod.Get, url); SendUtils.PrepareHttpRequest(request, _httpOptions); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream")); HttpResponseMessage response; try { response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); response.EnsureSuccessStatusCode(); startTcs.TrySetResult(null); } catch (Exception ex) { Log.TransportStopping(_logger); startTcs.TrySetException(ex); return; } using (var stream = await response.Content.ReadAsStreamAsync()) { var pipeOptions = new PipeOptions(pauseWriterThreshold: 0, resumeWriterThreshold: 0); var pipelineReader = StreamPipeConnection.CreateReader(pipeOptions, stream); var readCancellationRegistration = cancellationToken.Register( reader => ((PipeReader)reader).CancelPendingRead(), pipelineReader); try { while (true) { var result = await pipelineReader.ReadAsync(); var input = result.Buffer; if (result.IsCanceled || (input.IsEmpty && result.IsCompleted)) { Log.EventStreamEnded(_logger); break; } var consumed = input.Start; var examined = input.End; try { Log.ParsingSSE(_logger, input.Length); var parseResult = _parser.ParseMessage(input, out consumed, out examined, out var buffer); switch (parseResult) { case ServerSentEventsMessageParser.ParseResult.Completed: Log.MessageToApp(_logger, buffer.Length); await _application.Output.WriteAsync(buffer); _parser.Reset(); break; case ServerSentEventsMessageParser.ParseResult.Incomplete: if (result.IsCompleted) { throw new FormatException("Incomplete message."); } break; } } finally { pipelineReader.AdvanceTo(consumed, examined); } } } catch (OperationCanceledException) { Log.ReceiveCanceled(_logger); } finally { readCancellationRegistration.Dispose(); _transportCts.Cancel(); Log.ReceiveStopped(_logger); } } }
private async Task OpenConnection(Channel <byte[], SendMessage> application, Uri url, CancellationToken cancellationToken) { _logger.StartReceive(); var request = new HttpRequestMessage(HttpMethod.Get, url); SendUtils.PrepareHttpRequest(request, _httpOptions); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream")); var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); var stream = await response.Content.ReadAsStreamAsync(); var pipelineReader = StreamPipeConnection.CreateReader(new PipeOptions(_memoryPool), stream); var readCancellationRegistration = cancellationToken.Register( reader => ((IPipeReader)reader).CancelPendingRead(), pipelineReader); try { while (true) { var result = await pipelineReader.ReadAsync(); var input = result.Buffer; if (result.IsCancelled || (input.IsEmpty && result.IsCompleted)) { _logger.EventStreamEnded(); break; } var consumed = input.Start; var examined = input.End; try { var parseResult = _parser.ParseMessage(input, out consumed, out examined, out var buffer); switch (parseResult) { case ServerSentEventsMessageParser.ParseResult.Completed: _application.Writer.TryWrite(buffer); _parser.Reset(); break; case ServerSentEventsMessageParser.ParseResult.Incomplete: if (result.IsCompleted) { throw new FormatException("Incomplete message."); } break; } } finally { pipelineReader.Advance(consumed, examined); } } } catch (OperationCanceledException) { _logger.ReceiveCanceled(); } finally { readCancellationRegistration.Dispose(); _transportCts.Cancel(); stream.Dispose(); _logger.ReceiveStopped(); } }