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); } }
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); }