I'm using the RangeFilePathResult class to serve mp3 files from an MVC controller.
The action is defined as follows:
[CacheFilter]
[OutputCache(CacheProfile = "Mp3Cache")]
public RangeFilePathResult Mp3Completed(string f)
{
FileInfo info = new FileInfo(string.Format("C:\\test\\{0}.mp3", f));
return new RangeFilePathResult("audio/mpeg", info.FullName, info.LastWriteTimeUtc, info.Length);
}
And the cache policy as follows:
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="Mp3Cache" duration="3600" varyByParam="f" location="Any" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
Why does this work correctly as is? It seems that you would need explicit varyByHeader to ensure that range requests work with output caching? The problem I was addressing was that jPlayer on iOS would be unable to display the duration of MP3 files and would render NaN when served with with a traditional FilePathResult - it works under this implementation.
The most important thing here is that the response to range request is not typical 200 OK but 206 Partial Content.
In case of 206 Partial Content several additional conditions must be met:
The request must have included a Range header field indicating the desired range, and may have included an If-Range header field to make the request conditional
The response must include either a Content-Range header field indicating the range included with this response, or a multipart/byteranges Content-Type including Content-Range fields for each part.
The response must include ETag and/or Content-Location (if the header would have been sent in a 200 response to the same request)
The response must include Date
Now every caching mechanism (in case of location="Any" this is browser, proxy server and your hosting IIS) which supports HTTP protocol must know that 206 Partial Content is different than 200 OK and treat it accordingly. Below you can find most important rules of caching 206 Partial Content response:
Cache must not combine a 206 response with other previously cached content if the ETag or Last-Modified headers do not match exactly
Cache that does not support the Range and Content-Range headers must not cache 206 responses
To summary this up, you don't need to use varyByHeader because every cache which follows HTTP protocol knows that in case of 206 Partial Content the Range and Content-Range headers are part of variant.
Related
After reading a lot about caching validators (more intensively after reading this answer on SO), I had a doubt that didn't find the answer anywhere.
My use-case is to serve a static asset (a javascript file, ie: https://example.com/myasset.js) to be used in other websites, so messing with their Gpagespeed/gmetrix score matters the most.
I also need their users to receive updated versions of my static asset every time I deploy new changes.
For this, I have the following response headers:
Cache-Control: max-age=10800
etag: W/"4efa5de1947fe4ce90cf10992fa"
In short, we can see the following flow in terms of how browser behaves using etag
For the first request, the browser has no value for the If-None-Match Request Header, so the Server will send back the status code 200 (Ok), the content itself, and a Response header with ETag value.
For the subsequent requests, the browser will add the previously received ETag value in a form of the If-None-Match Request Header. This way, the server can compare this value with the current value from ETag and, if both match, the server can return 304 (Not Modified) telling the browser to use the latest version of the file, or just 200 followed by the new content and the related ETag value instead.
However, I couldn't find any information in regards to using the Cache-Control: max-age header and how will this affect the above behavior, like:
Will the browser request for new updates before max-age has met? Meaning that I can define a higher max-age value (pagespeed/gmetrix will be happy about it) and force this refresh using only etag fingerprint.
If not, then what are the advantages of using etag and adding extra bits to the network?
No, the browser will not send any requests until max-age has passed.
The advantage of using ETag is that, if the file hasn't changed, you don't need to resend the entire file to the client. The response will be a small 304.
Note that you can achieve the best of both worlds by using the stale-while-revalidate directive, which allows stale responses to be served while the cache silently revalidates the resource in the background.
When the HTTP request contains header Range and the server responds 206 Partial Content, should the response always include Vary: Range? I think because the output will be obviously different for any different Range but I cannot find any official requirement for this.
The Vary seems reduntant here because any user agent supporting partial content should implicitly understand this.
No "Vary" response field is needed here. HTTP Range Requests are not a form of content negotiation as specified.
I am developing my application in asp.net and wanted force refreshing of .js files after certain period of time. In order to test that in local environment, I made following change to my web.config file:
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="must-revalidate max-age=120" />
</customHeaders>
</httpProtocol>
As I understand cache-control, based on above settings, browser cache should expire after 2:00 minutes and any new postback should send a request to webserver to check if newer version of .js is avialable or not. I used fiddler to monitor network traffic and I don't see any request for js file. The browser I am using is Chrome. So I used dev tools to monitor network traffic. I see that the browser is using cached version of that file.
Are there any other settings that would force the browser to expire the cached files?
Your Cache-Control is invalid, try separate values by comma: must-revalidate, max-age=120.
Relevant excerpt from RFC 7234 Hypertext Transfer Protocol (HTTP/1.1): Caching:
Cache-Control = 1#cache-directive
cache-directive = token [ "=" ( token / quoted-string ) ]
And ABNF List Extension from RFC 7230 Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing:
A construct "#" is defined, similar to "*", for defining
comma-delimited lists of elements. The full form is "<n>#<m>element"
indicating at least <n> and at most <m> elements, each separated by a
single comma (",") and optional whitespace (OWS).
Also make sure to put new configuration in right place. For more information check Is it possible to add response http headers in web.config?.
I'm wondering how the browser determines whether a cached resource has expired or not.
Assume that I have set the max-age header to 300. I made a request at 14:00, 3 minutes later I made another request to the same resource. So how can the browser tell the resource haven't expired (the current age which is 180 is less than the max-age)? Does the browser hold a "expiry date" or "current age" for every requested resource? If so how can I inspect the "current age" at the time I made the request?
Check what browsers store in their cache
To have a better understanding on how the browser cache works, check what the browsers actually store in their cache:
Firefox: Navigate to about:cache.
Chrome: Navigate to chrome://cache.
Note that there's a key for each cache entry (requested URL). Associated with the key, you will find the whole response details (status codes, headers and content). With those details, the browser is able to determine the age of a requested resource and whether it's expired or not.
The reference for HTTP caching
The RFC 7234, the current reference for caching in HTTP/1.1, tells you a good part of the story about how cache is supposed to work:
2. Overview of Cache Operation
Proper cache operation preserves the semantics of HTTP transfers
while eliminating the transfer of information already
held in the cache. Although caching is an entirely OPTIONAL feature
of HTTP, it can be assumed that reusing a cached response is
desirable and that such reuse is the default behavior when no
requirement or local configuration prevents it. [...]
Each cache entry consists of a cache key and one or more HTTP
responses corresponding to prior requests that used the same key.
The most common form of cache entry is a successful result of a
retrieval request: i.e., a 200 (OK) response to a GET request, which
contains a representation of the resource identified by the request
target. However, it is also possible to
cache permanent redirects, negative results (e.g., 404 (Not Found)),
incomplete results (e.g., 206 (Partial Content)), and responses to
methods other than GET if the method's definition allows such caching
and defines something suitable for use as a cache key.
The primary cache key consists of the request method and target URI.
However, since HTTP caches in common use today are typically limited
to caching responses to GET, many caches simply decline other methods
and use only the URI as the primary cache key. [...]
Some rules are defined regarding storing responses in caches:
3. Storing Responses in Caches
A cache MUST NOT store a response to any request, unless:
The request method is understood by the cache and defined as being
cacheable, and
the response status code is understood by the cache, and
the no-store cache directive does not appear
in request or response header fields, and
the private response directive does not
appear in the response, if the cache is shared, and
the Authorization header field does
not appear in the request, if the cache is shared, unless the
response explicitly allows it, and
the response either:
contains an Expires header field, or
contains a max-age response directive, or
contains a s-maxage response directive
and the cache is shared, or
contains a Cache Control Extension that
allows it to be cached, or
has a status code that is defined as cacheable by default, or
contains a public response directive.
Note that any of the requirements listed above can be overridden by a cache-control extension. [...]
Usually (but not always) the server providing the resource will provide a Date header, indicating the time at which that resource was requested. Caching entities can use that Date and the current time to find the resource's age. If the Date response header does not appear, that the caching entity will probably mark the resource's request time in other metadata, and use that metadata for computing the age. Another possibly helpful response header to look for is the Last-Modified response header.
So first, you should check if the cached resource has the Date header for your own age calculation. If not present, it will then depend on which specific browser you are using, and how that browser handles caching for Date-less resources. More information on HTTP caching and the various factors involved, can be found in this caching tutorial.
Hope this helps!
I am working on an existing Silverlight file uploader that breaks files into multiple chunks and transmits the file using multiple HTTP requests.
Currently, it sends the start and total byte information on the querystring, but as learning exercise, I'd like to use a more standards-based approach.
I've previously used the HTTP Content-Range header when implementing an endpoint that serves content. Is this header also appropriate to use when posting content from a client to the server?
Yes.
RFC 2616 (HTTP 1.1), Section 14 begins by stating:
For entity-header fields, both sender and recipient refer to either
the client or the server, depending on who sends and who receives the
entity.
Other than that, Section 14.16, which defines the Content-Range header, does not appear to contain any language limiting its use to either the request or response.
Probably not, at least as of 2014 (the original answer is from 2011).
The updated HTTP 1.1 specification, rfc7231 (4.3.3), says the following about valid POST responses:
An origin server indicates response semantics by choosing an
appropriate status code depending on the result of processing the
POST request; almost all of the status codes defined by this
specification might be received in a response to POST (the exceptions
being 206 (Partial Content), 304 (Not Modified), and 416 (Range Not
Satisfiable)).
Given that this language was explicitly added to the updated spec, I doubt the authors intended that the Content-Range header be used with the POST method.