Пример #1
0
        private void ApplySessionToken(DocumentServiceRequest request)
        {
            if (request.Headers != null &&
                !string.IsNullOrEmpty(request.Headers[HttpConstants.HttpHeaders.SessionToken]))
            {
                if (ReplicatedResourceClient.IsMasterResource(request.ResourceType))
                {
                    request.Headers.Remove(HttpConstants.HttpHeaders.SessionToken);
                }
                return; //User is explicitly controlling the session.
            }

            string requestConsistencyLevel = request.Headers[HttpConstants.HttpHeaders.ConsistencyLevel];

            bool sessionConsistency =
                this.defaultConsistencyLevel == ConsistencyLevel.Session ||
                (!string.IsNullOrEmpty(requestConsistencyLevel) &&
                 string.Equals(requestConsistencyLevel, ConsistencyLevel.Session.ToString(), StringComparison.OrdinalIgnoreCase));

            if (!sessionConsistency || ReplicatedResourceClient.IsMasterResource(request.ResourceType))
            {
                return; // Only apply the session token in case of session consistency and when resource is not a master resource
            }

            //Apply the ambient session.
            string sessionToken = this.sessionContainer.ResolveGlobalSessionToken(request);

            if (!string.IsNullOrEmpty(sessionToken))
            {
                request.Headers[HttpConstants.HttpHeaders.SessionToken] = sessionToken;
            }
        }
Пример #2
0
        public virtual async Task <DocumentServiceResponse> ProcessMessageAsync(DocumentServiceRequest request, CancellationToken cancellationToken = default(CancellationToken))
        {
            GatewayStoreModel.ApplySessionToken(
                request,
                this.defaultConsistencyLevel,
                this.sessionContainer);

            DocumentServiceResponse response;

            try
            {
                Uri physicalAddress = GatewayStoreClient.IsFeedRequest(request.OperationType) ? this.GetFeedUri(request) : this.GetEntityUri(request);
                response = await this.gatewayStoreClient.InvokeAsync(request, request.ResourceType, physicalAddress, cancellationToken);
            }
            catch (DocumentClientException exception)
            {
                if ((!ReplicatedResourceClient.IsMasterResource(request.ResourceType)) &&
                    (exception.StatusCode == HttpStatusCode.PreconditionFailed || exception.StatusCode == HttpStatusCode.Conflict ||
                     (exception.StatusCode == HttpStatusCode.NotFound && exception.GetSubStatus() != SubStatusCodes.ReadSessionNotAvailable)))
                {
                    this.CaptureSessionToken(exception.StatusCode, exception.GetSubStatus(), request, exception.Headers);
                }

                throw;
            }

            this.CaptureSessionToken(response.StatusCode, response.SubStatusCode, request, response.Headers);
            return(response);
        }
        public virtual async Task <DocumentServiceResponse> ProcessMessageAsync(DocumentServiceRequest request, CancellationToken cancellationToken = default(CancellationToken))
        {
            this.ApplySessionToken(request);

            DocumentServiceResponse response;

            try
            {
                response = await this.InvokeAsync(request, request.ResourceType, cancellationToken);
            }
            catch (DocumentClientException exception)
            {
                if ((!ReplicatedResourceClient.IsMasterResource(request.ResourceType)) &&
                    (exception.StatusCode == HttpStatusCode.PreconditionFailed || exception.StatusCode == HttpStatusCode.Conflict ||
                     (exception.StatusCode == HttpStatusCode.NotFound && exception.GetSubStatus() != SubStatusCodes.ReadSessionNotAvailable)))
                {
                    this.CaptureSessionToken(request, exception.Headers);
                }

                throw;
            }

            this.CaptureSessionToken(request, response.Headers);
            return(response);
        }
