예제 #1
0
        void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e)
        {
            if (!Plugin.Instance.Configuration.SelectedGUIDs.Contains(e.Session.UserId.HasValue ? e.Session.UserId.Value.ToString("N") : null))
            {
                return;
            }
            Plugin.DebugLogger("Session manager PlaybackStopped!");

            //Remove state from dict if last client stopped playing..
            SessionDescriptor descr = new SessionDescriptor((Guid)e.Session.UserId,
                                                            e.Session.FullNowPlayingItem.Id);

            if (content_state.ContainsKey(descr))
            {
                int usercount = _sessionManager.Sessions.Where(
                    s => e.Session.UserId == s.UserId &&
                    e.Session.FullNowPlayingItem.Id == s.FullNowPlayingItem.Id &&
                    s.IsActive &&
                    s.Id != e.Session.Id
                    ).Count();

                if (usercount == 0)
                {
                    content_state.Remove(descr);
                    Plugin.DebugLogger($"Removed Session form Dict. Remainig Sessions: {content_state.Count}");
                }
                else
                {
                    Plugin.DebugLogger($"Other Clients ({usercount}) still active on this session! Remainig Sessions: {content_state.Count}");
                }
            }
        }
예제 #2
0
        public async Task UploadFilePart(MultipartUploadSession uploadSession, Stream inputStream, int templateCode)
        {
            if (SessionDescriptor.IsSessionExpired(uploadSession.SessionExpiresAt))
            {
                throw new SessionExpiredException(uploadSession.SessionId, uploadSession.SessionExpiresAt);
            }

            if (uploadSession.NextPartNumber == 1)
            {
                var sessionDescriptor = uploadSession.SessionDescriptor;
                var elementDescriptor = uploadSession.ElementDescriptor;
                EnsureFileHeaderIsValid(
                    templateCode,
                    uploadSession.ElementDescriptor.Type,
                    elementDescriptor.Constraints.For(sessionDescriptor.Language),
                    inputStream,
                    uploadSession.UploadedFileMetadata);
            }

            var key = uploadSession.SessionId.AsS3ObjectKey(uploadSession.FileKey);

            inputStream.Position = 0;

            var response = await _cephS3Client.UploadPartAsync(
                new UploadPartRequest
            {
                BucketName  = _filesBucketName,
                Key         = key,
                UploadId    = uploadSession.UploadId,
                InputStream = inputStream,
                PartNumber  = uploadSession.NextPartNumber
            });

            uploadSession.AddPart(response.ETag);
        }
예제 #3
0
 internal Session(Connection connection, SessionDescriptor sessionDescriptor, uint id)
 {
     Connection           = connection;
     Connection.OnClosed += _onConnectionClosed;
     Descriptor           = sessionDescriptor;
     ID = id;
     _messageIdProvider = new IdProvider(Connection.IsClient ? 0u : 1u, 2);
 }
예제 #4
0
 public MultipartUploadSession(Guid sessionId, SessionDescriptor sessionDescriptor, DateTime expiresAt, IElementDescriptor elementDescriptor, string fileKey, string fileName, string uploadId)
 {
     SessionId         = sessionId;
     SessionDescriptor = sessionDescriptor;
     ElementDescriptor = elementDescriptor;
     FileKey           = fileKey;
     FileName          = fileName;
     UploadId          = uploadId;
     SessionExpiresAt  = expiresAt;
 }
