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); }
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); }
public static byte[] CreateConnectionAcceptFrame() { var descriptor = new ConnectionDescriptor { ProtocolVersion = ProtocolVersion, MaxFrameSize = MaxFrameSize }; return(_createFrame(FrameType.ConnectionAccept, BsonCodec.Encode(descriptor))); }
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))); }
public static byte[] CreateConnectionRejectFrame(string error = null) { var descriptor = new ConnectionDescriptor { ProtocolVersion = ProtocolVersion, MaxFrameSize = MaxFrameSize, Error = error }; return(_createFrame(FrameType.ConnectionReject, BsonCodec.Encode(descriptor))); }
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); }
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"]); }
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); }
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); }
/// <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; } }
/// <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; } }