Пример #4
0
        private async Task CaptureSessionTokenAndHandleSplitAsync(
            HttpStatusCode?statusCode,
            SubStatusCodes subStatusCode,
            DocumentServiceRequest request,
            INameValueCollection responseHeaders)
        {
            // Exceptionless can try to capture session token from CompleteResponse
            if (request.IsValidStatusCodeForExceptionlessRetry((int)statusCode, subStatusCode))
            {
                // Not capturing on master resources
                if (ReplicatedResourceClient.IsMasterResource(request.ResourceType))
                {
                    return;
                }

                // Only capturing on 409, 412, 404 && !1002
                if (statusCode != HttpStatusCode.PreconditionFailed &&
                    statusCode != HttpStatusCode.Conflict &&
                    (statusCode != HttpStatusCode.NotFound || subStatusCode == SubStatusCodes.ReadSessionNotAvailable))
                {
                    return;
                }
            }

            if (request.ResourceType == ResourceType.Collection && request.OperationType == OperationType.Delete)
            {
                string resourceId;

                if (request.IsNameBased)
                {
                    resourceId = responseHeaders[HttpConstants.HttpHeaders.OwnerId];
                }
                else
                {
                    resourceId = request.ResourceId;
                }

                this.sessionContainer.ClearTokenByResourceId(resourceId);
            }
            else
            {
                this.sessionContainer.SetSessionToken(request, responseHeaders);
                PartitionKeyRange detectedPartitionKeyRange   = request.RequestContext.ResolvedPartitionKeyRange;
                string            partitionKeyRangeInResponse = responseHeaders[HttpConstants.HttpHeaders.PartitionKeyRangeId];
                if (detectedPartitionKeyRange != null &&
                    !string.IsNullOrEmpty(partitionKeyRangeInResponse) &&
                    !string.IsNullOrEmpty(request.RequestContext.ResolvedCollectionRid) &&
                    !partitionKeyRangeInResponse.Equals(detectedPartitionKeyRange.Id, StringComparison.OrdinalIgnoreCase))
                {
                    // The request ended up being on a different partition unknown to the client, so we better refresh the caches
                    await this.partitionKeyRangeCache.TryGetPartitionKeyRangeByIdAsync(
                        request.RequestContext.ResolvedCollectionRid,
                        partitionKeyRangeInResponse,
                        NoOpTrace.Singleton,
                        forceRefresh : true);
                }
            }
        }
Пример #5
0
 // DEVNOTE: This can be replace with ReplicatedResourceClient.IsMasterOperation on next Direct sync
 internal static bool IsMasterOperation(
     ResourceType resourceType,
     OperationType operationType)
 {
     // Stored procedures, trigger, and user defined functions CRUD operations are done on
     // master so they do not require the session token. Stored procedures execute is not a master operation
     return(ReplicatedResourceClient.IsMasterResource(resourceType) ||
            GatewayStoreModel.IsStoredProcedureCrudOperation(resourceType, operationType) ||
            resourceType == ResourceType.Trigger ||
            resourceType == ResourceType.UserDefinedFunction ||
            operationType == OperationType.QueryPlan);
 }
Пример #6
0
        private void CaptureSessionToken(
            HttpStatusCode?statusCode,
            SubStatusCodes subStatusCode,
            DocumentServiceRequest request,
            INameValueCollection responseHeaders)
        {
            // Exceptionless can try to capture session token from CompleteResponse
            if (request.IsValidStatusCodeForExceptionlessRetry((int)statusCode, subStatusCode))
            {
                // Not capturing on master resources
                if (ReplicatedResourceClient.IsMasterResource(request.ResourceType))
                {
                    return;
                }

                // Only capturing on 409, 412, 404 && !1002
                if (statusCode != HttpStatusCode.PreconditionFailed &&
                    statusCode != HttpStatusCode.Conflict &&
                    (statusCode != HttpStatusCode.NotFound || subStatusCode == SubStatusCodes.ReadSessionNotAvailable))
                {
                    return;
                }
            }

            if (request.ResourceType == ResourceType.Collection && request.OperationType == OperationType.Delete)
            {
                string resourceId;

                if (request.IsNameBased)
                {
                    resourceId = responseHeaders[HttpConstants.HttpHeaders.OwnerId];
                }
                else
                {
                    resourceId = request.ResourceId;
                }

                this.sessionContainer.ClearTokenByResourceId(resourceId);
            }
            else
            {
                this.sessionContainer.SetSessionToken(request, responseHeaders);
            }
        }