예제 #5
0
        void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
        {
            if (!Plugin.Instance.Configuration.SelectedGUIDs.Contains(e.Session.UserId.HasValue ? e.Session.UserId.Value.ToString("N") : null))
            {
                return;
            }
            Plugin.DebugLogger("Session manager PlaybackStart!");
            SessionDescriptor descr = new SessionDescriptor((Guid)e.Session.UserId,
                                                            e.Session.FullNowPlayingItem.Id);

            // First one to play
            if (!content_state.ContainsKey(descr))
            {
                content_state[descr] = new TogetherSession(TogetherSession.State.playing)
                {
                    ticks = e.PlaybackPositionTicks
                };
                Plugin.DebugLogger("Created new EmbyTogether Session!");
            }
            // Someone else is already playing, seek to their location
            else
            {
                Plugin.DebugLogger("New Client joining to established Session!");
                // Lets get the first matching session with matching userid and content id
                var sessions = _sessionManager.Sessions.Where(
                    s => e.Session.UserId == s.UserId &&
                    e.Session.FullNowPlayingItem.Id == s.FullNowPlayingItem.Id &&
                    s.Id != e.Session.Id &&    // force different session, otherwise we might pick up ourselves
                    s.IsActive
                    );

                if (sessions.Count() > 0)
                {
                    // Get ticks from this session and seek new user there
                    var session = sessions.First();

                    long last_update = session.LastActivityDate.Ticks;
                    long current     = DateTime.Now.Ticks;
                    Plugin.DebugLogger($"Difference in ticks: {last_update} {current} {current - last_update}!");
                    send_seek_command(session.PlayState.PositionTicks, e.Session.Id, e.Session.UserId);

                    // Is the current state paused? If yes, pause new user
                    if (content_state[descr].state == TogetherSession.State.paused)
                    {
                        send_command(PlaystateCommand.Pause, e.Session.Id, e.Session.UserId);
                    }
                }
                else
                {
                    Plugin.DebugLogger("Session exists, but without users. This should never happen!");
                }
            }
        }
예제 #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
        void _sessionManager_PlaybackProgress(object sender, PlaybackProgressEventArgs e)
        {
            if (!Plugin.Instance.Configuration.SelectedGUIDs.Contains(e.Session.UserId.HasValue ? e.Session.UserId.Value.ToString("N") : null))
            {
                return;
            }
            //Plugin.DebugLogger($"Session manager PlaybackProgress! {e.PlaybackPositionTicks}, {e.IsPaused}, {e.Session.UserId.Value.ToString("N")}, {e.Session.FullNowPlayingItem.Id}");
            Plugin.DebugLogger($"{e.Session.Id}: Session manager PlaybackProgress::! {e.PlaybackPositionTicks}, {e.IsPaused}");


            SessionDescriptor descr = new SessionDescriptor((Guid)e.Session.UserId,
                                                            e.Session.FullNowPlayingItem.Id);

            // Playback Progress might get triggered before playback started --> key might not exist yet
            if (!content_state.ContainsKey(descr))
            {
                Plugin.DebugLogger($"{e.Session.Id}: Key not Found!");
                return;
            }
            TogetherSession ts = content_state[descr];

            // only check play/pause changes at most once per 5 seconds, to avoid feedback loops (reduced to 1, if same user issued last command)
            Plugin.DebugLogger($"{e.Session.Id}: Time from last command: {(DateTime.Now - ts.last_update).TotalSeconds}");
            if (((DateTime.Now - ts.last_update).TotalSeconds > Plugin.Instance.Configuration.Timeoffset_cmd_same_user && ts.last_commander_uid == e.Session.UserId) ||
                ((DateTime.Now - ts.last_update).TotalSeconds > Plugin.Instance.Configuration.Timeoffset_cmd_diff_user))
            {
                List <PlaystateRequest> commands = ProcessClientstate(e, ts);

                int    num     = 0;
                string targets = $"{e.Session.Id}: Send to: ";
                //only send commands to sessions of the same user which play the same content
                foreach (SessionInfo s in _sessionManager.Sessions.Where(s =>
                                                                         e.Session.UserId == s.UserId &&
                                                                         e.Session.FullNowPlayingItem.Id == s.FullNowPlayingItem.Id &&
                                                                         s.Id != e.Session.Id))
                {
                    // Plugin.DebugLogger($"{e.Session.Id}: Sending command to #{num} {s.UserId.Value.ToString("N")}, {s.FullNowPlayingItem.Id}");
                    num++;
                    foreach (var command in commands)
                    {
                        _sessionManager.SendPlaystateCommand(s.Id, s.Id, command, CancellationToken.None);
                    }
                    targets += $"{s.Id}; ";
                }
                Plugin.DebugLogger(targets);
            }
            else
            {
                Plugin.DebugLogger($"{e.Session.Id}: Not scanning package for commands, since it arrived too soon!");
            }
        }
 public MultipartUploadSession(
     Guid sessionId,
     SessionDescriptor sessionDescriptor,
     DateTime expiresAt,
     IElementDescriptor elementDescriptor,
     IUploadedFileMetadata uploadedFileMetadata,
     string fileKey,
     string uploadId)
 {
     SessionId            = sessionId;
     SessionDescriptor    = sessionDescriptor;
     ElementDescriptor    = elementDescriptor;
     UploadedFileMetadata = uploadedFileMetadata;
     FileKey          = fileKey;
     UploadId         = uploadId;
     SessionExpiresAt = expiresAt;
 }
