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); }
/// <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); }
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)); }
public void Length() { var stream = new Substream(); ExceptionAssert.Throws <NotSupportedException>(() => { stream.SetLength(0); }); ExceptionAssert.Throws <NotSupportedException>(() => { var _ = stream.Length; }); }
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); }
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()); }
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); }
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; }); }
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; }); }
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); }
/// <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); }
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); }
/// <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(); } }