Ejemplo n.º 1
0
        public async Task Writing()
        {
            var ms    = new MemoryStream();
            var muxer = new Muxer {
                Channel = ms
            };
            var stream = new Substream {
                Muxer = muxer
            };
            var m1 = new byte[1];

            stream.AddData(new byte[] { 10 });
            Assert.IsTrue(stream.CanRead);
            Assert.IsTrue(stream.CanWrite);

            Assert.AreEqual(1, await stream.ReadAsync(m1, 0, 1));
            await stream.WriteAsync(m1, 0, 1);

            stream.WriteByte(11);
            await stream.FlushAsync();

            ms.Position = 0;
            var header = await Header.ReadAsync(ms);

            var length = await Varint.ReadVarint32Async(ms);

            var payload = new byte[length];

            ms.Read(payload, 0, length);
            Assert.AreEqual(stream.Id, header.StreamId);
            Assert.AreEqual(2, payload.Length);
            CollectionAssert.AreEqual(new byte[] { 10, 11 }, payload);
        }
Ejemplo n.º 2
0
        /// <summary>
        ///   Creates a new stream with the specified name.
        /// </summary>
        /// <param name="name">
        ///   A name for the stream.
        /// </param>
        /// <param name="cancel">
        ///   Is used to stop the task.  When cancelled, the <see cref="TaskCanceledException"/> is raised.
        /// </param>
        /// <returns>
        ///   A duplex stream.
        /// </returns>
        public async Task <Substream> CreateStreamAsync(string name = "", CancellationToken cancel = default(CancellationToken))
        {
            var streamId = NextStreamId;

            NextStreamId += 2;
            var substream = new Substream
            {
                Id              = streamId,
                Name            = name,
                Muxer           = this,
                SentMessageType = PacketType.MessageInitiator,
            };

            Substreams.TryAdd(streamId, substream);

            // Tell the other side about the new stream.
            using (await AcquireWriteAccessAsync().ConfigureAwait(false))
            {
                var header = new Header {
                    StreamId = streamId, PacketType = PacketType.NewStream
                };
                var wireName = Encoding.UTF8.GetBytes(name);
                await header.WriteAsync(Channel, cancel).ConfigureAwait(false);

                await Channel.WriteVarintAsync(wireName.Length, cancel).ConfigureAwait(false);

                await Channel.WriteAsync(wireName, 0, wireName.Length).ConfigureAwait(false);

                await Channel.FlushAsync().ConfigureAwait(false);
            }
            return(substream);
        }
Ejemplo n.º 3
0
        public async Task Reading_ClosedStream()
        {
            var m1     = new byte[10];
            var stream = new Substream();

            stream.NoMoreData();
            Assert.AreEqual(0, await stream.ReadAsync(m1, 0, 10));
        }
Ejemplo n.º 4
0
        public void Length()
        {
            var stream = new Substream();

            ExceptionAssert.Throws <NotSupportedException>(() => {
                stream.SetLength(0);
            });
            ExceptionAssert.Throws <NotSupportedException>(() => {
                var _ = stream.Length;
            });
        }
Ejemplo n.º 5
0
        public void Disposable()
        {
            var s = new Substream();

            Assert.IsTrue(s.CanRead);
            Assert.IsTrue(s.CanWrite);

            s.Dispose();
            Assert.IsFalse(s.CanRead);
            Assert.IsFalse(s.CanWrite);
        }
Ejemplo n.º 6
0
        public void Reading_Empty()
        {
            var m1     = new byte[0];
            var stream = new Substream();
            var _      = Task.Run(async() =>
            {
                await Task.Delay(100);
                stream.NoMoreData();
            });

            Assert.AreEqual(-1, stream.ReadByte());
        }
Ejemplo n.º 7
0
        public async Task Reading_Partial()
        {
            var m1     = new byte[] { 1, 2, 3, 4 };
            var m2     = new byte[m1.Length];
            var stream = new Substream();

            stream.AddData(m1);
            stream.NoMoreData();

            Assert.AreEqual(4, await stream.ReadAsync(m2, 0, 5));
            CollectionAssert.AreEqual(m1, m2);

            Assert.AreEqual(-1, stream.ReadByte());
            Assert.IsFalse(stream.CanRead);
        }
