/// <summary> /// Send consumer, reading messages one by one from stream and writes /// to websocket stream. /// </summary> /// <returns></returns> protected virtual async Task SendConsumerAsync(ICodecStream <S> codec) { if (_lastMessage == null) { _lastMessage = Message.Create(StreamId, _remoteId, PollRequest.Create((ulong)_timeout.TotalMilliseconds)); } while (!_open.IsCancellationRequested) { try { if (_lastMessage == null) { if (_send.Completion.IsCompleted) { // Pipeline closed, close the connection _receive.Complete(); _open.Cancel(); break; } try { _lastMessage = await _send.ReceiveAsync(_timeout, _open.Token).ConfigureAwait(false); if (_lastMessage.Content is DataMessage data) { data.SequenceNumber = _nextSendSequenceNumber++; } } catch (TimeoutException) { _lastMessage = Message.Create(StreamId, _remoteId, PollRequest.Create((ulong)_timeout.TotalMilliseconds)); } catch (OperationCanceledException) when(!_open.IsCancellationRequested) { _lastMessage = Message.Create(StreamId, _remoteId, PollRequest.Create((ulong)_timeout.TotalMilliseconds)); } } await codec.WriteAsync(_lastMessage, _open.Token).ConfigureAwait(false); await codec.Stream.FlushAsync(_open.Token).ConfigureAwait(false); _lastMessage.Dispose(); _lastMessage = null; } catch (OperationCanceledException) { break; } catch (Exception e) { ProxyEventSource.Log.StreamException(this, codec.Stream, e); break; } } }
/// <summary> /// Open stream /// </summary> /// <param name="ct"></param> /// <returns></returns> public Task <IMessageStream> OpenAsync(CancellationToken ct) { CreateReceiveBlock(); CreateSendBlock(); // Start producer receiving from poll _receiveTask = Task.Factory.StartNew(async() => { var sendTimeout = TimeSpan.FromMilliseconds(_pollTimeout * 2); var receiver = new IoTHubInvoker(_hubConnectionString); try { ulong nextSequenceNumber = 0; while (true) { using (var request = Message.Create(_streamId, _remoteId, PollRequest.Create(_pollTimeout, nextSequenceNumber))) { var response = await receiver.TryCallAsync( _link, request, sendTimeout, _open.Token).ConfigureAwait(false); // // Poll receives back a timeout error in case no data was available // within the requested timeout. This is decoupled from the consumers // that time out on their cancellation tokens. // if (response != null && response.Error != (int)SocketError.Timeout) { if (!await _receive.SendAsync(response).ConfigureAwait(false)) { break; } nextSequenceNumber++; } } // Continue polling until closed in which case we complete receive _open.Token.ThrowIfCancellationRequested(); } } catch (OperationCanceledException) { } catch (Exception e) { ProxyEventSource.Log.HandledExceptionAsError(this, e); throw; } finally { receiver.Dispose(); } }, _open.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).Unwrap(); return(Task.FromResult((IMessageStream)this)); }
protected override async Task SendConsumerAsync( ICodecStream <HybridConnectionStream> codec) { while (!_open.IsCancellationRequested) { try { if (_lastMessage == null) { if (_send.Completion.IsCompleted) { // Pipeline closed, close the connection _receive.Complete(); _open.Cancel(); break; } try { _lastMessage = await _send.ReceiveAsync(_timeout, _open.Token).ConfigureAwait(false); var data = _lastMessage.Content as DataMessage; if (data != null) { data.SequenceNumber = _nextSendSequenceNumber++; } } catch (TimeoutException) { _lastMessage = Message.Create(StreamId, _remoteId, PollRequest.Create((ulong)_timeout.TotalMilliseconds)); } catch (OperationCanceledException) when(!_open.IsCancellationRequested) { _lastMessage = Message.Create(StreamId, _remoteId, PollRequest.Create((ulong)_timeout.TotalMilliseconds)); } } // // Every write on the hybrid connection stream right now results in a binary // message not an individual fragment. // when using the codec directly on the stream then the first write confuses the // proxy decoder, which assumes a websocket message contains an entire message. // Override here to buffer the entire message in a memory stream before writing. // using (var mem = new MemoryStream()) { _lastMessage.Encode(mem, CodecId.Mpack); var buffered = mem.ToArray(); await codec.Stream.WriteAsync( buffered, 0, buffered.Length, _open.Token).ConfigureAwait(false); } await codec.Stream.FlushAsync(_open.Token).ConfigureAwait(false); _lastMessage.Dispose(); _lastMessage = null; } catch (OperationCanceledException) { break; } catch (Exception e) { ProxyEventSource.Log.StreamException(this, codec.Stream, e); break; } } }