From db7ad6da128609d2c14e480a9f54383b839d77e2 Mon Sep 17 00:00:00 2001 From: francois <francois@ctrlaltdel.ch> Date: Tue, 18 Nov 2008 07:41:35 -0700 Subject: [PATCH] filenode.py: Fix partial HTTP Range header handling according to RFC2616 Tahoe webapi was failing on HTTP request containing a partial Range header. This change allows movies players like mplayer to seek in movie files stored in tahoe. Associated tests for GET and HEAD methods are also included --- src/allmydata/test/test_web.py | 27 +++++++++++++++++++++++++++ src/allmydata/web/filenode.py | 9 ++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py index 6e7a1175..64e6a59e 100644 --- a/src/allmydata/test/test_web.py +++ b/src/allmydata/test/test_web.py @@ -552,6 +552,20 @@ class Web(WebMixin, testutil.StallMixin, unittest.TestCase): d.addCallback(_got) return d + def test_GET_FILEURL_partial_range(self): + headers = {"range": "bytes=5-"} + length = len(self.BAR_CONTENTS) + d = self.GET(self.public_url + "/foo/bar.txt", headers=headers, + return_response=True) + def _got((res, status, headers)): + self.failUnlessEqual(int(status), 206) + self.failUnless(headers.has_key("content-range")) + self.failUnlessEqual(headers["content-range"][0], + "bytes 5-%d/%d" % (length-1, length)) + self.failUnlessEqual(res, self.BAR_CONTENTS[5:]) + d.addCallback(_got) + return d + def test_HEAD_FILEURL_range(self): headers = {"range": "bytes=1-10"} d = self.HEAD(self.public_url + "/foo/bar.txt", headers=headers, @@ -565,6 +579,19 @@ class Web(WebMixin, testutil.StallMixin, unittest.TestCase): d.addCallback(_got) return d + def test_HEAD_FILEURL_partial_range(self): + headers = {"range": "bytes=5-"} + length = len(self.BAR_CONTENTS) + d = self.HEAD(self.public_url + "/foo/bar.txt", headers=headers, + return_response=True) + def _got((res, status, headers)): + self.failUnlessEqual(int(status), 206) + self.failUnless(headers.has_key("content-range")) + self.failUnlessEqual(headers["content-range"][0], + "bytes 5-%d/%d" % (length-1, length)) + d.addCallback(_got) + return d + def test_GET_FILEURL_range_bad(self): headers = {"range": "BOGUS=fizbop-quarnak"} d = self.shouldFail2(error.Error, "test_GET_FILEURL_range_bad", diff --git a/src/allmydata/web/filenode.py b/src/allmydata/web/filenode.py index 4c200794..e8d52229 100644 --- a/src/allmydata/web/filenode.py +++ b/src/allmydata/web/filenode.py @@ -374,7 +374,14 @@ class FileDownloader(rend.Page): start, end = bytesrange[1].split('-') if start: offset = int(start) - if end: + if not end: + # RFC 2616 says: + # + # "If the last-byte-pos value is absent, or if the value is + # greater than or equal to the current length of the + # entity-body, last-byte-pos is taken to be equal to one less + # than the current length of the entity- body in bytes." + end = filesize - 1 size = int(end) - offset + 1 req.setResponseCode(http.PARTIAL_CONTENT) req.setHeader('content-range',"bytes %s-%s/%s" % -- 2.45.2