/// <summary> /// Reads headers from a stream into the given frame. /// </summary> /// <param name="stream"></param> /// <param name="headerCount"></param> /// <param name="frame"></param> private static void ReadHeadersFromStream(Stream stream, ushort headerCount, GdsFrame frame) { BinaryReader reader = new BinaryReader(stream); for (int i = 0; i < headerCount; i++) { ushort keyLength = (ushort)IPAddress.NetworkToHostOrder((short)reader.ReadUInt16()); ushort valueLength = (ushort)IPAddress.NetworkToHostOrder((short)reader.ReadUInt16()); byte[] key = reader.ReadBytes(keyLength); if (key.Length != keyLength) { throw new EndOfStreamException(); } byte[] value = reader.ReadBytes(valueLength); if (value.Length != valueLength) { throw new EndOfStreamException(); } frame.Headers[HeaderEncoding.GetString(key)] = value; } }
public void TestPing() { ObjectPool <byte[]> pool = new ObjectPool <byte[]>(() => { return(new byte[1024]); }); Random rand = new Random(this.GetHashCode() ^ DateTime.Now.Millisecond); uint streamId = (uint)rand.Next(0, (int)(Math.Pow(2, 24) - 1)); GdsFrame frame = GdsFrame.NewPingFrame(streamId); Assert.AreEqual(true, frame.IsComplete); Assert.AreEqual(GdsFrame.GdsFrameType.Ping, frame.Type); Assert.AreEqual(streamId, frame.StreamId); MemoryStream stream = new MemoryStream(); frame.Write(stream); Assert.AreEqual(4, stream.Position); stream.Position = 0; GdsFrame readFrame = GdsFrame.ParseFrame(stream, pool); Assert.AreEqual(true, readFrame.IsComplete); Assert.AreEqual(GdsFrame.GdsFrameType.Ping, readFrame.Type); Assert.AreEqual(streamId, readFrame.StreamId); }
public ChunkedBuffer ToBuffer(GdsFrame frame) { ObjectPool <byte[]> pool = new ObjectPool <byte[]>(() => { return(new byte[1024]); }); ChunkedBuffer buffer = new ChunkedBuffer(pool); frame.Write(buffer.Stream); return(buffer); }
public void TestFullCompressed() { ObjectPool <byte[]> pool = new ObjectPool <byte[]>(() => { return(new byte[1024]); }); Random rand = new Random(this.GetHashCode() ^ DateTime.Now.Millisecond); uint streamId = (uint)rand.Next(0, (int)(Math.Pow(2, 24) - 1)); string header1Key = "the first key"; byte[] header1Value = new byte[rand.Next(32, 1024 * 64)]; rand.NextBytes(header1Value); string header2Key = "the second key"; byte[] header2Value = new byte[rand.Next(32, 1024 * 64)]; rand.NextBytes(header2Value); byte[] bodyBuffer = new byte[rand.Next(1024, 1024 * 64)]; rand.NextBytes(bodyBuffer); ChunkedBuffer body = new ChunkedBuffer(pool); body.OfferRaw(bodyBuffer, 0, bodyBuffer.Length); GdsFrame frame = GdsFrame.NewContentFrame(streamId, new Dictionary <string, byte[]>() { { header1Key, header1Value }, { header2Key, header2Value } }, true, body, true); Assert.AreEqual(true, frame.IsComplete); Assert.AreEqual(GdsFrame.GdsFrameType.Full, frame.Type); Assert.AreEqual(streamId, frame.StreamId); Assert.AreEqual(2, frame.Headers.Count); MemoryStream stream = new MemoryStream(); frame.Write(stream); stream.Position = 0; GdsFrame readFrame = GdsFrame.ParseFrame(stream, pool); Assert.AreEqual(true, readFrame.IsComplete); Assert.AreEqual(GdsFrame.GdsFrameType.Full, readFrame.Type); Assert.AreEqual(streamId, readFrame.StreamId); Assert.AreEqual(2, readFrame.Headers.Count); byte[] readBodyBuffer = new byte[readFrame.Body.AvailableBytesToRead]; readFrame.Body.Read(readBodyBuffer, 0, readBodyBuffer.Length); AssertEquals(bodyBuffer, readBodyBuffer); frame.Dispose(); readFrame.Dispose(); }
/// <summary> /// Updates the given chunked frame with a new frame. /// </summary> /// <param name="frame"></param> private static void UpdateChunk(ref GdsFrame chunkedFrame, GdsFrame frame, ISockNetChannel channel) { if (chunkedFrame == null) { chunkedFrame = frame; // set initial frame } else { if (frame.Type == GdsFrame.GdsFrameType.Ping || frame.Type == GdsFrame.GdsFrameType.Pong || frame.Type == GdsFrame.GdsFrameType.Close) { chunkedFrame = frame; } GdsFrame.GdsFrameType type = chunkedFrame.Type; ChunkedBuffer body = null; if (frame.Type == GdsFrame.GdsFrameType.BodyOnly || frame.Type == GdsFrame.GdsFrameType.Full) { if (type == GdsFrame.GdsFrameType.HeadersOnly) { type = GdsFrame.GdsFrameType.Full; } body = new ChunkedBuffer(channel.BufferPool); chunkedFrame.Body.DrainToStreamSync(body.Stream).Close(); frame.Body.DrainToStreamSync(body.Stream).Close(); } Dictionary <string, byte[]> headers = null; if (frame.Type == GdsFrame.GdsFrameType.HeadersOnly || frame.Type == GdsFrame.GdsFrameType.Full) { if (type == GdsFrame.GdsFrameType.BodyOnly) { type = GdsFrame.GdsFrameType.Full; } headers = new Dictionary <string, byte[]>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair <string, byte[]> kvp in chunkedFrame.Headers) { headers[kvp.Key] = kvp.Value; } foreach (KeyValuePair <string, byte[]> kvp in frame.Headers) { headers[kvp.Key] = kvp.Value; } } chunkedFrame.Dispose(); frame.Dispose(); chunkedFrame = GdsFrame.NewContentFrame(frame.StreamId, headers, false, body, frame.IsComplete); } }
public void TestHeaderOnlyNotCompressed() { ObjectPool <byte[]> pool = new ObjectPool <byte[]>(() => { return(new byte[1024]); }); Random rand = new Random(this.GetHashCode() ^ DateTime.Now.Millisecond); uint streamId = (uint)rand.Next(0, (int)(Math.Pow(2, 24) - 1)); string header1Key = "the first key"; byte[] header1Value = new byte[rand.Next(32, 1024 * 64)]; rand.NextBytes(header1Value); string header2Key = "the second key"; byte[] header2Value = new byte[rand.Next(32, 1024 * 64)]; rand.NextBytes(header2Value); GdsFrame frame = GdsFrame.NewContentFrame(streamId, new Dictionary <string, byte[]>() { { header1Key, header1Value }, { header2Key, header2Value } }, false, null, true); Assert.AreEqual(true, frame.IsComplete); Assert.AreEqual(GdsFrame.GdsFrameType.HeadersOnly, frame.Type); Assert.AreEqual(streamId, frame.StreamId); Assert.AreEqual(2, frame.Headers.Count); MemoryStream stream = new MemoryStream(); frame.Write(stream); Assert.AreEqual( 4 // frame definition + 2 // header definition + (4 * 2) // header sizes for two headers + header1Key.Length + header1Value.Length + header2Key.Length + header2Value.Length , stream.Position); stream.Position = 0; GdsFrame readFrame = GdsFrame.ParseFrame(stream, pool); Assert.AreEqual(true, readFrame.IsComplete); Assert.AreEqual(GdsFrame.GdsFrameType.HeadersOnly, readFrame.Type); Assert.AreEqual(streamId, readFrame.StreamId); Assert.AreEqual(2, readFrame.Headers.Count); AssertEquals(header1Value, readFrame.Headers[header1Key]); AssertEquals(header2Value, readFrame.Headers[header2Key]); }
/// <summary> /// Handles an incomming raw Gds message. /// </summary> /// <param name="channel"></param> /// <param name="obj"></param> public void HandleIncoming(ISockNetChannel channel, ref object obj) { if (!(obj is ChunkedBuffer)) { return; } ChunkedBuffer stream = (ChunkedBuffer)obj; long startingPosition = stream.ReadPosition; try { GdsFrame frame = GdsFrame.ParseFrame(stream.Stream, channel.BufferPool); if (combineChunks) { if (frame.IsComplete) { UpdateChunk(ref chunkedFrame, frame, channel); obj = chunkedFrame; chunkedFrame = null; } else { UpdateChunk(ref chunkedFrame, frame, channel); } } else { obj = frame; } if (SockNetLogger.DebugEnabled) { SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, "Received Gds message. Body Size: {0}, Type: {1}, IsComplete: {2}", frame.Body.AvailableBytesToRead, Enum.GetName(typeof(GdsFrame.GdsFrameType), frame.Type), frame.IsComplete); } } catch (EndOfStreamException) { // frame isn't done stream.ReadPosition = startingPosition; } catch (ArgumentOutOfRangeException) { // frame isn't done stream.ReadPosition = startingPosition; } catch (Exception e) { SockNetLogger.Log(SockNetLogger.LogLevel.ERROR, this, "Unable to parse Gds message", e); } }
public void TestBodyOnly() { ObjectPool <byte[]> pool = new ObjectPool <byte[]>(() => { return(new byte[1024]); }); Random rand = new Random(this.GetHashCode() ^ DateTime.Now.Millisecond); uint streamId = (uint)rand.Next(0, (int)(Math.Pow(2, 24) - 1)); byte[] bodyBuffer = new byte[rand.Next(1024, 1024 * 64)]; rand.NextBytes(bodyBuffer); ChunkedBuffer body = new ChunkedBuffer(pool); body.OfferRaw(bodyBuffer, 0, bodyBuffer.Length); GdsFrame frame = GdsFrame.NewContentFrame(streamId, null, false, body, true); Assert.AreEqual(true, frame.IsComplete); Assert.AreEqual(GdsFrame.GdsFrameType.BodyOnly, frame.Type); Assert.AreEqual(streamId, frame.StreamId); Assert.AreEqual(0, frame.Headers.Count); MemoryStream stream = new MemoryStream(); frame.Write(stream); Assert.AreEqual( 4 // frame definition + 4 // body definition + bodyBuffer.Length , stream.Position); stream.Position = 0; GdsFrame readFrame = GdsFrame.ParseFrame(stream, pool); Assert.AreEqual(true, readFrame.IsComplete); Assert.AreEqual(GdsFrame.GdsFrameType.BodyOnly, readFrame.Type); Assert.AreEqual(streamId, readFrame.StreamId); Assert.AreEqual(0, readFrame.Headers.Count); byte[] readBodyBuffer = new byte[readFrame.Body.AvailableBytesToRead]; readFrame.Body.Read(readBodyBuffer, 0, readBodyBuffer.Length); AssertEquals(bodyBuffer, readBodyBuffer); frame.Dispose(); readFrame.Dispose(); }
/// <summary> /// Handles an outgoing HttpRequest and converts it to a raw buffer. /// </summary> /// <param name="channel"></param> /// <param name="obj"></param> public void HandleOutgoing(ISockNetChannel channel, ref object obj) { if (!(obj is GdsFrame)) { return; } GdsFrame gdsFrame = (GdsFrame)obj; ChunkedBuffer buffer = new ChunkedBuffer(channel.BufferPool); gdsFrame.Write(buffer.Stream); gdsFrame.Dispose(); obj = buffer; }
public void TestSimpleSslContent() { ObjectPool <byte[]> pool = new ObjectPool <byte[]>(() => { return(new byte[1024]); }); GdsEchoServer server = new GdsEchoServer(pool); try { server.Start(true); BlockingCollection <object> blockingCollection = new BlockingCollection <object>(); ClientSockNetChannel client = (ClientSockNetChannel)SockNetClient.Create(server.Endpoint, ClientSockNetChannel.DefaultNoDelay, ClientSockNetChannel.DefaultTtl, pool) .AddModule(new GdsSockNetChannelModule(true)); client.ConnectWithTLS((object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) => { return(true); }) .WaitForValue(TimeSpan.FromSeconds(5)); object currentObject; client.Pipe.AddIncomingLast <GdsFrame>((ISockNetChannel sockNetClient, ref GdsFrame data) => { blockingCollection.Add(data); }); ChunkedBuffer body = new ChunkedBuffer(pool); body.OfferRaw(Encoding.UTF8.GetBytes("some test"), 0, Encoding.UTF8.GetByteCount("some test")); client.Send(GdsFrame.NewContentFrame(1, null, false, body, true)); Assert.IsTrue(blockingCollection.TryTake(out currentObject, 5000)); Assert.IsTrue(currentObject is GdsFrame); Assert.AreEqual("some test", ((GdsFrame)currentObject).Body.ToString(Encoding.UTF8)); Console.WriteLine("Got response: \n" + ((GdsFrame)currentObject).Body); client.Disconnect().WaitForValue(TimeSpan.FromSeconds(5)); } finally { server.Stop(); } }
public void TestCompression() { Random rand = new Random(this.GetHashCode() ^ DateTime.Now.Millisecond); uint streamId = (uint)rand.Next(0, (int)(Math.Pow(2, 24) - 1)); // deflate works great on text - it is horrible with random byte arrays string header1Key = "Some key"; byte[] header1Value = Encoding.UTF8.GetBytes("Well here is a great value for some key. We're really great at making keys and value."); string header2Key = "Another key"; byte[] header2Value = Encoding.UTF8.GetBytes("Yet another great value for another key. This is just getting absurd."); GdsFrame frame = GdsFrame.NewContentFrame(streamId, new Dictionary <string, byte[]>() { { header1Key, header1Value }, { header2Key, header2Value } }, true, null, true); Assert.AreEqual(true, frame.IsComplete); Assert.AreEqual(GdsFrame.GdsFrameType.HeadersOnly, frame.Type); Assert.AreEqual(streamId, frame.StreamId); Assert.AreEqual(2, frame.Headers.Count); MemoryStream stream = new MemoryStream(); frame.Write(stream); int uncompressedSize = (4 // frame definition + 2 // header definition + (4 * 2) // header sizes for two headers + header1Key.Length + header1Value.Length + header2Key.Length + header2Value.Length); Console.WriteLine("Compressed: " + stream.Position + ", Uncompressed: " + uncompressedSize); Assert.IsTrue(stream.Position < uncompressedSize); }
/// <summary> /// Creates a new content frame where the frame is a headers only, body only, or a full frame. /// </summary> /// <param name="streamId"></param> /// <param name="headers"></param> /// <param name="areHeadersCompressed"></param> /// <param name="body"></param> /// <param name="isComplete"></param> /// <returns></returns> public static GdsFrame NewContentFrame(uint streamId, Dictionary <string, byte[]> headers = null, bool areHeadersCompressed = false, ChunkedBuffer body = null, bool isComplete = true) { GdsFrameType type = GdsFrameType.Full; if (body == null) { type = GdsFrameType.HeadersOnly; } else if (headers == null) { type = GdsFrameType.BodyOnly; } GdsFrame frame = new GdsFrame(null) { IsComplete = isComplete, Type = type, StreamId = streamId, AreHeadersCompressed = areHeadersCompressed }; if (headers != null) { foreach (KeyValuePair <string, byte[]> kvp in headers) { frame.Headers[kvp.Key] = kvp.Value; } } if (body != null) { frame.Body = body; } return(frame); }
public void TestChunks() { ObjectPool <byte[]> pool = new ObjectPool <byte[]>(() => { return(new byte[1024]); }); DummySockNetChannel channel = new DummySockNetChannel() { State = null, IsActive = true, BufferPool = pool }; channel.Pipe = new SockNetChannelPipe(channel); GdsSockNetChannelModule module = new GdsSockNetChannelModule(true); channel.AddModule(module); channel.Connect(); uint streamId = 1; ChunkedBuffer body = new ChunkedBuffer(pool); body.OfferRaw(Encoding.UTF8.GetBytes("This "), 0, Encoding.UTF8.GetByteCount("This ")); GdsFrame chunk1 = GdsFrame.NewContentFrame(streamId, new Dictionary <string, byte[]>() { { "test1", new byte[] { 1 } }, { "test", new byte[] { 1 } }, }, false, body, false); ChunkedBuffer buffer = ToBuffer(chunk1); object receiveResponse = buffer; channel.Receive(ref receiveResponse); buffer.Close(); Assert.IsTrue(receiveResponse is ChunkedBuffer); body = new ChunkedBuffer(pool); body.OfferRaw(Encoding.UTF8.GetBytes("is "), 0, Encoding.UTF8.GetByteCount("is ")); GdsFrame chunk2 = GdsFrame.NewContentFrame(streamId, new Dictionary <string, byte[]>() { { "test2", new byte[] { 2 } }, { "test", new byte[] { 2 } }, }, false, body, false); buffer = ToBuffer(chunk2); receiveResponse = buffer; channel.Receive(ref receiveResponse); buffer.Close(); Assert.IsTrue(receiveResponse is ChunkedBuffer); body = new ChunkedBuffer(pool); body.OfferRaw(Encoding.UTF8.GetBytes("awesome!"), 0, Encoding.UTF8.GetByteCount("awesome!")); GdsFrame chunk3 = GdsFrame.NewContentFrame(streamId, new Dictionary <string, byte[]>() { { "test3", new byte[] { 3 } }, { "test", new byte[] { 3 } }, }, false, body, true); buffer = ToBuffer(chunk3); receiveResponse = buffer; channel.Receive(ref receiveResponse); buffer.Close(); Assert.IsTrue(receiveResponse is GdsFrame); Assert.AreEqual("This is awesome!", ((GdsFrame)receiveResponse).Body.ToString(Encoding.UTF8)); Assert.AreEqual(1, ((GdsFrame)receiveResponse).Headers["test1"][0]); Assert.AreEqual(2, ((GdsFrame)receiveResponse).Headers["test2"][0]); Assert.AreEqual(3, ((GdsFrame)receiveResponse).Headers["test3"][0]); Assert.AreEqual(3, ((GdsFrame)receiveResponse).Headers["test"][0]); body.Dispose(); chunk1.Dispose(); chunk2.Dispose(); chunk3.Dispose(); Console.WriteLine("Pool stats: " + pool.ObjectsInPool + "/" + pool.TotalNumberOfObjects); }
/// <summary> /// Parses a frame from a stream. /// </summary> /// <param name="stream"></param> /// <returns></returns> public static GdsFrame ParseFrame(Stream stream, ObjectPool <byte[]> bufferPool) { GdsFrame frame = new GdsFrame(bufferPool); BinaryReader reader = new BinaryReader(stream); uint frameDefinition = (uint)IPAddress.NetworkToHostOrder((int)reader.ReadUInt32()); if (!Enum.IsDefined(typeof(GdsFrameType), (byte)((frameDefinition & (uint)TypeMask) >> TypeShift))) { throw new ArgumentException("Invalid type: " + (GdsFrameType)(byte)((frameDefinition & (uint)TypeMask) >> TypeShift)); } frame.IsComplete = ((frameDefinition & IsCompleteMask) >> IsCompleteShift) == 1; frame.Type = (GdsFrameType)(byte)((frameDefinition & (uint)TypeMask) >> TypeShift); frame.StreamId = (frameDefinition & (uint)StreamIdMask) >> StreamIdShift; // parse the headers if (frame.Type == GdsFrameType.Full || frame.Type == GdsFrameType.HeadersOnly) { ushort headersDefinition = (ushort)IPAddress.NetworkToHostOrder((short)reader.ReadUInt16()); frame.AreHeadersCompressed = ((headersDefinition & (ushort)HeadersIsCompressedMask) >> HeadersIsCompressedShift) == 1; ushort count = (ushort)((headersDefinition & HeadersLengthMask) >> HeadersLengthShift); if (frame.AreHeadersCompressed) { long movePosition = stream.Position; using (DeflateStream compressedHeaderStream = new DeflateStream(stream, CompressionMode.Decompress, true)) { ReadHeadersFromStream(compressedHeaderStream, count, frame); movePosition += compressedHeaderStream.TotalIn; } stream.Position = movePosition; // we need to fix the position since the DeflateStream buffers the base stream } else { ReadHeadersFromStream(stream, count, frame); } } // read the body if (frame.Type == GdsFrameType.Full || frame.Type == GdsFrameType.BodyOnly) { uint length = (uint)IPAddress.NetworkToHostOrder((int)reader.ReadUInt32()); frame.Body = new ChunkedBuffer(bufferPool); PooledObject <byte[]> buffer = bufferPool.Borrow(); int count = 0; while ((count = reader.Read(buffer.Value, 0, buffer.Value.Length)) > 0) { frame.Body.OfferChunk(buffer, 0, count); buffer = bufferPool.Borrow(); } if (count < 1) { buffer.Return(); } if (frame.Body.AvailableBytesToRead != length) { throw new EndOfStreamException(); } } return(frame); }