public TransportPacket Assemble() { Debug.Assert(Finished); TransportPacket packet = new TransportPacket(); foreach (TransportPacket frag in fragments) { packet.Append(frag, 0, frag.Length); frag.Dispose(); } fragments = null; return packet; }
public void TestToArray() { TransportPacket packet = new TransportPacket(); packet.Append(new byte[] { 0, 1, 2, 3 }); packet.Append(new byte[] { 4, 5, 6, 7, 8 }); packet.Append(new byte[] { 9 }); byte[] original = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; Assert.AreEqual(original, packet.ToArray()); for (int i = 0; i < packet.Length; i++) { for (int count = 0; count < packet.Length - i; count++) { byte[] sub = packet.ToArray(i, count); for (int j = 0; j < count; j++) { Assert.AreEqual(original[i + j], sub[j]); } } } CheckDisposed(packet); CheckForUndisposedSegments(); }
private void FragmentMessage(Message message, ITransportDeliveryCharacteristics tdc, TransportPacket packet, MarshalledResult mr) { // <item> if the message is the first fragment, then the high-bit is // set on the message-type; the number of fragments is encoded using // the adaptive <see cref="ByteUtils.EncodeLength(int)"/> format. // <pre>[byte:message-type'] [byte:channelId] [uint32:packet-size] // [byte:seqno] [bytes:encoded-#-fragments] [bytes:frag]</pre> // </item> // <item> for all subsequent fragments; seqno' = seqno | 128; // the number of fragments is encoded using the adaptive // <see cref="ByteUtils.EncodeLength(int)"/> format. // <pre>[byte:message-type'] [byte:channelId] [uint32:packet-size] // [byte:seqno'] [bytes:encoded-fragment-#] [bytes:frag]</pre> // Although we use an adaptive scheme for encoding the number, // we assume a maximum of 4 bytes for encoding # frags // for a total of 4 extra bytes bytes; we determine the frag // size from the message size - MaxHeaderSize const uint maxFragHeaderSize = 1 /*seqno*/ + 4; const uint maxHeaderSize = LWMCFv11.HeaderSize + maxFragHeaderSize; uint maxPacketSize = Math.Max(maxHeaderSize, tdc.MaximumPacketSize - maxHeaderSize); // round up the number of possible fragments uint numFragments = (uint)(packet.Length + maxPacketSize - 1) / maxPacketSize; Debug.Assert(numFragments > 1); uint seqNo = AllocateOutgoingSeqNo(tdc); for (uint fragNo = 0; fragNo < numFragments; fragNo++) { TransportPacket newPacket = new TransportPacket(); Stream s = newPacket.AsWriteStream(); uint fragSize = (uint)Math.Min(maxPacketSize, packet.Length - (fragNo * maxPacketSize)); if (fragNo == 0) { s.WriteByte((byte)seqNo); ByteUtils.EncodeLength(numFragments, s); } else { s.WriteByte((byte)(seqNo | 128)); ByteUtils.EncodeLength(fragNo, s); } newPacket.Prepend(LWMCFv11.EncodeHeader((MessageType)((byte)message.MessageType | 128), message.ChannelId, (uint)(fragSize + s.Length))); newPacket.Append(packet, (int)(fragNo * maxPacketSize), (int)fragSize); mr.AddPacket(newPacket); } packet.Dispose(); }
public void TestSplitOut() { TransportPacket packet = new TransportPacket(); for (int i = 0; i < 256; ) { byte[] source = new byte[8]; for (int j = 0; j < source.Length; j++, i++) { source[j] = (byte)i; } packet.Append(source); } Assert.AreEqual(256, packet.Length); Assert.IsTrue(((IList<ArraySegment<byte>>)packet).Count > 1); for (int splitCount = 0; splitCount <= packet.Length; splitCount++) { TransportPacket back = packet.Copy(); TransportPacket front = back.SplitOut(splitCount); Assert.AreEqual(splitCount, front.Length); Assert.AreEqual(packet.Length - splitCount, back.Length); int index = 0; for (; index < front.Length; index++) { Assert.AreEqual((byte)index, front.ByteAt(index)); } for (int i = 0; i < back.Length; i++) { Assert.AreEqual((byte)(index + i), back.ByteAt(i)); } CheckNotDisposed(back); CheckNotDisposed(front); } CheckDisposed(packet); CheckForUndisposedSegments(); }
public void TestSubset() { byte[] source = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // We test for various subsets that overlap partially or fully with the different segments for (int sourceStart = 0; sourceStart < source.Length / 2; sourceStart++) { for (int sourceEnd = source.Length - 1; sourceEnd - sourceStart > 0; sourceEnd--) { int sourceCount = sourceEnd - sourceStart + 1; TransportPacket packet = new TransportPacket(source, sourceStart, sourceCount); Assert.AreEqual(sourceCount, packet.Length); Assert.AreEqual(1, ((IList<ArraySegment<byte>>)packet).Count); for (int i = 0; i < 10; i++) { packet.Append(source, sourceStart, sourceCount); } Assert.AreEqual(sourceCount * 11, packet.Length); const int subsetStart = 4; int subsetCount = Math.Min(17, packet.Length - 2); TransportPacket subset = packet.Subset(subsetStart, subsetCount); Assert.AreEqual(subsetCount, subset.Length); byte[] result = subset.ToArray(); Assert.AreEqual(subsetCount, result.Length); for (int i = 0; i < result.Length; i++) { Assert.AreEqual(source[sourceStart + ((subsetStart + i) % sourceCount)], result[i]); Assert.AreEqual(source[sourceStart + ((subsetStart + i) % sourceCount)], subset.ByteAt(i)); Assert.AreEqual(packet.ByteAt(subsetStart + i), subset.ByteAt(i)); } // And ensure the subset has the same backing byte array is still referenced // sourceEquivIndex = the equivalent index in source to subset[0] int sourceEquivIndex = sourceStart + (subsetStart % sourceCount); Assert.AreEqual(source[sourceEquivIndex], packet.ByteAt(subsetStart)); Assert.AreEqual(source[sourceEquivIndex], subset.ByteAt(0)); packet.Replace(subsetStart, new byte[] { 255 }, 0, 1); Assert.AreEqual(255, packet.ByteAt(subsetStart)); Assert.AreEqual(255, subset.ByteAt(0)); packet.Replace(subsetStart, source, sourceEquivIndex, 1); Assert.AreEqual(source[sourceEquivIndex], packet.ByteAt(subsetStart)); Assert.AreEqual(source[sourceEquivIndex], subset.ByteAt(0)); CheckNotDisposed(subset); /// Ensure that disposing of the subset doesn't dispose the parent packet for (int i = 0; i < packet.Length; i++) { Assert.AreEqual(source[sourceStart + (i % sourceCount)], packet.ByteAt(i)); } CheckDisposed(packet); } } CheckForUndisposedSegments(); }
public void TestNewTransportPacket() { TransportPacket.MaxSegmentSize = TransportPacket.MinSegmentSize; TransportPacket packet = new TransportPacket(); packet.Append(new byte[] { 0, 1, 2, 3 }); packet.Append(new byte[] { 4, 5, 6, 7, 8 }); packet.Append(new byte[] { 9 }); // Ensure that new TransportPacket() properly copies out TransportPacket copy = new TransportPacket(packet, 1, 8); Assert.AreEqual(packet.ToArray(1, 8), copy.ToArray()); CheckDisposed(packet, copy); CheckForUndisposedSegments(); }
public void TestReadStream() { byte[] source = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int sourceStart = 1; int sourceCount = 6; TransportPacket packet = new TransportPacket(); packet.Append(source, sourceStart, sourceCount); packet.Append(source, sourceStart, sourceCount); packet.Append(source, sourceStart, sourceCount); Stream s = packet.AsReadStream(); int packetLength = packet.Length; Assert.AreEqual(3 * sourceCount, packetLength); for (int i = 0; i < packetLength; i++) { Assert.AreEqual(source[sourceStart + (i % sourceCount)], s.ReadByte()); Assert.AreEqual(packetLength - i - 1, s.Length - s.Position); } Assert.AreEqual(s.Length, s.Position); CheckDisposed(packet); CheckForUndisposedSegments(); }
public void TestMultiSegments() { byte[] source = new byte[] { 0, 1, 2, 3, 4 }; TransportPacket p = new TransportPacket(source, 1, 4); Assert.AreEqual(4, p.Length); Assert.AreEqual(1, ((IList<ArraySegment<byte>>)p).Count); for (int i = 0; i < 10; i++) { p.Append(source, 1, 4); } Assert.AreEqual(4 * 11, p.Length); byte[] result = p.ToArray(); Assert.AreEqual(4 * 11, result.Length); for (int j = 0; j < 11; j++) { for (int i = 0; i < 4; i++) { Assert.AreEqual(source[1 + i], result[4 * j + i]); } } CheckDisposed(p); CheckForUndisposedSegments(); }
public void TestDisposal() { TransportPacket packet = new TransportPacket(10); packet.Dispose(); try { packet.Grow(20); Assert.Fail("should have thrown ObjectDisposedException"); } catch(ObjectDisposedException) { /* expected */ } try { packet.RemoveBytes(0,2); Assert.Fail("should have thrown ObjectDisposedException"); } catch(ObjectDisposedException) { /* expected */ } try { packet.ByteAt(0); Assert.Fail("should have thrown ObjectDisposedException"); } catch(ObjectDisposedException) { /* expected */ } try { packet.Consolidate(); Assert.Fail("should have thrown ObjectDisposedException"); } catch(ObjectDisposedException) { /* expected */ } try { packet.Prepend(new byte[0]); Assert.Fail("should have thrown ObjectDisposedException"); } catch (ObjectDisposedException) { /* expected */ } try { packet.Append(new byte[0]); Assert.Fail("should have thrown ObjectDisposedException"); } catch (ObjectDisposedException) { /* expected */ } try { Console.WriteLine(packet.Length); Assert.Fail("should have thrown ObjectDisposedException"); } catch (ObjectDisposedException) { /* expected */ } }
public void TestByteAt() { TransportPacket packet = new TransportPacket(); packet.Append(new byte[] {0, 1, 2, 3}); packet.Append(new byte[] {4, 5, 6, 7, 8}); packet.Append(new byte[] {9}); for(int i = 0; i < 10; i++) { Assert.AreEqual(i, packet.ByteAt(i)); packet.BytesAt(i, 1, (b,offset) => Assert.AreEqual(i, b[offset])); } packet.BytesAt(0, 10, (bytes, offset) => { for(int i = 0; i < 10; i++) { Assert.AreEqual(i, bytes[i]); } }); try { packet.ByteAt(10); Assert.Fail("Should have thrown ArgumentOutOfRange"); } catch (ArgumentOutOfRangeException) { /*ignore*/ } try { packet.BytesAt(10,1, (b,o) => Assert.Fail("should have thrown AOOR")); Assert.Fail("Should have thrown ArgumentOutOfRange"); } catch (ArgumentOutOfRangeException) { /*ignore*/ } try { packet.BytesAt(8, 8, (b, o) => Assert.Fail("should have thrown AOOR")); Assert.Fail("Should have thrown ArgumentOutOfRange"); } catch (ArgumentOutOfRangeException) { /*ignore*/ } CheckDisposed(packet); CheckForUndisposedSegments(); }
public override IMarshalledResult Marshal(int senderIdentity, Message msg, ITransportDeliveryCharacteristics tdc) { // This marshaller doesn't use <see cref="senderIdentity"/>. if (msg is RawMessage) { MarshalledResult mr = new MarshalledResult(); TransportPacket tp = new TransportPacket(); // NB: SystemMessages use ChannelId to encode the sysmsg descriptor RawMessage rm = (RawMessage)msg; tp.Prepend(LWMCFv11.EncodeHeader(msg.MessageType, msg.ChannelId, (uint)rm.Bytes.Length)); tp.Append(rm.Bytes); mr.AddPacket(tp); return mr; } return base.Marshal(senderIdentity, msg, tdc); }
/// <summary> /// Handle incoming packets from previously-unknown remote endpoints. /// We check if the packet indicates a handshake and, if so, negotiate. /// Otherwise we send a GT error message and disregard the packet. /// </summary> /// <param name="ep">the remote endpoint</param> /// <param name="packet">the first packet</param> protected void PreviouslyUnseenUdpEndpoint(EndPoint ep, TransportPacket packet) { TransportPacket response; Stream ms; // This is the GT (UDP) protocol 1.0: // bytes 0 - 3: the protocol version (the result from ProtocolDescriptor) // bytes 4 - n: the number of bytes in the capability dictionary (see ByteUtils.EncodeLength) // bytes n+1 - end: the capability dictionary // The # bytes in the dictionary isn't actually necessary in UDP, but oh well foreach(TransportFactory<UdpHandle> factory in factories) { if(packet.Length >= factory.ProtocolDescriptor.Length && ByteUtils.Compare(packet.ToArray(0, factory.ProtocolDescriptor.Length), factory.ProtocolDescriptor)) { packet.RemoveBytes(0, factory.ProtocolDescriptor.Length); ms = packet.AsReadStream(); Dictionary<string, string> dict = null; try { uint count = ByteUtils.DecodeLength(ms); // we don't use it dict = ByteUtils.DecodeDictionary(ms); if(ms.Position != ms.Length) { byte[] rest = packet.ToArray(); log.Info(String.Format( "{0} bytes still left at end of UDP handshake packet: {1} ({2})", rest.Length, ByteUtils.DumpBytes(rest, 0, rest.Length), ByteUtils.AsPrintable(rest, 0, rest.Length))); } ITransport result = factory.CreateTransport(UdpHandle.Bind(udpMultiplexer, ep)); if (ShouldAcceptTransport(result, dict)) { // Send confirmation // NB: following uses the format specified by LWMCF v1.1 response = new TransportPacket(LWMCFv11.EncodeHeader(MessageType.System, (byte)SystemMessageType.Acknowledged, (uint)factory.ProtocolDescriptor.Length)); response.Append(factory.ProtocolDescriptor); udpMultiplexer.Send(response, ep); NotifyNewTransport(result, dict); } else { // NB: following follows the format specified by LWMCF v1.1 response = new TransportPacket(LWMCFv11.EncodeHeader(MessageType.System, (byte)SystemMessageType.IncompatibleVersion, 0)); udpMultiplexer.Send(response, ep); result.Dispose(); } return; } catch(Exception e) { log.Warn(String.Format("Error decoding handshake from remote {0}", ep), e); } } } // If we can figure out some way to say: the packet is an invalid form: // response = new TransportPacket(); // ms = response.AsWriteStream(); // log.Info("Undecipherable packet (ignored)"); // // NB: following follows the format specified by LWMCF v1.1 // LWMCFv11.EncodeHeader(MessageType.System, (byte)SystemMessageType.UnknownConnexion, // (uint)ProtocolDescriptor.Length, ms); // ms.Write(ProtocolDescriptor, 0, ProtocolDescriptor.Length); // ms.Flush(); // udpMultiplexer.Send(response, ep); response = new TransportPacket(); ms = response.AsWriteStream(); log.Info("Unknown protocol version: " + ByteUtils.DumpBytes(packet.ToArray(), 0, 4) + " [" + ByteUtils.AsPrintable(packet.ToArray(), 0, 4) + "]"); // NB: following follows the format specified by LWMCF v1.1 LWMCFv11.EncodeHeader(MessageType.System, (byte)SystemMessageType.IncompatibleVersion, 0, ms); ms.Flush(); udpMultiplexer.Send(response, ep); }