Пример #1
0
        public void EncodeDecode_SessionStart_Single_WithDictionaryData()
        {
            Dictionary <string, object> data = new Dictionary <string, object>
            {
                { "keyString", "string" },
                { "keyInteger", 36 },
                { "keyArray", new byte[10] },
            };

            var encoded = Frame.CreateSessionStartFrame(0, BsonCodec.Encode(data));
            var decoded = Frame.DecodeFrameBuffer(encoded, 0, encoded.Length, out var newOffset, out var newAvailable);

            Assert.AreEqual(1, decoded.Count);
            Assert.AreEqual(0, newOffset);
            Assert.AreEqual(0, newAvailable);
            Assert.AreEqual(FrameType.SessionStart, decoded[0].Type);
            Assert.AreEqual(0, decoded[0].SessionID);

            // validate data
            Assert.NotNull(decoded[0].Payload);
            var dataDecoded = BsonCodec.Decode <Dictionary <string, object> >(decoded[0].Payload);

            Assert.NotNull(dataDecoded);

            // keys and values present
            Assert.IsTrue(dataDecoded.ContainsKey("keyString"));
            Assert.AreEqual("string", (string)dataDecoded["keyString"]);

            Assert.IsTrue(dataDecoded.ContainsKey("keyInteger"));
            Assert.AreEqual(36, (int)(long)dataDecoded["keyInteger"]);      // need to first unbox

            Assert.IsTrue(dataDecoded.ContainsKey("keyArray"));
            Assert.AreEqual(10, ((byte[])dataDecoded["keyArray"]).Length);
        }
Пример #2
0
        public void EncodeDecode_Dictionary_Concurrent()
        {
            List <Dictionary <string, object> > dictionaries = new List <Dictionary <string, object> >();

            for (int i = 0; i < 1000; i++)
            {
                dictionaries.Add(new Dictionary <string, object>
                {
                    { "key", i.ToString() },
                    { "data", new byte[80000] }
                });
            }

            var result = Parallel.ForEach(dictionaries, new ParallelOptions {
                MaxDegreeOfParallelism = 10
            }, (s) =>
            {
                var encoded = BsonCodec.Encode(s);
                Assert.NotNull(encoded);
                var decoded = BsonCodec.Decode <Dictionary <string, object> >(encoded);
                Assert.NotNull(decoded);
                Assert.IsTrue(decoded.ContainsKey("key"));
                Assert.IsTrue(decoded.ContainsKey("data"));
                Assert.AreEqual(80000, (decoded["data"] as byte[]).Length);
            });

            Assert.IsTrue(result.IsCompleted);
        }
Пример #3
0
        public static byte[] CreateConnectionAcceptFrame()
        {
            var descriptor = new ConnectionDescriptor
            {
                ProtocolVersion = ProtocolVersion,
                MaxFrameSize    = MaxFrameSize
            };

            return(_createFrame(FrameType.ConnectionAccept, BsonCodec.Encode(descriptor)));
        }
Пример #4
0
        public static byte[] CreateConnectionStartFrame(Dictionary <string, string> data = null)
        {
            var descriptor = new ConnectionDescriptor
            {
                ProtocolVersion = ProtocolVersion,
                MaxFrameSize    = MaxFrameSize,
                Data            = data
            };

            return(_createFrame(FrameType.ConnectionStart, BsonCodec.Encode(descriptor)));
        }
Пример #5
0
        public static byte[] CreateConnectionRejectFrame(string error = null)
        {
            var descriptor = new ConnectionDescriptor
            {
                ProtocolVersion = ProtocolVersion,
                MaxFrameSize    = MaxFrameSize,
                Error           = error
            };

            return(_createFrame(FrameType.ConnectionReject, BsonCodec.Encode(descriptor)));
        }
