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 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 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); }
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(); } }