Ejemplo n.º 8
0
        public void Seeking()
        {
            var stream = new Substream();

            Assert.IsFalse(stream.CanSeek);
            ExceptionAssert.Throws <NotSupportedException>(() => {
                stream.Seek(0, SeekOrigin.Begin);
            });
            ExceptionAssert.Throws <NotSupportedException>(() => {
                stream.Position = 0;
            });
            ExceptionAssert.Throws <NotSupportedException>(() => {
                var _ = stream.Position;
            });
        }
Ejemplo n.º 9
0
        public void Timeout()
        {
            var stream = new Substream();

            Assert.IsFalse(stream.CanTimeout);
            ExceptionAssert.Throws <InvalidOperationException>(() => {
                stream.ReadTimeout = 0;
            });
            ExceptionAssert.Throws <InvalidOperationException>(() => {
                var _ = stream.ReadTimeout;
            });
            ExceptionAssert.Throws <InvalidOperationException>(() => {
                stream.WriteTimeout = 0;
            });
            ExceptionAssert.Throws <InvalidOperationException>(() => {
                var _ = stream.WriteTimeout;
            });
        }
Ejemplo n.º 10
0
        public async Task Reading()
        {
            var m1     = new byte[] { 1, 2, 3, 4 };
            var m2     = new byte[m1.Length];
            var stream = new Substream();

            stream.AddData(new byte[] { 1, 2 });
            stream.AddData(new byte[] { 3, 4 });
            stream.NoMoreData();
            Assert.IsTrue(stream.CanRead);

            m2[0] = (byte)stream.ReadByte();
            Assert.AreEqual(1, stream.Read(m2, 1, 1));
            Assert.AreEqual(2, await stream.ReadAsync(m2, 2, 2));
            CollectionAssert.AreEqual(m1, m2);

            Assert.AreEqual(-1, stream.ReadByte());
            Assert.IsFalse(stream.CanRead);
        }
Ejemplo n.º 11
0
        /// <summary>
        ///   Remove the stream.
        /// </summary>
        /// <remarks>
        ///   Internal method called by Substream.Dispose().
        /// </remarks>
        public async Task <Substream> RemoveStreamAsync(Substream stream, CancellationToken cancel = default(CancellationToken))
        {
            if (Substreams.TryRemove(stream.Id, out Substream _))
            {
                // Tell the other side.
                using (await AcquireWriteAccessAsync().ConfigureAwait(false))
                {
                    var header = new Header
                    {
                        StreamId   = stream.Id,
                        PacketType = PacketType.CloseInitiator
                    };
                    await header.WriteAsync(Channel, cancel).ConfigureAwait(false);

                    Channel.WriteByte(0); // length
                    await Channel.FlushAsync().ConfigureAwait(false);
                }
            }

            return(stream);
        }
Ejemplo n.º 12
0
        public async Task Reading_Delayed_Partial()
        {
            var m1     = new byte[] { 1, 2, 3, 4 };
            var m2     = new byte[m1.Length];
            var stream = new Substream();

#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            Task.Run(async() =>
            {
                await Task.Delay(100);
                stream.AddData(new byte[] { 1, 2 });
                await Task.Delay(100);
                stream.AddData(new byte[] { 3, 4 });
                await Task.Delay(100);
                stream.NoMoreData();
            });
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed

            Assert.AreEqual(4, await stream.ReadAsync(m2, 0, 5));
            CollectionAssert.AreEqual(m1, m2);
        }