Пример #6
0
        public void EncodeDecode_SessionDescriptor()
        {
            SessionDescriptor descriptor = new SessionDescriptor {
                Name = "testName"
            };
            var descEncoded = BsonCodec.Encode(descriptor);

            Assert.NotNull(descEncoded);
            var decoded = BsonCodec.Decode <SessionDescriptor>(descEncoded);

            Assert.NotNull(decoded);
            Assert.AreEqual(descriptor.Name, decoded.Name);
        }
Пример #7
0
        public void SessionOpen()
        {
            var data = new Dictionary <string, object>
            {
                { "data", "Frame.CreateSessionOpenFrame" }
            };

            var encoded = Frame.CreateSessionOpenFrame(99, BsonCodec.Encode(data));
            var decoded = Frame.DecodeFrameBuffer(encoded, 0, encoded.Length, out var newOffset, out var newAvailable);

            Assert.AreEqual(1, decoded.Count);
            Assert.AreEqual(0, newOffset);
            Assert.AreEqual(0, newAvailable);
            Assert.AreEqual(FrameType.SessionOpen, decoded[0].Type);
            Assert.AreEqual(99, decoded[0].SessionID);
            Assert.NotNull(decoded[0].Payload);
            var decodedData = BsonCodec.Decode <Dictionary <string, object> >(decoded[0].Payload);

            Assert.NotNull(decodedData);
            Assert.IsTrue(decodedData.ContainsKey("data"));
            Assert.AreEqual("Frame.CreateSessionOpenFrame", (string)decodedData["data"]);
        }
Пример #8
0
        public void EncodeDecode_SessionDescriptor_Concurrent()
        {
            List <SessionDescriptor> descriptors = new List <SessionDescriptor>();

            for (int i = 0; i < 1000; i++)
            {
                descriptors.Add(new SessionDescriptor {
                    Name = i.ToString()
                });
            }

            var result = Parallel.ForEach(descriptors, new ParallelOptions {
                MaxDegreeOfParallelism = 10
            }, (s) =>
            {
                var encoded = BsonCodec.Encode(s);
                Assert.NotNull(encoded);
                var decoded = BsonCodec.Decode <SessionDescriptor>(encoded);
                Assert.NotNull(decoded);
                Assert.AreEqual(s.Name, decoded.Name);
            });

            Assert.IsTrue(result.IsCompleted);
        }
