Search code examples
asp.netasp.net-mvcjplayerlib.web.mvc

Mixing OutputCache and Dynamic Range Requests in ASP.NET MVC


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.


Solution

  • 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.