Пример #7
0
        private static bool ShouldUpdateSessionToken(
            DocumentServiceRequest request,
            INameValueCollection responseHeaders,
            out ResourceId resourceId,
            out string collectionName)
        {
            resourceId = null;
            string ownerFullName = responseHeaders[HttpConstants.HttpHeaders.OwnerFullName];

            if (string.IsNullOrEmpty(ownerFullName))
            {
                ownerFullName = request.ResourceAddress;
            }

            collectionName = PathsHelper.GetCollectionPath(ownerFullName);
            string resourceIdString;

            if (request.IsNameBased)
            {
                resourceIdString = responseHeaders[HttpConstants.HttpHeaders.OwnerId];
                if (string.IsNullOrEmpty(resourceIdString))
                {
                    resourceIdString = request.ResourceId;
                }
            }
            else
            {
                resourceIdString = request.ResourceId;
            }

            if (!string.IsNullOrEmpty(resourceIdString))
            {
                resourceId = ResourceId.Parse(resourceIdString);

                if (resourceId.DocumentCollection != 0 &&
                    collectionName != null &&
                    !ReplicatedResourceClient.IsReadingFromMaster(request.ResourceType, request.OperationType))
                {
                    return(true);
                }
            }

            return(false);
        }
Пример #8
0
        private void HandleUnsuccessfulStoreResponse(DocumentServiceRequest request, HttpStatusCode?statusCode, SubStatusCodes subStatusCode, INameValueCollection responseHeaders)
        {
            if (request.IsNameBased && statusCode == HttpStatusCode.NotFound &&
                subStatusCode == SubStatusCodes.ReadSessionNotAvailable &&
                request.ClearSessionTokenOnSessionReadFailure)
            {
                // Clear the session token, because the collection name might be reused.
                DefaultTrace.TraceWarning("Clear the the token for named base request {0}", request.ResourceAddress);

                this.sessionContainer.ClearToken(null, request.ResourceAddress, responseHeaders);
            }
            else
            {
                if ((!ReplicatedResourceClient.IsMasterResource(request.ResourceType)) &&
                    (statusCode == HttpStatusCode.PreconditionFailed || statusCode == HttpStatusCode.Conflict ||
                     (statusCode == HttpStatusCode.NotFound && subStatusCode != SubStatusCodes.ReadSessionNotAvailable)))
                {
                    this.CaptureSessionToken(request, responseHeaders);
                }
            }
        }