Пример #9
0
        public void EncodeDecode_SessionStart_Multiple_WithData()
        {
            var descriptor0 = new SessionDescriptor {
                Name = "0"
            };
            var encoded0 = Frame.CreateSessionStartFrame(10, BsonCodec.Encode(descriptor0));

            var descriptor1 = new SessionDescriptor {
                Name = "1"
            };
            var encoded1 = Frame.CreateSessionStartFrame(11, BsonCodec.Encode(descriptor1));

            var descriptor2 = new SessionDescriptor {
                Name = "2"
            };
            var encoded2 = Frame.CreateSessionStartFrame(12, BsonCodec.Encode(descriptor2));

            var descriptor3 = new SessionDescriptor {
                Name = "3"
            };
            var encoded3 = Frame.CreateSessionStartFrame(13, BsonCodec.Encode(descriptor3));

            var descriptor4 = new SessionDescriptor {
                Name = "4"
            };
            var encoded4 = Frame.CreateSessionStartFrame(14, BsonCodec.Encode(descriptor4));

            var combined = new byte[encoded0.Length + encoded1.Length + encoded2.Length + encoded3.Length + encoded4.Length];

            Array.Copy(encoded0, 0, combined, 0, encoded0.Length);
            Array.Copy(encoded1, 0, combined, encoded0.Length, encoded1.Length);
            Array.Copy(encoded2, 0, combined, encoded0.Length + encoded1.Length, encoded2.Length);
            Array.Copy(encoded3, 0, combined, encoded0.Length + encoded1.Length + encoded2.Length, encoded3.Length);
            Array.Copy(encoded4, 0, combined, encoded0.Length + encoded1.Length + encoded2.Length + encoded3.Length, encoded4.Length);

            var decoded = Frame.DecodeFrameBuffer(combined, 0, combined.Length, out var newOffset, out var newAvailable);

            Assert.AreEqual(5, decoded.Count);
            Assert.AreEqual(0, newOffset);
            Assert.AreEqual(0, newAvailable);

            Assert.AreEqual(FrameType.SessionStart, decoded[0].Type);
            Assert.AreEqual(10, decoded[0].SessionID);
            Assert.NotNull(decoded[0].Payload);
            var decodedDescriptor0 = BsonCodec.Decode <SessionDescriptor>(decoded[0].Payload);

            Assert.AreEqual("0", decodedDescriptor0.Name);

            Assert.AreEqual(FrameType.SessionStart, decoded[1].Type);
            Assert.AreEqual(11, decoded[1].SessionID);
            Assert.NotNull(decoded[1].Payload);
            var decodedDescriptor1 = BsonCodec.Decode <SessionDescriptor>(decoded[1].Payload);

            Assert.AreEqual("1", decodedDescriptor1.Name);

            Assert.AreEqual(FrameType.SessionStart, decoded[2].Type);
            Assert.AreEqual(12, decoded[2].SessionID);
            Assert.NotNull(decoded[2].Payload);
            var decodedDescriptor2 = BsonCodec.Decode <SessionDescriptor>(decoded[2].Payload);

            Assert.AreEqual("2", decodedDescriptor2.Name);

            Assert.AreEqual(FrameType.SessionStart, decoded[3].Type);
            Assert.AreEqual(13, decoded[3].SessionID);
            Assert.NotNull(decoded[3].Payload);
            var decodedDescriptor3 = BsonCodec.Decode <SessionDescriptor>(decoded[3].Payload);

            Assert.AreEqual("3", decodedDescriptor3.Name);

            Assert.AreEqual(FrameType.SessionStart, decoded[4].Type);
            Assert.AreEqual(14, decoded[4].SessionID);
            Assert.NotNull(decoded[4].Payload);
            var decodedDescriptor4 = BsonCodec.Decode <SessionDescriptor>(decoded[4].Payload);

            Assert.AreEqual("4", decodedDescriptor4.Name);
        }
