public static void Merge(this IResponseMetadata metadataA, IResponseMetadata metadataB, bool useGreater = true) { metadataA.Date = metadataA.Date.GetValue(metadataB.Date, useGreater); metadataA.LastModified = metadataA.LastModified.GetValue(metadataB.LastModified, useGreater); metadataA.Expiration = metadataA.Expiration.GetValue(metadataB.Expiration, useGreater); if (metadataB.ShouldRevalidate) { metadataA.ShouldRevalidate = metadataB.ShouldRevalidate; } if (metadataB.ETag != null) { metadataA.ETag = (metadataA.ETag ?? string.Empty) + metadataB.ETag; } foreach (var header in metadataB.VaryHeaders.Where(header => !metadataA.VaryHeaders.Contains(header))) { metadataA.VaryHeaders.Add(header); } foreach (var uri in metadataB.DependentUris.Where(uri => !metadataA.DependentUris.Contains(uri))) { metadataA.DependentUris.Add(uri); } }
/// <summary> /// Gets the request ID for logging. /// </summary> /// <param name="responseHeaders">The response headers.</param> /// <param name="response">The response.</param> /// <param name="exception">The exception, if available.</param> /// <returns>The request ID.</returns> public string GetRequestId(Metadata responseHeaders, object response, RpcException exception) { string requestId = new AdsResponseMetadata(responseHeaders).RequestId; // For streaming calls, the trailing response headers are returned only after // the entire stream is read, whereas we write summary logs each time we retrieve // a response object from the stream. As a result, the requestId in summary logs // appear blank in all except the last entry. As a fix, we read the request Id // from the stream response object as a fallback. if (string.IsNullOrEmpty(requestId)) { IResponseMetadata responseMetadata = response as IResponseMetadata; if (responseMetadata != null) { requestId = responseMetadata.RequestId; } } if (string.IsNullOrEmpty(requestId)) { AdsBaseException adsException = exception as AdsBaseException; if (adsException != null) { requestId = adsException.RequestId; } } return(requestId); }
public static IResponse <Target> MapMetadata <Target>(IResponseMetadata source) where Target : class { var target = new Response <Target>(); target.Errors = source.Errors; target.Messages = source.Messages; return(target); }
public static bool AllowStale(HttpRequestMessage request, IResponseMetadata responseMetadata) { //This is almost verbatim from the CacheCow HttpCallCacheHandler's IsFreshOrStaleAcceptable if (responseMetadata == null) { throw new ArgumentException(SR.CacheContextResponseInfoRequiredError, nameof(responseMetadata)); } if (request == null) { throw new ArgumentException(SR.CacheContextRequestMessageRequiredError, nameof(request)); } if (!responseMetadata.HasContent) { return(false); } if (responseMetadata.Expiration < DateTimeOffset.UtcNow) { return(false); } var responseDate = responseMetadata.LastModified ?? responseMetadata.Date; var staleness = DateTimeOffset.UtcNow - responseDate; if (request.Headers.CacheControl == null) { return(staleness < TimeSpan.Zero); } if (request.Headers.CacheControl.MinFresh.HasValue) { return(-staleness > request.Headers.CacheControl.MinFresh.Value); // staleness is negative if still fresh } if (request.Headers.CacheControl.MaxStale) // stale acceptable { return(true); } if (request.Headers.CacheControl.MaxStaleLimit.HasValue) { return(staleness < request.Headers.CacheControl.MaxStaleLimit.Value); } if (request.Headers.CacheControl.MaxAge.HasValue) { return(responseDate.Add(request.Headers.CacheControl.MaxAge.Value) > DateTimeOffset.Now); } return(false); }
public static ResponseValidationResult ValidateResponse(IResponseMetadata responseMetadata, ISet <HttpStatusCode> cacheableHttpStatusCodes) { //This is almost verbatim from the CacheCow HttpCacheHandler's ResponseValidator func // 13.4 //Unless specifically constrained by a cache-control (section 14.9) directive, a caching system MAY always store // a successful response (see section 13.8) as a cache entry, MAY return it without validation if it // is fresh, and MAY return it after successful validation. If there is neither a cache validator nor an // explicit expiration time associated with a response, we do not expect it to be cached, but certain caches MAY violate this expectation // (for example, when little or no network connectivity is available). // 14.9.1 // If the no-cache directive does not specify a field-name, then a cache MUST NOT use the response to satisfy a subsequent request without // successful revalidation with the origin server. This allows an origin server to prevent caching // even by caches that have been configured to return stale responses to client requests. //If the no-cache directive does specify one or more field-names, then a cache MAY use the response // to satisfy a subsequent request, subject to any other restrictions on caching. However, the specified // field-name(s) MUST NOT be sent in the response to a subsequent request without successful revalidation // with the origin server. This allows an origin server to prevent the re-use of certain header fields in a metadata, while still allowing caching of the rest of the response. if (!cacheableHttpStatusCodes.Contains(responseMetadata.StatusCode)) { return(ResponseValidationResult.NotCacheable); } if (responseMetadata.NoStore) { return(ResponseValidationResult.NotCacheable); } if (!responseMetadata.HasContent) { return(ResponseValidationResult.NotCacheable); } if (!responseMetadata.Expiration.HasValue) { return(ResponseValidationResult.NotCacheable); } if (responseMetadata.NoCache) { return(ResponseValidationResult.MustRevalidate); } if (responseMetadata.Expiration < DateTimeOffset.UtcNow) { return(responseMetadata.ShouldRevalidate ? ResponseValidationResult.MustRevalidate : ResponseValidationResult.Stale); } return(ResponseValidationResult.OK); }
private static IEnumerable <Uri> GetDependentUris(IResponseMetadata metadata, object result, IEnumerable <Uri> dependentUris) { var uris = (dependentUris ?? Enumerable.Empty <Uri>()).Concat(metadata.DependentUris); var cacheableResult = result as ICacheableHttpResult; if (cacheableResult?.DependentUris != null) { uris = uris.Concat(cacheableResult.DependentUris); } return(uris.Normalize()); }
public static bool ShouldRevalidate(HttpRequestMessage request, IResponseMetadata responseMetadata, ISet <HttpMethod> cacheableHttpMethods) { if (request == null) { return(false); } if (!cacheableHttpMethods.Contains(request.Method)) { return(false); } return(responseMetadata != null && responseMetadata.StatusCode == HttpStatusCode.NotModified); }
/// <summary> /// Initializes a new instance of the <see cref="AdsResponseMetadata"/> class. /// </summary> /// <param name="metadata">The metadata.</param> public AdsResponseMetadata(IResponseMetadata metadata) : base() { RequestId = metadata?.RequestId; }
public CacheEntry(IResponseMetadata metadata) { Metadata = metadata; }
public CacheEntry(object value, IResponseMetadata metadata) : this(metadata) { Value = value; }
/// <summary> /// Called when a response metadata is received. /// </summary> /// <param name="responseMetadata">The response metadata.</param> internal void OnResponseMetadataReceived(IResponseMetadata responseMetadata) { Client?.OnResponseMetadataReceived?.Invoke(this, responseMetadata); }