Пример #9
0
        public virtual async Task <DocumentServiceResponse> ProcessMessageAsync(DocumentServiceRequest request, CancellationToken cancellationToken = default)
        {
            await GatewayStoreModel.ApplySessionTokenAsync(
                request,
                this.defaultConsistencyLevel,
                this.sessionContainer,
                this.partitionKeyRangeCache,
                this.clientCollectionCache,
                this.endpointManager);

            DocumentServiceResponse response;

            try
            {
                Uri physicalAddress = GatewayStoreClient.IsFeedRequest(request.OperationType) ? this.GetFeedUri(request) : this.GetEntityUri(request);
                // Collect region name only for document resources
                if (request.ResourceType.Equals(ResourceType.Document) && this.endpointManager.TryGetLocationForGatewayDiagnostics(request.RequestContext.LocationEndpointToRoute, out string regionName))
                {
                    request.RequestContext.RegionName = regionName;
                }
                response = await this.gatewayStoreClient.InvokeAsync(request, request.ResourceType, physicalAddress, cancellationToken);
            }
            catch (DocumentClientException exception)
            {
                if ((!ReplicatedResourceClient.IsMasterResource(request.ResourceType)) &&
                    (exception.StatusCode == HttpStatusCode.PreconditionFailed || exception.StatusCode == HttpStatusCode.Conflict ||
                     (exception.StatusCode == HttpStatusCode.NotFound && exception.GetSubStatus() != SubStatusCodes.ReadSessionNotAvailable)))
                {
                    await this.CaptureSessionTokenAndHandleSplitAsync(exception.StatusCode, exception.GetSubStatus(), request, exception.Headers);
                }

                throw;
            }

            await this.CaptureSessionTokenAndHandleSplitAsync(response.StatusCode, response.SubStatusCode, request, response.Headers);

            return(response);
        }
        /// <summary>
        /// Resolves the endpoint of the partition for the given request
        /// </summary>
        /// <param name="request">Request for which the partition endpoint resolution is to be performed</param>
        /// <param name="forceRefreshPartitionAddresses">Force refresh the partition's endpoint</param>
        /// <param name="cancellationToken">Cancellation token</param>
        /// <returns>An instance of <see cref="ResolutionResult"/>.</returns>
        private async Task <ResolutionResult> ResolveAddressesAndIdentityAsync(
            DocumentServiceRequest request,
            bool forceRefreshPartitionAddresses,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (request.ServiceIdentity != null)
            {
                // In this case we don't populate request.RequestContext.ResolvedPartitionKeyRangeId,
                // which is needed for session token.
                // The assumption is that:
                //     1. Master requests never use session consistency.
                //     2. Service requests (like collection create etc.) don't use session consistency.
                //     3. Requests which target specific partition of an existing collection will use x-ms-documentdb-partitionkeyrangeid header
                //        to send request to specific partition and will not set request.ServiceIdentity
                ServiceIdentity             identity  = request.ServiceIdentity;
                PartitionAddressInformation addresses = await this.addressCache.TryGetAddressesAsync(request, null, identity, forceRefreshPartitionAddresses, cancellationToken);

                if (addresses == null)
                {
                    DefaultTrace.TraceInformation("Could not get addresses for explicitly specified ServiceIdentity {0}", identity);
                    throw new NotFoundException()
                          {
                              ResourceAddress = request.ResourceAddress
                          };
                }

                return(new ResolutionResult(addresses, identity));
            }

            if (ReplicatedResourceClient.IsReadingFromMaster(request.ResourceType, request.OperationType) && request.PartitionKeyRangeIdentity == null)
            {
                DefaultTrace.TraceInformation("Resolving Master service address, forceMasterRefresh: {0}, currentMaster: {1}",
                                              request.ForceMasterRefresh,
                                              this.masterServiceIdentityProvider?.MasterServiceIdentity);

                // Client implementation, GlobalAddressResolver passes in a null IMasterServiceIdentityProvider, because it doesn't actually use the serviceIdentity
                // in the addressCache.TryGetAddresses method. In GatewayAddressCache.cs, the master address is resolved by making a call to Gateway AddressFeed,
                // not using the serviceIdentity that is passed in
                if (request.ForceMasterRefresh && this.masterServiceIdentityProvider != null)
                {
                    ServiceIdentity previousMasterService = this.masterServiceIdentityProvider.MasterServiceIdentity;
                    await this.masterServiceIdentityProvider.RefreshAsync(previousMasterService, cancellationToken);
                }
                ServiceIdentity             serviceIdentity           = this.masterServiceIdentityProvider?.MasterServiceIdentity;
                PartitionKeyRangeIdentity   partitionKeyRangeIdentity = this.masterPartitionKeyRangeIdentity;
                PartitionAddressInformation addresses = await this.addressCache.TryGetAddressesAsync(
                    request,
                    partitionKeyRangeIdentity,
                    serviceIdentity,
                    forceRefreshPartitionAddresses,
                    cancellationToken);

                if (addresses == null)
                {
                    // This shouldn't really happen.
                    DefaultTrace.TraceCritical("Could not get addresses for master partition {0}", serviceIdentity);
                    throw new NotFoundException()
                          {
                              ResourceAddress = request.ResourceAddress
                          };
                }

                PartitionKeyRange partitionKeyRange = new PartitionKeyRange {
                    Id = PartitionKeyRange.MasterPartitionKeyRangeId
                };
                return(new ResolutionResult(partitionKeyRange, addresses, serviceIdentity));
            }

            bool collectionCacheIsUptoDate = !request.IsNameBased ||
                                             (request.PartitionKeyRangeIdentity != null && request.PartitionKeyRangeIdentity.CollectionRid != null);

            bool collectionRoutingMapCacheIsUptoDate = false;

            ContainerProperties collection = await this.collectionCache.ResolveCollectionAsync(request, cancellationToken);

            CollectionRoutingMap routingMap = await this.collectionRoutingMapCache.TryLookupAsync(
                collection.ResourceId, null, request, cancellationToken);

            if (routingMap != null && request.ForceCollectionRoutingMapRefresh)
            {
                DefaultTrace.TraceInformation(
                    "AddressResolver.ResolveAddressesAndIdentityAsync ForceCollectionRoutingMapRefresh collection.ResourceId = {0}",
                    collection.ResourceId);

                routingMap = await this.collectionRoutingMapCache.TryLookupAsync(collection.ResourceId, routingMap, request, cancellationToken);
            }

            if (request.ForcePartitionKeyRangeRefresh)
            {
                collectionRoutingMapCacheIsUptoDate   = true;
                request.ForcePartitionKeyRangeRefresh = false;
                if (routingMap != null)
                {
                    routingMap = await this.collectionRoutingMapCache.TryLookupAsync(collection.ResourceId, routingMap, request, cancellationToken);
                }
            }

            if (routingMap == null && !collectionCacheIsUptoDate)
            {
                // Routing map was not found by resolved collection rid. Maybe collection rid is outdated.
                // Refresh collection cache and reresolve routing map.
                request.ForceNameCacheRefresh       = true;
                collectionCacheIsUptoDate           = true;
                collectionRoutingMapCacheIsUptoDate = false;
                collection = await this.collectionCache.ResolveCollectionAsync(request, cancellationToken);

                routingMap = await this.collectionRoutingMapCache.TryLookupAsync(
                    collection.ResourceId,
                    previousValue : null,
                    request : request,
                    cancellationToken : cancellationToken);
            }

            AddressResolver.EnsureRoutingMapPresent(request, routingMap, collection);

            // At this point we have both collection and routingMap.
            ResolutionResult result = await this.TryResolveServerPartitionAsync(
                request,
                collection,
                routingMap,
                collectionCacheIsUptoDate,
                collectionRoutingMapCacheIsUptodate : collectionRoutingMapCacheIsUptoDate,
                forceRefreshPartitionAddresses : forceRefreshPartitionAddresses,
                cancellationToken : cancellationToken);

            if (result == null)
            {
                // Couldn't resolve server partition or its addresses.
                // Either collection cache is outdated or routing map cache is outdated.
                if (!collectionCacheIsUptoDate)
                {
                    request.ForceNameCacheRefresh = true;
                    collectionCacheIsUptoDate     = true;
                    collection = await this.collectionCache.ResolveCollectionAsync(request, cancellationToken);

                    if (collection.ResourceId != routingMap.CollectionUniqueId)
                    {
                        // Collection cache was stale. We resolved to new Rid. routing map cache is potentially stale
                        // for this new collection rid. Mark it as such.
                        collectionRoutingMapCacheIsUptoDate = false;
                        routingMap = await this.collectionRoutingMapCache.TryLookupAsync(
                            collection.ResourceId,
                            previousValue : null,
                            request : request,
                            cancellationToken : cancellationToken);
                    }
                }

                if (!collectionRoutingMapCacheIsUptoDate)
                {
                    collectionRoutingMapCacheIsUptoDate = true;
                    routingMap = await this.collectionRoutingMapCache.TryLookupAsync(
                        collection.ResourceId,
                        previousValue : routingMap,
                        request : request,
                        cancellationToken : cancellationToken);
                }

                AddressResolver.EnsureRoutingMapPresent(request, routingMap, collection);

                result = await this.TryResolveServerPartitionAsync(
                    request,
                    collection,
                    routingMap,
                    collectionCacheIsUptodate : true,
                    collectionRoutingMapCacheIsUptodate : true,
                    forceRefreshPartitionAddresses : forceRefreshPartitionAddresses,
                    cancellationToken : cancellationToken);
            }

            if (result == null)
            {
                DefaultTrace.TraceInformation("Couldn't route partitionkeyrange-oblivious request after retry/cache refresh. Collection doesn't exist.");

                // At this point collection cache and routing map caches are refreshed.
                // The only reason we will get here is if collection doesn't exist.
                // Case when partitionkeyrange doesn't exist is handled in the corresponding method.
                throw new NotFoundException()
                      {
                          ResourceAddress = request.ResourceAddress
                      };
            }

            if (request.IsNameBased)
            {
                // Append collection rid.
                // If we resolved collection rid incorrectly because of outdated cache, this can lead
                // to incorrect routing decisions. But backend will validate collection rid and throw
                // InvalidPartitionException if we reach wrong collection.
                // Also this header will be used by backend to inject collection rid into metrics for
                // throttled requests.
                request.Headers[WFConstants.BackendHeaders.CollectionRid] = collection.ResourceId;
            }

            return(result);
        }