Пример #10
0
        /// <summary>
        /// Processes a received frame depending on frame type and current connection state
        /// </summary>
        /// <param name="frame">The frame to process</param>
        private void _processFrame(Frame frame)
        {
            // do NOT process anything if in a closed or errored state
            if (State == ConnectionState.End || State == ConnectionState.ConnectionError)
            {
                return;
            }

            switch (frame.Type)
            {
                #region Connection Frame Handling

            case FrameType.ConnectionStart:
            {
                if (State == ConnectionState.Start)
                {
                    Log.DebugFormat("[{0}] FrameType.ConnectionStart received", ID);

                    // decode connection descriptor content
                    var descriptor = BsonCodec.Decode <ConnectionDescriptor>(frame.Payload);

                    // store any data in the property - used by application to provide details about the connection
                    Data = descriptor.Data;

                    // is the protocol compatible
                    if (descriptor.ProtocolVersion.Major == Frame.ProtocolVersion.Major)
                    {
                        if (descriptor.MaxFrameSize < Frame.MaxFrameSize)
                        {
                            // protocol will use the smallest of the two max frame sizes
                            Frame.MaxFrameSize = descriptor.MaxFrameSize;
                        }

                        // update connection state
                        State = ConnectionState.ConnectionAcceptSent;

                        // send accept frame (version and (new) frame size will be returned)
                        _sendFrame(FrameType.ConnectionAccept, Frame.CreateConnectionAcceptFrame());
                    }
                    else
                    {
                        // set state
                        State = ConnectionState.End;

                        // reject the connection (description of why provided)
                        _sendFrame(FrameType.ConnectionReject, Frame.CreateConnectionRejectFrame("Protocol version incompatible"));

                        // should close here
                        _closeInternal();
                    }
                }
            } break;

            case FrameType.ConnectionAccept:
            {
                if (State == ConnectionState.ConnectionStartSent)
                {
                    Log.DebugFormat("[{0}] FrameType.ConnectionAccept received", ID);

                    // now in open state
                    State = ConnectionState.Open;

                    // send the open frame to the endpoint
                    _sendFrame(FrameType.ConnectionOpen, Frame.CreateConnectionOpenFrame());

                    // signal that the connection is open
                    _connectionOpen.Set();
                }
            } break;

            case FrameType.ConnectionReject:
            {
                if (State == ConnectionState.ConnectionStartSent)
                {
                    Log.DebugFormat("[{0}] FrameType.ConnectionReject received", ID);

                    // transition to end state
                    State = ConnectionState.End;

                    // close everything
                    _closeInternal();
                }
            } break;

            case FrameType.ConnectionOpen:
            {
                // used for both client and server endpoints
                if (State == ConnectionState.ConnectionAcceptSent || State == ConnectionState.ConnectionOpenSent)
                {
                    Log.DebugFormat("[{0}] FrameType.ConnectionOpen received", ID);

                    // connection is now open - set state
                    State = ConnectionState.Open;

                    // signal that connection is open
                    _connectionOpen.Set();
                }
            } break;

            case FrameType.ConnectionClose:
            {
                // valid in any state (except errored - but shoulnd't have received it in the first place)

                Log.DebugFormat("[{0}] FrameType.ConnectionClose received", ID);

                // close the connection
                State = ConnectionState.End;

                // close the physcial connection and any associated components
                _closeInternal();

                // raise the closed event
                OnClosed?.Invoke(this, null);
            } break;

                #endregion

                #region Session Frame Handling

            case FrameType.SessionStart:
            {
                var descriptor = BsonCodec.Decode <SessionDescriptor>(frame.Payload);

                // TODO: getting a null descriptor here!
                Log.DebugFormat("[{0}] FrameType.SessionStart received: ID [{1}], Name [{2}]", ID, frame.SessionID, descriptor.Name);

                // validate
                if (_pendingSessions.ContainsKey(frame.SessionID))
                {
                    Log.DebugFormat("[{0}] Received SessionStart from when one is already pending", ID);
                    return;
                }

                if (_activeSessions.ContainsKey(frame.SessionID))
                {
                    Log.DebugFormat("[{0}] Received SessionStart from when one is already active", ID);
                    return;
                }

                // create the incoming session and add to the pending dictionary
                var session = new Session(this, descriptor, frame.SessionID);
                _pendingSessions.Add(frame.SessionID, session);

                // notify application
                _ = Task.Run(() => OnSession?.Invoke(this, session));
            } break;

            case FrameType.SessionAccept:
            {
                Log.DebugFormat("[{0}] FrameType.SessionAccept received: ID [{1}]", ID, frame.SessionID);

                // move to active session but do not trigger Settle until Open received
                Session session;
                if (_pendingSessions.Remove(frame.SessionID, out session))
                {
                    // move session from pending to active
                    _activeSessions.Add(frame.SessionID, session);

                    Log.DebugFormat("[{0}] Session moved from pending to active: ID [{1}], Pending [{2}], Active [{3}]", ID, frame.SessionID, _pendingSessions.Count, _activeSessions.Count);

                    // send session open
                    _sendFrame(FrameType.SessionOpen, Frame.CreateSessionOpenFrame(frame.SessionID, null));
                }
            } break;

            case FrameType.SessionReject:
            {
                Log.DebugFormat("[{0}] FrameType.SessionReject received: ID [{1}]", ID, frame.SessionID);

                _pendingSessions.Remove(frame.SessionID);

                Log.DebugFormat("[{0}] Session removed from pending sessions: ID [{1}], Pending [{2}], Active [{3}]", ID, frame.SessionID, _pendingSessions.Count, _activeSessions.Count);
            } break;

            case FrameType.SessionOpen:
            {
                Log.DebugFormat("[{0}] FrameType.SessionOpen received: ID [{1}]", ID, frame.SessionID);

                Session session;
                if (_pendingSessions.Remove(frame.SessionID, out session))
                {
                    // move session from pending to active
                    _activeSessions.Add(frame.SessionID, session);

                    Log.DebugFormat("[{0}] Session moved from pending to active: ID [{1}], Pending [{2}], Active [{3}]", ID, frame.SessionID, _pendingSessions.Count, _activeSessions.Count);

                    // respond with session open
                    _sendFrame(FrameType.SessionOpen, Frame.CreateSessionOpenFrame(frame.SessionID, null));

                    // signal that the session is now Open
                    session.NotifySessionOpen();
                }
                else if (_activeSessions.ContainsKey(frame.SessionID))
                {
                    // signal that the session is now Open
                    _activeSessions[frame.SessionID].NotifySessionOpen();
                }
            }
            break;

            case FrameType.SessionClose:
            {
                Log.DebugFormat("[{0}] FrameType.SessionClose received: ID [{1}]", ID, frame.SessionID);

                Session session;
                if (_activeSessions.Remove(frame.SessionID, out session))
                {
                    // remove session from active collection
                    _activeSessions.Remove(frame.SessionID);

                    // call close on the session
                    session.Close();
                }
            } break;

                #endregion

                #region Message Frame Handling

            case FrameType.MessageSend:
            case FrameType.MessageAccept:
            case FrameType.MessageReject:
            {
                Log.DebugFormat("[{0}] {1} received", ID, frame.Type.ToString());

                var sessionId = frame.SessionID;
                if (_activeSessions.ContainsKey(sessionId))
                {
                    _activeSessions[sessionId].NotifyMessage(frame.Type, frame.MessageID, frame.Payload);
                }
            } break;

                #endregion

            default:
                break;
            }
        }
