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;
 }
Beispiel #2
0
        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();
        }
Beispiel #4
0
        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();
        }
Beispiel #5
0
        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();
        }
Beispiel #6
0
        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();
        }
Beispiel #7
0
        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();
        }
Beispiel #8
0
        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();
        }
Beispiel #9
0
 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 */ }
 }
Beispiel #10
0
        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();
        }
Beispiel #11
0
        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);
        }