Ejemplo n.º 13
0
        /// <summary>
        ///   Read the multiplex packets.
        /// </summary>
        /// <param name="cancel"></param>
        /// <returns></returns>
        /// <remarks>
        ///   A background task that reads and processes the multiplex packets while
        ///   the <see cref="Channel"/> is open and not <paramref name="cancel">cancelled</paramref>.
        ///   <para>
        ///   Any encountered errors will close the <see cref="Channel"/>.
        ///   </para>
        /// </remarks>
        public async Task ProcessRequestsAsync(CancellationToken cancel = default(CancellationToken))
        {
            try
            {
                while (Channel.CanRead && !cancel.IsCancellationRequested)
                {
                    // Read the packet prefix.
                    var header = await Header.ReadAsync(Channel, cancel).ConfigureAwait(false);

                    var length = await Varint.ReadVarint32Async(Channel, cancel).ConfigureAwait(false);

                    if (log.IsTraceEnabled)
                    {
                        log.TraceFormat("received '{0}', stream={1}, length={2}", header.PacketType, header.StreamId, length);
                    }

                    // Read the payload.
                    var payload = new byte[length];
                    int offset  = 0;
                    while (offset < length)
                    {
                        offset += await Channel.ReadAsync(payload, offset, length - offset, cancel).ConfigureAwait(false);
                    }

                    // Process the packet
                    Substreams.TryGetValue(header.StreamId, out Substream substream);
                    switch (header.PacketType)
                    {
                    case PacketType.NewStream:
                        if (substream != null)
                        {
                            log.Warn($"Stream {substream.Id} already exists");
                            continue;
                        }
                        substream = new Substream
                        {
                            Id    = header.StreamId,
                            Name  = Encoding.UTF8.GetString(payload),
                            Muxer = this
                        };
                        if (!Substreams.TryAdd(substream.Id, substream))
                        {
                            // Should not happen.
                            throw new Exception($"Stream {substream.Id} already exists");
                        }
                        SubstreamCreated?.Invoke(this, substream);

                        // Special hack for go-ipfs
#if true
                        if (Receiver && (substream.Id & 1) == 1)
                        {
                            log.Debug($"go-hack sending newstream {substream.Id}");
                            using (await AcquireWriteAccessAsync().ConfigureAwait(false))
                            {
                                var hdr = new Header
                                {
                                    StreamId   = substream.Id,
                                    PacketType = PacketType.NewStream
                                };
                                await hdr.WriteAsync(Channel, cancel).ConfigureAwait(false);

                                Channel.WriteByte(0);     // length
                                await Channel.FlushAsync().ConfigureAwait(false);
                            }
                        }
#endif
                        break;

                    case PacketType.MessageInitiator:
                        if (substream == null)
                        {
                            log.Warn($"Message to unknown stream #{header.StreamId}");
                            continue;
                        }
                        substream.AddData(payload);
                        break;

                    case PacketType.MessageReceiver:
                        if (substream == null)
                        {
                            log.Warn($"Message to unknown stream #{header.StreamId}");
                            continue;
                        }
                        substream.AddData(payload);
                        break;

                    case PacketType.CloseInitiator:
                    case PacketType.CloseReceiver:
                    case PacketType.ResetInitiator:
                    case PacketType.ResetReceiver:
                        if (substream == null)
                        {
                            log.Warn($"Reset of unknown stream #{header.StreamId}");
                            continue;
                        }
                        substream.NoMoreData();
                        Substreams.TryRemove(substream.Id, out Substream _);
                        SubstreamClosed?.Invoke(this, substream);
                        break;

                    default:
                        throw new InvalidDataException($"Unknown Muxer packet type '{header.PacketType}'.");
                    }
                }
            }
            catch (EndOfStreamException)
            {
                // eat it
            }
            catch (IOException)
            {
                // eat it
            }
            catch (SocketException e) when(e.SocketErrorCode == SocketError.ConnectionReset)
            {
                // eat it
            }
            catch (Exception) when(cancel.IsCancellationRequested)
            {
                // eat it
            }
            catch (Exception e)
            {
                // Log error if the channel is not closed.
                if (Channel.CanRead || Channel.CanWrite)
                {
                    log.Error("failed", e);
                }
            }

            // Some of the tests do not pass a connection.
            if (Connection != null)
            {
                Connection.Dispose();
            }
            else if (Channel != null)
            {
                Channel.Dispose();
            }

            // Dispose of all the substreams.
            var streams = Substreams.Values.ToArray();
            Substreams.Clear();
            foreach (var stream in streams)
            {
                stream.Dispose();
            }
        }