예제 #1
0
        private async Task<NegotiationResponse> Negotiate(Uri url, HttpClient httpClient, ILogger logger)
        {
            try
            {
                // Get a connection ID from the server
                logger.EstablishingConnection(url);
                var urlBuilder = new UriBuilder(url);
                if (!urlBuilder.Path.EndsWith("/"))
                {
                    urlBuilder.Path += "/";
                }
                urlBuilder.Path += "negotiate";

                using (var request = new HttpRequestMessage(HttpMethod.Post, urlBuilder.Uri))
                {
                    SendUtils.PrepareHttpRequest(request, _httpOptions);

                    using (var response = await httpClient.SendAsync(request))
                    {
                        response.EnsureSuccessStatusCode();
                        return await ParseNegotiateResponse(response, logger);
                    }
                }
            }
            catch (Exception ex)
            {
                logger.ErrorWithNegotiation(url, ex);
                throw;
            }
        }
        public Task StartAsync(Uri url, IDuplexPipe application, TransferFormat transferFormat, IConnection connection)
        {
            if (transferFormat != TransferFormat.Text)
            {
                throw new ArgumentException($"The '{transferFormat}' transfer format is not supported by this transport.", nameof(transferFormat));
            }

            _application = application;

            Log.StartTransport(_logger, transferFormat);

            var startTcs    = new TaskCompletionSource <object>(TaskContinuationOptions.RunContinuationsAsynchronously);
            var sendTask    = SendUtils.SendMessages(url, _application, _httpClient, _httpOptions, _transportCts, _logger);
            var receiveTask = OpenConnection(_application, url, startTcs, _transportCts.Token);

            Running = Task.WhenAll(sendTask, receiveTask).ContinueWith(t =>
            {
                Log.TransportStopped(_logger, t.Exception?.InnerException);
                _application.Output.Complete(t.Exception?.InnerException);
                _application.Input.Complete();

                return(t);
            }).Unwrap();

            return(startTcs.Task);
        }
예제 #3
0
        public Task StartAsync(Uri url, IDuplexPipe application, TransferMode requestedTransferMode, IConnection connection)
        {
            if (requestedTransferMode != TransferMode.Binary && requestedTransferMode != TransferMode.Text)
            {
                throw new ArgumentException("Invalid transfer mode.", nameof(requestedTransferMode));
            }

            connection.Features.Set <IConnectionInherentKeepAliveFeature>(new ConnectionInherentKeepAliveFeature(_httpClient.Timeout));

            _application = application;
            Mode         = requestedTransferMode;

            Log.StartTransport(_logger, Mode.Value);

            // Start sending and polling (ask for binary if the server supports it)
            _poller = Poll(url, _transportCts.Token);
            _sender = SendUtils.SendMessages(url, _application, _httpClient, _httpOptions, _transportCts, _logger);

            Running = Task.WhenAll(_sender, _poller).ContinueWith(t =>
            {
                Log.TransportStopped(_logger, t.Exception?.InnerException);
                _application.Output.Complete(t.Exception?.InnerException);
                _application.Input.Complete();
                return(t);
            }).Unwrap();

            return(Task.CompletedTask);
        }
예제 #4
0
        public Task StartAsync(Uri url, Channel <byte[], SendMessage> application, TransferMode requestedTransferMode, string connectionId)
        {
            if (requestedTransferMode != TransferMode.Binary && requestedTransferMode != TransferMode.Text)
            {
                throw new ArgumentException("Invalid transfer mode.", nameof(requestedTransferMode));
            }

            _application  = application;
            Mode          = requestedTransferMode;
            _connectionId = connectionId;

            _logger.StartTransport(_connectionId, Mode.Value);

            // Start sending and polling (ask for binary if the server supports it)
            _poller = Poll(url, _transportCts.Token);
            _sender = SendUtils.SendMessages(url, _application, _httpClient, _transportCts, _logger, _connectionId);

            Running = Task.WhenAll(_sender, _poller).ContinueWith(t =>
            {
                _logger.TransportStopped(_connectionId, t.Exception?.InnerException);
                _application.Out.TryComplete(t.IsFaulted ? t.Exception.InnerException : null);
                return(t);
            }).Unwrap();

            return(Task.CompletedTask);
        }