예제 #9
0
        public async Task <MultipartUploadSession> InitiateMultipartUploadInternal(
            Guid sessionId,
            int templateCode,
            IUploadedFileMetadata uploadedFileMetadata,
            SessionDescriptor sessionDescriptor,
            DateTime expiresAt)
        {
            if (string.IsNullOrEmpty(uploadedFileMetadata.FileName))
            {
                throw new MissingFilenameException($"Filename has not been provided for the item '{templateCode}'");
            }

            if (sessionDescriptor.BinaryElementTemplateCodes.All(x => x != templateCode))
            {
                throw new InvalidTemplateException(
                          $"Binary content is not expected for the item '{templateCode}' within template '{sessionDescriptor.TemplateId}' " +
                          $"with version Id '{sessionDescriptor.TemplateVersionId}'.");
            }

            var elementDescriptor = await GetElementDescriptor(sessionDescriptor.TemplateId, sessionDescriptor.TemplateVersionId, templateCode);

            BinaryValidationUtils.EnsureFileMetadataIsValid(elementDescriptor, sessionDescriptor.Language, uploadedFileMetadata);

            var fileKey = Guid.NewGuid().ToString();
            var key     = sessionId.AsS3ObjectKey(fileKey);
            var request = new InitiateMultipartUploadRequest
            {
                BucketName  = _filesBucketName,
                Key         = key,
                ContentType = uploadedFileMetadata.ContentType
            };
            var metadataWrapper = MetadataCollectionWrapper.For(request.Metadata);

            metadataWrapper.Write(MetadataElement.Filename, uploadedFileMetadata.FileName);

            var uploadResponse = await _cephS3Client.InitiateMultipartUploadAsync(request);

            return(new MultipartUploadSession(
                       sessionId,
                       sessionDescriptor,
                       expiresAt,
                       elementDescriptor,
                       uploadedFileMetadata,
                       fileKey,
                       uploadResponse.UploadId));
        }
예제 #10
0
        public CDEFSession(SessionDescriptor to, SessionDescriptor from, 
                           CDEFConsumer parent, ILogger errorlLog, bool isConnected, bool autoConnect)
        {
            mOwner = parent;
            mLogger = errorlLog;
            mAutoConnect = autoConnect;
            mTo = to;
            mFrom = from;
            mkey = new SessionKey(to, from);
            mSessionPermConnected = isConnected;

            if (mSessionPermConnected)
            {
                mSessionEstablished.Set();
            }

            TimerCallback timerSessDelegate = new TimerCallback(ReconnectSessionCb);
            mReconSessTimer = new Timer(timerSessDelegate, null, Timeout.Infinite, Timeout.Infinite);
        }