Пример #11
0
        public async Task <INameValueCollection> CreateCommonHeadersAsync(FeedOptions feedOptions)
        {
            INameValueCollection requestHeaders = new DictionaryNameValueCollection();

            Cosmos.ConsistencyLevel defaultConsistencyLevel = (Cosmos.ConsistencyLevel) await this.Client.GetDefaultConsistencyLevelAsync();

            Cosmos.ConsistencyLevel?desiredConsistencyLevel = (Cosmos.ConsistencyLevel?) await this.Client.GetDesiredConsistencyLevelAsync();

            if (!string.IsNullOrEmpty(feedOptions.SessionToken) && !ReplicatedResourceClient.IsReadingFromMaster(this.ResourceTypeEnum, OperationType.ReadFeed))
            {
                if (defaultConsistencyLevel == Cosmos.ConsistencyLevel.Session || (desiredConsistencyLevel.HasValue && desiredConsistencyLevel.Value == Cosmos.ConsistencyLevel.Session))
                {
                    // Query across partitions is not supported today. Master resources (for e.g., database)
                    // can span across partitions, whereas server resources (viz: collection, document and attachment)
                    // don't span across partitions. Hence, session token returned by one partition should not be used
                    // when quering resources from another partition.
                    // Since master resources can span across partitions, don't send session token to the backend.
                    // As master resources are sync replicated, we should always get consistent query result for master resources,
                    // irrespective of the chosen replica.
                    // For server resources, which don't span partitions, specify the session token
                    // for correct replica to be chosen for servicing the query result.
                    requestHeaders[HttpConstants.HttpHeaders.SessionToken] = feedOptions.SessionToken;
                }
            }

            requestHeaders[HttpConstants.HttpHeaders.Continuation] = feedOptions.RequestContinuationToken;
            requestHeaders[HttpConstants.HttpHeaders.IsQuery]      = bool.TrueString;

            // Flow the pageSize only when we are not doing client eval
            if (feedOptions.MaxItemCount.HasValue)
            {
                requestHeaders[HttpConstants.HttpHeaders.PageSize] = feedOptions.MaxItemCount.ToString();
            }

            requestHeaders[HttpConstants.HttpHeaders.EnableCrossPartitionQuery] = feedOptions.EnableCrossPartitionQuery.ToString();

            if (feedOptions.MaxDegreeOfParallelism != 0)
            {
                requestHeaders[HttpConstants.HttpHeaders.ParallelizeCrossPartitionQuery] = bool.TrueString;
            }

            if (this.feedOptions.EnableScanInQuery != null)
            {
                requestHeaders[HttpConstants.HttpHeaders.EnableScanInQuery] = this.feedOptions.EnableScanInQuery.ToString();
            }

            if (this.feedOptions.EmitVerboseTracesInQuery != null)
            {
                requestHeaders[HttpConstants.HttpHeaders.EmitVerboseTracesInQuery] = this.feedOptions.EmitVerboseTracesInQuery.ToString();
            }

            if (this.feedOptions.EnableLowPrecisionOrderBy != null)
            {
                requestHeaders[HttpConstants.HttpHeaders.EnableLowPrecisionOrderBy] = this.feedOptions.EnableLowPrecisionOrderBy.ToString();
            }

            if (!string.IsNullOrEmpty(this.feedOptions.FilterBySchemaResourceId))
            {
                requestHeaders[HttpConstants.HttpHeaders.FilterBySchemaResourceId] = this.feedOptions.FilterBySchemaResourceId;
            }

            if (this.feedOptions.ResponseContinuationTokenLimitInKb != null)
            {
                requestHeaders[HttpConstants.HttpHeaders.ResponseContinuationTokenLimitInKB] = this.feedOptions.ResponseContinuationTokenLimitInKb.ToString();
            }

            if (this.feedOptions.ConsistencyLevel.HasValue)
            {
                await this.Client.EnsureValidOverwriteAsync((Documents.ConsistencyLevel) feedOptions.ConsistencyLevel.Value);

                requestHeaders.Set(HttpConstants.HttpHeaders.ConsistencyLevel, this.feedOptions.ConsistencyLevel.Value.ToString());
            }
            else if (desiredConsistencyLevel.HasValue)
            {
                requestHeaders.Set(HttpConstants.HttpHeaders.ConsistencyLevel, desiredConsistencyLevel.Value.ToString());
            }

            if (this.feedOptions.EnumerationDirection.HasValue)
            {
                requestHeaders.Set(HttpConstants.HttpHeaders.EnumerationDirection, this.feedOptions.EnumerationDirection.Value.ToString());
            }

            if (this.feedOptions.ReadFeedKeyType.HasValue)
            {
                requestHeaders.Set(HttpConstants.HttpHeaders.ReadFeedKeyType, this.feedOptions.ReadFeedKeyType.Value.ToString());
            }

            if (this.feedOptions.StartId != null)
            {
                requestHeaders.Set(HttpConstants.HttpHeaders.StartId, this.feedOptions.StartId);
            }

            if (this.feedOptions.EndId != null)
            {
                requestHeaders.Set(HttpConstants.HttpHeaders.EndId, this.feedOptions.EndId);
            }

            if (this.feedOptions.StartEpk != null)
            {
                requestHeaders.Set(HttpConstants.HttpHeaders.StartEpk, this.feedOptions.StartEpk);
            }

            if (this.feedOptions.EndEpk != null)
            {
                requestHeaders.Set(HttpConstants.HttpHeaders.EndEpk, this.feedOptions.EndEpk);
            }

            if (this.feedOptions.PopulateQueryMetrics)
            {
                requestHeaders[HttpConstants.HttpHeaders.PopulateQueryMetrics] = bool.TrueString;
            }

            if (this.feedOptions.ForceQueryScan)
            {
                requestHeaders[HttpConstants.HttpHeaders.ForceQueryScan] = bool.TrueString;
            }

            if (this.feedOptions.MergeStaticId != null)
            {
                requestHeaders.Set(HttpConstants.HttpHeaders.MergeStaticId, this.feedOptions.MergeStaticId);
            }

            if (this.feedOptions.CosmosSerializationFormatOptions != null)
            {
                requestHeaders[HttpConstants.HttpHeaders.ContentSerializationFormat] = this.feedOptions.CosmosSerializationFormatOptions.ContentSerializationFormat;
            }
            else if (this.feedOptions.ContentSerializationFormat.HasValue)
            {
                requestHeaders[HttpConstants.HttpHeaders.ContentSerializationFormat] = this.feedOptions.ContentSerializationFormat.Value.ToString();
            }

            return(requestHeaders);
        }