Пример #11
0
        /// <summary>
        /// Creates a new session between both endpoints
        /// </summary>
        /// <param name="name">The application-defined name for the session</param>
        /// <param name="data">Optional payload data to be sent to the remote endpoint</param>
        /// <returns>The created session</returns>
        public Session CreateSession(string name, byte[] data = null)
        {
            if (State != ConnectionState.Open)
            {
                throw new ConnectionException();
            }

            uint id = uint.MaxValue;

            try
            {
                // create the session descriptor which is sent to the remote endpoint
                var descriptor = new SessionDescriptor
                {
                    Name = name,
                    Data = data
                };

                // construct new session frame
                Session session;
                lock (_pendingSessions)
                {
                    id      = _sessionIdProvider.Next();
                    session = new Session(this, descriptor, id);
                    _pendingSessions.Add(session.ID, session);
                }

                // send the session start frame
                _sendFrame(FrameType.SessionStart, Frame.CreateSessionStartFrame(session.ID, BsonCodec.Encode(descriptor)));

                // block until the connection is settled or a timeout occurs
                var index = WaitHandle.WaitAny(new[] { session.Settled, session.SessionError }, 5000);

                switch (index)
                {
                // session was settled (application needs to check if it was accepted/rejected
                case 0: return(session);

                // the session was rejected by the remote endpoint
                case 1: throw new SessionRejectedException($"The session was rejected by the remote endpoint");

                // the session creation timedout
                case WaitHandle.WaitTimeout: throw new SessionTimeoutException($"Session settlement timed out after {5000} ms");

                // should never hit here, but have to return something
                default: throw new Exception();
                }
            }
            catch (Exception)
            {
                lock (_pendingSessions)
                {
                    if (_pendingSessions.ContainsKey(id))
                    {
                        _pendingSessions.Remove(id);
                    }

                    _sessionIdProvider.Remove(id);
                }

                throw;
            }
        }