예제 #11
0
        public async Task Setup(Guid sessionId, long templateId, string templateVersionId, Language language, AuthorInfo authorInfo)
        {
            if (language == Language.Unspecified)
            {
                throw new SessionCannotBeCreatedException("Language must be explicitly specified.");
            }

            var templateDescriptor = await _templatesStorageReader.GetTemplateDescriptor(templateId, templateVersionId);

            var sessionDescriptor = new SessionDescriptor
            {
                TemplateId                 = templateDescriptor.Id,
                TemplateVersionId          = templateDescriptor.VersionId,
                Language                   = language,
                BinaryElementTemplateCodes = templateDescriptor.GetBinaryElementTemplateCodes()
            };
            var request = new PutObjectRequest
            {
                BucketName  = _filesBucketName,
                Key         = sessionId.AsS3ObjectKey(Tokens.SessionPostfix),
                CannedACL   = S3CannedACL.PublicRead,
                ContentType = ContentType.Json,
                ContentBody = JsonConvert.SerializeObject(sessionDescriptor, SerializerSettings.Default)
            };

            var expiresAt       = SessionDescriptor.CurrentTime().Add(_sessionExpiration);
            var metadataWrapper = MetadataCollectionWrapper.For(request.Metadata);

            metadataWrapper.Write(MetadataElement.ExpiresAt, expiresAt);
            metadataWrapper.Write(MetadataElement.Author, authorInfo.Author);
            metadataWrapper.Write(MetadataElement.AuthorLogin, authorInfo.AuthorLogin);
            metadataWrapper.Write(MetadataElement.AuthorName, authorInfo.AuthorName);

            await _eventSender.SendAsync(_sessionsTopicName, new SessionCreatingEvent(sessionId, expiresAt));

            await _cephS3Client.PutObjectAsync(request);

            _createdSessionsMetric.Inc();
        }
예제 #12
0
        protected override void BeginProcessing()
        {
            base.BeginProcessing();

            var TDRUrl     = "https://protect.cylance.com/Reports/ThreatDataReportV1/";
            var APIAuthUrl = "https://protectapi.cylance.com/auth/v2/token";

            switch (Region)
            {
            case "apne1":
            case "au":
            case "euc1":
            case "sae1":
                TDRUrl     = $"https://protect-{Region}.cylance.com/Reports/ThreatDataReportV1/";
                APIAuthUrl = $"https://protectapi-{Region}.cylance.com/auth/v2/token";
                break;

            case "us-gov":
                TDRUrl     = "https://protect.us.cylance.com/Reports/ThreatDataReportV1/";
                APIAuthUrl = "https://protectapi.us.cylance.com/auth/v2/token";
                break;
            }

            var apiSecret = ConvertToSecureString("APISecret", APISecret);

            var console = ConfigurationManager.Default.New();

            console.ConsoleId = Console;
            console.APIId     = APIId;
            console.SetAPISecret(apiSecret);
            console.APITenantId = APITenantId;
            console.APIUrl      = APIAuthUrl;
            console.TDRUrl      = TDRUrl;

            var apiSession = new SessionDescriptor
            {
                APIId       = console.APIId,
                APISecret   = console.APISecret.Value,
                APITenantId = console.APITenantId,
                APIBaseUrl  = console.APIUrl,
                Proxy       = (string.IsNullOrEmpty(ProxyServer)) ? null : new System.Net.WebProxy(ProxyServer)
            };

            ApiConnectionHandle session = new ApiConnectionHandle(apiSession);
            ApiV2 Connection            = new ApiV2(apiSession);

            Connection.ConnectAsync().Wait();

            if (null != LoginPassword)
            {
                var loginPassword = ConvertToSecureString("LoginPassword", LoginPassword);
                console.SetLoginPassword(loginPassword);
            }

            if (!string.IsNullOrEmpty(LoginUser))
            {
                console.LoginUser = LoginUser;
            }

            WriteVerbose($"Writing new console entry '{console.ConsoleId}' for API ID {console.APIId}, Tenant ID {console.APITenantId}, API auth URL {console.APIUrl}, TDR URL {console.TDRUrl}");

            ConfigurationManager.Default.Write(console);
        }
