public async Task <RemoteUpdateResult> UpdateAsync(RemoteUpdateResult lastResult, CancellationToken cancellationToken)
        {
            if (!enabled)
            {
                return(CreateEmptyResult(lastResult));
            }

            var request         = CreateRequest(lastResult?.Version);
            var requestPriority = lastResult == null ? RequestPriority.Critical : RequestPriority.Ordinary;
            var requestResult   = await client.SendAsync(request, priority : requestPriority, cancellationToken : cancellationToken).ConfigureAwait(false);

            if (cancellationToken.IsCancellationRequested)
            {
                throw new OperationCanceledException();
            }

            var updateResult = TryHandleFailure(requestResult, lastResult);

            if (updateResult != null)
            {
                return(updateResult);
            }

            switch (requestResult.Response.Code)
            {
            case ResponseCode.NotModified:
                return(HandleNotModifiedResponse(lastResult));

            case ResponseCode.Ok:
                return(HandleSuccessResponse(lastResult, requestResult.Response, requestResult.Replica));
            }

            throw NoAcceptableResponseException(requestResult);
        }
        private RemoteUpdateResult HandleSuccessResponse(RemoteUpdateResult lastUpdateResult, Response response, Uri replica)
        {
            if (!response.HasContent)
            {
                throw Empty200ResponseException();
            }

            if (response.Headers.LastModified == null)
            {
                throw MissingLastModifiedHeaderException();
            }

            var version = DateTime.Parse(response.Headers.LastModified, null, DateTimeStyles.AssumeUniversal).ToUniversalTime();

            if (lastUpdateResult != null && version <= lastUpdateResult.Version)
            {
                if (version < lastUpdateResult.Version)
                {
                    LogStaleVersion(version, lastUpdateResult.Version);
                }

                return(new RemoteUpdateResult(false, lastUpdateResult.Tree, lastUpdateResult.Version));
            }

            var tree = new RemoteTree(response.Content.ToArray(), TreeSerializers.V1);

            LogReceivedNewZone(tree, version, replica);

            return(new RemoteUpdateResult(true, tree, version));
        }
        private RemoteUpdateResult HandleNotModifiedResponse(RemoteUpdateResult lastUpdateResult)
        {
            if (lastUpdateResult == null)
            {
                throw UnexpectedNotModifiedResponseException();
            }

            return(new RemoteUpdateResult(false, lastUpdateResult.Tree, lastUpdateResult.Version));
        }
        private RemoteUpdateResult TryHandleFailure(ClusterResult requestResult, RemoteUpdateResult lastUpdateResult)
        {
            switch (requestResult.Status)
            {
            case ClusterResultStatus.ReplicasNotFound:
                // (iloktionov): If no replicas were resolved during update and we haven't seen any non-trivial settings earlier,
                // (iloktionov): we just silently assume CC is not deployed in current environment and return empty settings.

                // (tsup): We make assumptions above in case we are not forced to assume that cluster config is deployed.
                if (lastUpdateResult?.Tree == null && !assumeClusterConfigDeployed)
                {
                    if (lastUpdateResult == null)
                    {
                        LogAssumingNoServer();
                    }

                    return(CreateEmptyResult(lastUpdateResult));
                }

                break;

            case ClusterResultStatus.ReplicasExhausted:
                // (iloktionov): Getting here means that no replica returned a 200 or 304 response.
                // (iloktionov): If at least some of them responded with 404, it's reasonably safe to assume that zone does not exist.
                if (requestResult.ReplicaResults.Any(r => r.Response.Code == ResponseCode.NotFound))
                {
                    var updateResult = CreateEmptyResult(lastUpdateResult);
                    if (updateResult.Changed)
                    {
                        LogZoneDoesNotExist();
                    }

                    return(updateResult);
                }

                break;
            }

            return(null);
        }
 private static RemoteUpdateResult CreateEmptyResult([CanBeNull] RemoteUpdateResult lastResult)
 => new RemoteUpdateResult(lastResult?.Version != EmptyResultVersion, null, EmptyResultVersion);