private async Task <(EndPointAddress endPoint, string securityToken)> AddClientCoreAsync(CancellationToken cancellation)
        {
            var now                     = _dateTimeProvider.GetCurrentTime();
            var securityToken           = Guid.NewGuid().ToString();
            var leaseEnd                = now + Timeout;
            var securityTokenBytesCount = Encoding.UTF8.GetByteCount(securityToken);

            using (ArrayPool <byte> .Shared.Rent(8 + 4 + securityTokenBytesCount, out var bytes))
            {
                var payloadLength = EncodePayload(bytes, securityToken.AsSpan(), leaseEnd);
                var payload       = bytes.AsMemory().Slice(start: 0, payloadLength);

                EndPointAddress endPoint;
                do
                {
                    endPoint = AllocateEndPointAddress();
                    var path = GetPath(endPoint);

                    try
                    {
                        await _coordinationManager.CreateAsync(path, payload, cancellation : cancellation);

                        _logger?.LogDebug($"Assigning end-point '{endPoint.ToString()}' to recently connected client.");

                        break;
                    }
                    catch (DuplicateEntryException) // TODO: Add a TryCreateAsync method to the coordination service.
                    {
                        continue;
                    }
                }while (cancellation.ThrowOrContinue());

                return(endPoint, securityToken);
            }
        }
Beispiel #2
0
        private static CoordinationEntryPath GetPrefixPath(ReadOnlyMemory <char> prefix, EndPointAddress endPoint, Session session, bool normalize = true)
        {
            if (normalize)
            {
                prefix = NormalizePrefix(prefix);
            }

            var uniqueEntryName = IdGenerator.GenerateId(endPoint.ToString(), session.ToString());

            return(_rootPrefixesPath.GetChildPath(prefix, uniqueEntryName.AsMemory()));
        }
 private CoordinationEntryPath GetPath(EndPointAddress endPoint)
 {
     return(BasePath.GetChildPath(endPoint.ToString()));
 }
        private async Task <bool> ValidateClientCoreAsync(EndPointAddress endPoint,
                                                          string securityToken,
                                                          CancellationToken cancellation)
        {
            var now  = _dateTimeProvider.GetCurrentTime();
            var path = GetPath(endPoint);

            do
            {
                var entry = await _coordinationManager.GetAsync(path, cancellation : cancellation);

                if (entry == null)
                {
                    _logger?.LogDebug($"Cannot update session for client with end-point '{endPoint.ToString()}'. Session is terminated.");

                    return(false);
                }

                var(comparandSecurityToken, leaseEnd) = DecodePayload(entry.Value.Span);

                // We have to assume that the client is not connected anymore.
                // This is a race condition, that has to be prevented.
                if (now >= leaseEnd)
                {
                    await _coordinationManager.DeleteAsync(entry.Path, cancellation : cancellation);

                    _logger?.LogDebug($"Session for client with end-point '{endPoint.ToString()}' is terminated. Removing entry.");
                    _logger?.LogDebug($"Cannot update session for client with end-point '{endPoint.ToString()}'. Session is terminated.");
                    return(false);
                }

                if (securityToken != comparandSecurityToken)
                {
                    _logger?.LogDebug($"Cannot update session for client with end-point '{endPoint.ToString()}'. Session is terminated.");
                    return(false);
                }

                var newLeaseEnd = now + Timeout;

                if (newLeaseEnd > leaseEnd)
                {
                    leaseEnd = newLeaseEnd;
                }

                var securityTokenBytesCount = Encoding.UTF8.GetByteCount(securityToken);

                using (ArrayPool <byte> .Shared.Rent(8 + 4 + securityTokenBytesCount, out var bytes))
                {
                    var payloadLength = EncodePayload(bytes, securityToken.AsSpan(), leaseEnd);
                    var payload       = bytes.AsMemory().Slice(start: 0, payloadLength);
                    endPoint = new EndPointAddress("client/" + Guid.NewGuid().ToString());

                    var version = await _coordinationManager.SetValueAsync(path, payload, version : entry.Version, cancellation : cancellation);

                    if (version == entry.Version)
                    {
                        _logger?.LogDebug($"Updated session for client with end-point '{endPoint.ToString()}'.");

                        return(true);
                    }
                }
            }while (true);
        }