예제 #13
0
        public async Task <string> CompleteMultipartUpload(MultipartUploadSession uploadSession)
        {
            var uploadKey      = uploadSession.SessionId.AsS3ObjectKey(uploadSession.FileKey);
            var partETags      = uploadSession.Parts.Select(x => new PartETag(x.PartNumber, x.Etag)).ToList();
            var uploadResponse = await _cephS3Client.CompleteMultipartUploadAsync(
                new CompleteMultipartUploadRequest
            {
                BucketName = _filesBucketName,
                Key        = uploadKey,
                UploadId   = uploadSession.UploadId,
                PartETags  = partETags
            });

            uploadSession.Complete();

            if (SessionDescriptor.IsSessionExpired(uploadSession.SessionExpiresAt))
            {
                throw new SessionExpiredException(uploadSession.SessionId, uploadSession.SessionExpiresAt);
            }

            try
            {
                using (var getResponse = await _cephS3Client.GetObjectAsync(_filesBucketName, uploadKey))
                {
                    var memoryStream = new MemoryStream();
                    using (getResponse.ResponseStream)
                    {
                        getResponse.ResponseStream.CopyTo(memoryStream);
                        memoryStream.Position = 0;
                    }

                    using (memoryStream)
                    {
                        var sessionDescriptor = uploadSession.SessionDescriptor;
                        var elementDescriptor = uploadSession.ElementDescriptor;
                        EnsureFileContentIsValid(
                            elementDescriptor.TemplateCode,
                            elementDescriptor.Type,
                            elementDescriptor.Constraints.For(sessionDescriptor.Language),
                            memoryStream,
                            uploadSession.UploadedFileMetadata);
                    }

                    var metadataWrapper = MetadataCollectionWrapper.For(getResponse.Metadata);
                    var fileName        = metadataWrapper.Read <string>(MetadataElement.Filename);

                    var fileExtension = Path.GetExtension(fileName).ToLowerInvariant();
                    var fileKey       = Path.ChangeExtension(uploadSession.SessionId.AsS3ObjectKey(uploadResponse.ETag), fileExtension);
                    var copyRequest   = new CopyObjectRequest
                    {
                        ContentType       = uploadSession.UploadedFileMetadata.ContentType,
                        SourceBucket      = _filesBucketName,
                        SourceKey         = uploadKey,
                        DestinationBucket = _filesBucketName,
                        DestinationKey    = fileKey,
                        MetadataDirective = S3MetadataDirective.REPLACE,
                        CannedACL         = S3CannedACL.PublicRead
                    };
                    foreach (var metadataKey in getResponse.Metadata.Keys)
                    {
                        copyRequest.Metadata.Add(metadataKey, getResponse.Metadata[metadataKey]);
                    }

                    await _cephS3Client.CopyObjectAsync(copyRequest);

                    _uploadedBinariesMetric.Inc();

                    _memoryCache.Set(fileKey, new BinaryMetadata(fileName, getResponse.ContentLength), uploadSession.SessionExpiresAt);

                    return(fileKey);
                }
            }
            finally
            {
                await _cephS3Client.DeleteObjectAsync(_filesBucketName, uploadKey);
            }
        }
예제 #14
0
 public SessionKey(SessionDescriptor to, SessionDescriptor from)
 {
     mToAddr = to;
     mFromAddr = from;
 }
예제 #15
0
        /// <summary>
        /// We are only interested in some function codes
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="idx"></param>
        /// <param name="bufferLength"></param>
        /// <returns></returns>
        public virtual bool InterestedCb(byte[] buffer, int idx, int bufferLength)
        {
            SessionDescriptor toDesc = new SessionDescriptor(CDEFMessage.Get32bitNr(buffer, idx + 4));

            //
            // Handle session msgs
            //
            if (buffer[CDEFMessage.SESS + idx] == 2)
            {
                return true;
            }
            if (mFrom == null || (toDesc.AddressType != mFrom.AddressType)
                              || (toDesc.SalesLocation != mFrom.SalesLocation)
                              || (toDesc.UniqueAddress != mFrom.UniqueAddress))
            {
                return false;
            }
            else
            {
                return mOwner.InterestedCb(this, buffer, idx, bufferLength);
            }
        }
예제 #16
0
 public ClientSession(ClientConnection clientConnection, SessionInfo sessionInfo, SessionDescriptor sessionDescriptor)
 {
     _clientConnection  = clientConnection;
     _sessionInfo       = sessionInfo;
     _sessionDescriptor = sessionDescriptor;
 }
예제 #17
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);
        }
예제 #18
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;
            }
        }