Ejemplo n.º 1
0
        /// <summary>
        /// Attempt to flush any blocks to <see cref="CoapClient"/> that have been queued up.
        /// </summary>
        /// <inheritdoc/>
        public override async Task FlushAsync(CancellationToken cancellationToken)
        {
            _writerEvent.Set();

            await FlushFinishedEvent.WaitAsync(cancellationToken);

            ThrowExceptionIfCaught();
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Attempt to flush any blocks to <see cref="CoapClient"/> that have been queued up.
        /// </summary>
        /// <inheritdoc/>
        public override void Flush()
        {
            if (CaughtException == null && !_writerTask.IsCompleted)
            {
                _writerEvent.Set();
                FlushFinishedEvent.WaitAsync(CancellationToken.None).Wait();
            }

            ThrowExceptionIfCaught();
        }
Ejemplo n.º 3
0
        /// <inheritdoc/>
        public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            if (EndOfStream)
            {
                throw new EndOfStreamException("Stream ended before all bytes were written", CaughtException);
            }

            // Lets artificailly block while the writer task has blocks to write.
            if (_writer.Length > BlockSize)
            {
                await FlushFinishedEvent.WaitAsync(cancellationToken);
            }

            _writer.Enqueue(buffer, offset, count);
            _writerEvent.Set();
        }
Ejemplo n.º 4
0
        private async Task WriteBlocksAsync()
        {
            var token = CancellationTokenSource.Token;

            try
            {
                while (!token.IsCancellationRequested)
                {
                    await _writerEvent.WaitAsync(token);

                    while (_writer.Length > BlockSize || (EndOfStream && _writer.Length > 0))
                    {
                        var message = Context.Request.Clone();

                        // Reset the message Id so it's set by CoapClient
                        message.Id = 0;

                        message.Options.Add(new Options.Block1(_writeBlockNumber, BlockSizeInternal, _writer.Length > BlockSizeInternal));

                        message.Payload = new byte[_writer.Length < BlockSizeInternal ? _writer.Length : BlockSizeInternal];
                        _writer.Peek(message.Payload, 0, BlockSizeInternal);

                        Context.MessageId = await Context.Client.SendAsync(message, token);

                        // Keep the response in the queue in case the Applciation needs it.
                        var result = await Context.Client.GetResponseAsync(Context.MessageId, token);

                        if (EndOfStream)
                        {
                            Context.Response = result;
                        }

                        if (result.Code.IsSuccess())
                        {
                            _writer.AdvanceQueue(message.Payload.Length);
                            _writeBlockNumber++;

                            var block      = result.Options.Get <Options.Block1>();
                            var blockDelta = block.BlockSize - BlockSizeInternal;

                            // Only update the size if it's smaller
                            if (blockDelta < 0)
                            {
                                BlockSizeInternal += blockDelta;
                                _writeBlockNumber -= blockDelta / BlockSizeInternal;
                            }
                            else if (blockDelta > 0)
                            {
                                throw new CoapBlockException($"Remote endpoint requested to increase blocksize from {BlockSizeInternal} to {BlockSizeInternal + blockDelta}");
                            }
                        }
                        else if (result.Code.IsClientError() || result.Code.IsServerError())
                        {
                            if (_writeBlockNumber == 0 && result.Code == CoapMessageCode.RequestEntityTooLarge && BlockSizeInternal > 16)
                            {
                                // Try again and attempt at sending a smaller block size.
                                _writeBlockNumber  = 0;
                                BlockSizeInternal /= 2;

                                continue;
                            }

                            Context.Response = result;
                            throw new CoapBlockException($"Failed to send block ({_writeBlockNumber}) to remote endpoint", CoapException.FromCoapMessage(result), result.Code);
                        }
                    }

                    // flag the mot recent flush has been performed
                    if (_writer.Length <= BlockSize)
                    {
                        FlushFinishedEvent.Set();
                    }

                    if (EndOfStream)
                    {
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                // Hold onto the exception to throw it from a synchronous call.
                CaughtException = ex;
            }
            finally
            {
                EndOfStream = true;
                FlushFinishedEvent.Set();
            }
        }