예제 #5
0
        private async Task <NegotiationResponse> Negotiate(Uri url, HttpClient httpClient, ILogger logger)
        {
            try
            {
                // Get a connection ID from the server
                Log.EstablishingConnection(logger, url);
                var urlBuilder = new UriBuilder(url);
                if (!urlBuilder.Path.EndsWith("/"))
                {
                    urlBuilder.Path += "/";
                }
                urlBuilder.Path += "negotiate";

                using (var request = new HttpRequestMessage(HttpMethod.Post, urlBuilder.Uri))
                {
                    // Corefx changed the default version and High Sierra curlhandler tries to upgrade request
                    request.Version = new Version(1, 1);
                    SendUtils.PrepareHttpRequest(request, _httpOptions);

                    using (var response = await httpClient.SendAsync(request))
                    {
                        response.EnsureSuccessStatusCode();
                        return(await ParseNegotiateResponse(response, logger));
                    }
                }
            }
            catch (Exception ex)
            {
                Log.ErrorWithNegotiation(logger, url, ex);
                throw;
            }
        }
        public Task StartAsync(Uri url, Channel <byte[], SendMessage> application, TransferMode requestedTransferMode, IConnection connection)
        {
            if (requestedTransferMode != TransferMode.Binary && requestedTransferMode != TransferMode.Text)
            {
                throw new ArgumentException("Invalid transfer mode.", nameof(requestedTransferMode));
            }

            _application = application;
            Mode         = TransferMode.Text; // Server Sent Events is a text only transport

            _logger.StartTransport(Mode.Value);

            var sendTask    = SendUtils.SendMessages(url, _application, _httpClient, _httpOptions, _transportCts, _logger);
            var receiveTask = OpenConnection(_application, url, _transportCts.Token);

            Running = Task.WhenAll(sendTask, receiveTask).ContinueWith(t =>
            {
                _logger.TransportStopped(t.Exception?.InnerException);

                _application.Writer.TryComplete(t.IsFaulted ? t.Exception.InnerException : null);
                return(t);
            }).Unwrap();

            return(Task.CompletedTask);
        }
        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);
                }
            }
        }
예제 #8
0
        private async Task Poll(Uri pollUrl, CancellationToken cancellationToken)
        {
            Log.StartReceive(_logger);
            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    var request = new HttpRequestMessage(HttpMethod.Get, pollUrl);
                    SendUtils.PrepareHttpRequest(request, _httpOptions);

                    HttpResponseMessage response;

                    try
                    {
                        response = await _httpClient.SendAsync(request, cancellationToken);
                    }
                    catch (OperationCanceledException)
                    {
                        // SendAsync will throw the OperationCanceledException if the passed cancellationToken is canceled
                        // or if the http request times out due to HttpClient.Timeout expiring. In the latter case we
                        // just want to start a new poll.
                        continue;
                    }

                    response.EnsureSuccessStatusCode();

                    if (response.StatusCode == HttpStatusCode.NoContent || cancellationToken.IsCancellationRequested)
                    {
                        Log.ClosingConnection(_logger);

                        // Transport closed or polling stopped, we're done
                        break;
                    }
                    else
                    {
                        Log.ReceivedMessages(_logger);

                        var stream = new PipeWriterStream(_application.Output);
                        await response.Content.CopyToAsync(stream);

                        await _application.Output.FlushAsync();
                    }
                }
            }
            catch (OperationCanceledException)
            {
                // transport is being closed
                Log.ReceiveCanceled(_logger);
            }
            catch (Exception ex)
            {
                Log.ErrorPolling(_logger, pollUrl, ex);
                throw;
            }
            finally
            {
                // Make sure the send loop is terminated
                _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();
            }
        }
예제 #10
0
        private async Task Poll(Uri pollUrl, CancellationToken cancellationToken)
        {
            _logger.StartReceive(_connectionId);
            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    var request = new HttpRequestMessage(HttpMethod.Get, pollUrl);
                    SendUtils.PrepareHttpRequest(request, _httpOptions);

                    HttpResponseMessage response;

                    try
                    {
                        response = await _httpClient.SendAsync(request, cancellationToken);
                    }
                    catch (OperationCanceledException)
                    {
                        // SendAsync will throw the OperationCanceledException if the passed cancellationToken is canceled
                        // or if the http request times out due to HttpClient.Timeout expiring. In the latter case we
                        // just want to start a new poll.
                        continue;
                    }

                    response.EnsureSuccessStatusCode();

                    if (response.StatusCode == HttpStatusCode.NoContent || cancellationToken.IsCancellationRequested)
                    {
                        _logger.ClosingConnection(_connectionId);

                        // Transport closed or polling stopped, we're done
                        break;
                    }
                    else
                    {
                        _logger.ReceivedMessages(_connectionId);

                        // Until Pipeline starts natively supporting BytesReader, this is the easiest way to do this.
                        var payload = await response.Content.ReadAsByteArrayAsync();

                        if (payload.Length > 0)
                        {
                            while (!_application.Writer.TryWrite(payload))
                            {
                                if (cancellationToken.IsCancellationRequested || !await _application.Writer.WaitToWriteAsync(cancellationToken))
                                {
                                    return;
                                }
                            }
                        }
                    }
                }
            }
            catch (OperationCanceledException)
            {
                // transport is being closed
                _logger.ReceiveCanceled(_connectionId);
            }
            catch (Exception ex)
            {
                _logger.ErrorPolling(_connectionId, pollUrl, ex);
                throw;
            }
            finally
            {
                // Make sure the send loop is terminated
                _transportCts.Cancel();
                _logger.ReceiveStopped(_connectionId);
            }
        }