Bug 462707 - nsHttpChannel::GetEntityID should respect Accept-Ranges response header; r,sr=biesi a191=beltzner
authorEhsan Akhgari <ehsan.akhgari@gmail.com>
Fri, 28 Nov 2008 15:28:57 -0800
changeset 22105 356e3865c629263d172eaad3d8b548355301bd74
parent 22104 1551ebc4dbaa3a01459ec7ba6e2b42e7472430c4
child 22108 a07d428bd7073ef8d8da4c4c47197602f115db21
push id3801
push usersdwilsh@shawnwilsher.com
push dateFri, 28 Nov 2008 23:36:09 +0000
treeherdermozilla-central@aadde428c8ce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi
bugs462707
milestone1.9.1b3pre
Bug 462707 - nsHttpChannel::GetEntityID should respect Accept-Ranges response header; r,sr=biesi a191=beltzner
netwerk/protocol/http/src/nsHttpChannel.cpp
netwerk/test/unit/test_resumable_channel.js
--- a/netwerk/protocol/http/src/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/src/nsHttpChannel.cpp
@@ -5121,16 +5121,27 @@ NS_IMETHODIMP
 nsHttpChannel::GetEntityID(nsACString& aEntityID)
 {
     // Don't return an entity ID for Non-GET requests which require
     // additional data
     if (mRequestHead.Method() != nsHttp::Get) {
         return NS_ERROR_NOT_RESUMABLE;
     }
 
+    // Don't return an entity if the server sent the following header:
+    // Accept-Ranges: none
+    // Not sending the Accept-Ranges header means we can still try
+    // sending range requests.
+    const char* acceptRanges =
+        mResponseHead->PeekHeader(nsHttp::Accept_Ranges);
+    if (acceptRanges &&
+        !nsHttp::FindToken(acceptRanges, "bytes", HTTP_HEADER_VALUE_SEPS)) {
+        return NS_ERROR_NOT_RESUMABLE;
+    }
+
     PRUint64 size = LL_MAXUINT;
     nsCAutoString etag, lastmod;
     if (mResponseHead) {
         size = mResponseHead->TotalEntitySize();
         const char* cLastMod = mResponseHead->PeekHeader(nsHttp::Last_Modified);
         if (cLastMod)
             lastmod = cLastMod;
         const char* cEtag = mResponseHead->PeekHeader(nsHttp::ETag);
--- a/netwerk/test/unit/test_resumable_channel.js
+++ b/netwerk/test/unit/test_resumable_channel.js
@@ -67,16 +67,17 @@ Requestor.prototype = {
   prompt2: null
 };
 
 function run_test() {
   dump("*** run_test\n");
   httpserver = new nsHttpServer();
   httpserver.registerPathHandler("/auth", authHandler);
   httpserver.registerPathHandler("/range", rangeHandler);
+  httpserver.registerPathHandler("/acceptranges", acceptRangesHandler);
   httpserver.registerPathHandler("/redir", redirHandler);
 
   var entityID;
 
   function get_entity_id(request, data, ctx) {
     do_check_true(request instanceof Ci.nsIResumableChannel,
                   "must be a resumable channel");
     entityID = request.entityID;
@@ -96,16 +97,92 @@ function run_test() {
     chan.nsIResumableChannel.resumeAt(1, entityID);
     chan.asyncOpen(new ChannelListener(try_resume_zero, null), null);
   }
 
   function try_resume_zero(request, data, ctx) {
     do_check_true(request.nsIHttpChannel.requestSucceeded);
     do_check_eq(data, rangeBody.substring(1));
 
+    // Try a server which doesn't support range requests
+    var chan = make_channel("http://localhost:4444/acceptranges");
+    chan.nsIResumableChannel.resumeAt(0, entityID);
+    chan.nsIHttpChannel.setRequestHeader("X-Range-Type", "none", false);
+    chan.asyncOpen(new ChannelListener(try_no_range, null, CL_EXPECT_FAILURE), null);
+  }
+
+  function try_no_range(request, data, ctx) {
+    do_check_true(request.nsIHttpChannel.requestSucceeded);
+    do_check_eq(request.status, NS_ERROR_NOT_RESUMABLE);
+
+    // Try a server which supports "bytes" range requests
+    var chan = make_channel("http://localhost:4444/acceptranges");
+    chan.nsIResumableChannel.resumeAt(0, entityID);
+    chan.nsIHttpChannel.setRequestHeader("X-Range-Type", "bytes", false);
+    chan.asyncOpen(new ChannelListener(try_bytes_range, null), null);
+  }
+
+  function try_bytes_range(request, data, ctx) {
+    do_check_true(request.nsIHttpChannel.requestSucceeded);
+    do_check_eq(data, rangeBody);
+
+    // Try a server which supports "foo" and "bar" range requests
+    var chan = make_channel("http://localhost:4444/acceptranges");
+    chan.nsIResumableChannel.resumeAt(0, entityID);
+    chan.nsIHttpChannel.setRequestHeader("X-Range-Type", "foo, bar", false);
+    chan.asyncOpen(new ChannelListener(try_foo_bar_range, null, CL_EXPECT_FAILURE), null);
+  }
+
+  function try_foo_bar_range(request, data, ctx) {
+    do_check_true(request.nsIHttpChannel.requestSucceeded);
+    do_check_eq(request.status, NS_ERROR_NOT_RESUMABLE);
+
+    // Try a server which supports "foobar" range requests
+    var chan = make_channel("http://localhost:4444/acceptranges");
+    chan.nsIResumableChannel.resumeAt(0, entityID);
+    chan.nsIHttpChannel.setRequestHeader("X-Range-Type", "foobar", false);
+    chan.asyncOpen(new ChannelListener(try_foobar_range, null, CL_EXPECT_FAILURE), null);
+  }
+
+  function try_foobar_range(request, data, ctx) {
+    do_check_true(request.nsIHttpChannel.requestSucceeded);
+    do_check_eq(request.status, NS_ERROR_NOT_RESUMABLE);
+
+    // Try a server which supports "bytes" and "foobar" range requests
+    var chan = make_channel("http://localhost:4444/acceptranges");
+    chan.nsIResumableChannel.resumeAt(0, entityID);
+    chan.nsIHttpChannel.setRequestHeader("X-Range-Type", "bytes, foobar", false);
+    chan.asyncOpen(new ChannelListener(try_bytes_foobar_range, null), null);
+  }
+
+  function try_bytes_foobar_range(request, data, ctx) {
+    do_check_true(request.nsIHttpChannel.requestSucceeded);
+    do_check_eq(data, rangeBody);
+
+    // Try a server which supports "bytesfoo" and "bar" range requests
+    var chan = make_channel("http://localhost:4444/acceptranges");
+    chan.nsIResumableChannel.resumeAt(0, entityID);
+    chan.nsIHttpChannel.setRequestHeader("X-Range-Type", "bytesfoo, bar", false);
+    chan.asyncOpen(new ChannelListener(try_bytesfoo_bar_range, null, CL_EXPECT_FAILURE), null);
+  }
+
+  function try_bytesfoo_bar_range(request, data, ctx) {
+    do_check_true(request.nsIHttpChannel.requestSucceeded);
+    do_check_eq(request.status, NS_ERROR_NOT_RESUMABLE);
+
+    // Try a server which doesn't send Accept-Ranges header at all
+    var chan = make_channel("http://localhost:4444/acceptranges");
+    chan.nsIResumableChannel.resumeAt(0, entityID);
+    chan.asyncOpen(new ChannelListener(try_no_accept_ranges, null), null);
+  }
+
+  function try_no_accept_ranges(request, data, ctx) {
+    do_check_true(request.nsIHttpChannel.requestSucceeded);
+    do_check_eq(data, rangeBody);
+
     // Try a successful resume from 0
     var chan = make_channel("http://localhost:4444/range");
     chan.nsIResumableChannel.resumeAt(0, entityID);
     chan.asyncOpen(new ChannelListener(success, null), null);
   }
 
   function success(request, data, ctx) {
     do_check_true(request.nsIHttpChannel.requestSucceeded);
@@ -265,16 +342,24 @@ function rangeHandler(metadata, response
     // always respond to successful range requests with 206
     response.setStatusLine(metadata.httpVersion, 206, "Partial Content");
     response.setHeader("Content-Range", from + "-" + to + "/" + rangeBody.length, false);
   }
 
   response.bodyOutputStream.write(body, body.length);
 }
 
+// /acceptranges
+function acceptRangesHandler(metadata, response) {
+  response.setHeader("Content-Type", "text/html", false);
+  if (metadata.hasHeader("X-Range-Type"))
+    response.setHeader("Accept-Ranges", metadata.getHeader("X-Range-Type"), false);
+  response.bodyOutputStream.write(rangeBody, rangeBody.length);
+}
+
 // /redir
 function redirHandler(metadata, response) {
   response.setStatusLine(metadata.httpVersion, 302, "Found");
   response.setHeader("Content-Type", "text/html", false);
   response.setHeader("Location", metadata.getHeader("X-Redir-To"), false);
   var body = "redirect\r\n";
   response.bodyOutputStream.write(body, body.length);
 }