From: Brian Warner Date: Tue, 3 Jun 2008 07:03:16 +0000 (-0700) Subject: web: transform FileTooLargeError into a friendlier '413 Request Entity Too Large... X-Git-Tag: allmydata-tahoe-1.1.0~53 X-Git-Url: https://git.rkrishnan.org/specifications/something?a=commitdiff_plain;h=32c89a8d594673a9f142a89dc8c359faaf120a7a;p=tahoe-lafs%2Ftahoe-lafs.git web: transform FileTooLargeError into a friendlier '413 Request Entity Too Large' error --- diff --git a/src/allmydata/test/common.py b/src/allmydata/test/common.py index cf709d76..694000c9 100644 --- a/src/allmydata/test/common.py +++ b/src/allmydata/test/common.py @@ -5,7 +5,8 @@ from twisted.internet import defer from twisted.python import failure from twisted.application import service from allmydata import uri, dirnode -from allmydata.interfaces import IURI, IMutableFileNode, IFileNode +from allmydata.interfaces import IURI, IMutableFileNode, IFileNode, \ + FileTooLargeError from allmydata.encode import NotEnoughSharesError from allmydata.util import log @@ -70,12 +71,18 @@ class FakeMutableFileNode: class-level dictionary.""" implements(IMutableFileNode) + MUTABLE_SIZELIMIT = 10000 all_contents = {} + def __init__(self, client): self.client = client self.my_uri = make_mutable_file_uri() self.storage_index = self.my_uri.storage_index def create(self, initial_contents, key_generator=None): + if len(initial_contents) > self.MUTABLE_SIZELIMIT: + raise FileTooLargeError("SDMF is limited to one segment, and " + "%d > %d" % (len(initial_contents), + self.MUTABLE_SIZELIMIT)) self.all_contents[self.storage_index] = initial_contents return defer.succeed(self) def init_from_uri(self, myuri): @@ -100,10 +107,15 @@ class FakeMutableFileNode: def download_best_version(self): return defer.succeed(self.all_contents[self.storage_index]) def overwrite(self, new_contents): + if len(new_contents) > self.MUTABLE_SIZELIMIT: + raise FileTooLargeError("SDMF is limited to one segment, and " + "%d > %d" % (len(new_contents), + self.MUTABLE_SIZELIMIT)) assert not self.is_readonly() self.all_contents[self.storage_index] = new_contents return defer.succeed(None) def modify(self, modifier): + # this does not implement FileTooLargeError, but the real one does return defer.maybeDeferred(self._modify, modifier) def _modify(self, modifier): assert not self.is_readonly() diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py index 90a7be0d..35379ed5 100644 --- a/src/allmydata/test/test_web.py +++ b/src/allmydata/test/test_web.py @@ -62,6 +62,7 @@ class FakeClient(service.MultiService): d.addCallback(lambda res: n) return d + MUTABLE_SIZELIMIT = FakeMutableFileNode.MUTABLE_SIZELIMIT def create_mutable_file(self, contents=""): n = FakeMutableFileNode(self) return n.create(contents) @@ -288,12 +289,13 @@ class WebMixin(object): res.trap(expected_failure) if substring: self.failUnless(substring in str(res), - "substring '%s' not in '%s'" - % (substring, str(res))) + "%s: substring '%s' not in '%s'" + % (which, substring, str(res))) if response_substring: self.failUnless(response_substring in res.value.response, - "response substring '%s' not in '%s'" - % (response_substring, res.value.response)) + "%s: response substring '%s' not in '%s'" + % (which, + response_substring, res.value.response)) else: self.fail("%s was supposed to raise %s, not get '%s'" % (which, expected_failure, res)) @@ -607,6 +609,15 @@ class Web(WebMixin, unittest.TestCase): self.NEWFILE_CONTENTS)) return d + def test_PUT_NEWFILEURL_mutable_toobig(self): + d = self.shouldFail2(error.Error, "test_PUT_NEWFILEURL_mutable_toobig", + "413 Request Entity Too Large", + "SDMF is limited to one segment, and 10001 > 10000", + self.PUT, + self.public_url + "/foo/new.txt?mutable=true", + "b" * (self.s.MUTABLE_SIZELIMIT+1)) + return d + def test_PUT_NEWFILEURL_replace(self): d = self.PUT(self.public_url + "/foo/bar.txt", self.NEWFILE_CONTENTS) # TODO: we lose the response code, so we can't check this @@ -1052,6 +1063,17 @@ class Web(WebMixin, unittest.TestCase): d.addCallback(_check4) return d + def test_POST_upload_no_link_mutable_toobig(self): + d = self.shouldFail2(error.Error, + "test_POST_upload_no_link_mutable_toobig", + "413 Request Entity Too Large", + "SDMF is limited to one segment, and 10001 > 10000", + self.POST, + "/uri", t="upload", mutable="true", + file=("new.txt", + "b" * (self.s.MUTABLE_SIZELIMIT+1)) ) + return d + def test_POST_upload_mutable(self): # this creates a mutable file d = self.POST(self.public_url + "/foo", t="upload", mutable="true", @@ -1196,9 +1218,34 @@ class Web(WebMixin, unittest.TestCase): self.failUnlessEqual(headers["content-type"], ["text/plain"]) d.addCallback(_got_headers) + # make sure that size errors are displayed correctly for overwrite + d.addCallback(lambda res: + self.shouldFail2(error.Error, + "test_POST_upload_mutable-toobig", + "413 Request Entity Too Large", + "SDMF is limited to one segment, and 10001 > 10000", + self.POST, + self.public_url + "/foo", t="upload", + mutable="true", + file=("new.txt", + "b" * (self.s.MUTABLE_SIZELIMIT+1)), + )) + d.addErrback(self.dump_error) return d + def test_POST_upload_mutable_toobig(self): + d = self.shouldFail2(error.Error, + "test_POST_upload_no_link_mutable_toobig", + "413 Request Entity Too Large", + "SDMF is limited to one segment, and 10001 > 10000", + self.POST, + self.public_url + "/foo", + t="upload", mutable="true", + file=("new.txt", + "b" * (self.s.MUTABLE_SIZELIMIT+1)) ) + return d + def dump_error(self, f): # if the web server returns an error code (like 400 Bad Request), # web.client.getPage puts the HTTP response body into the .response diff --git a/src/allmydata/web/common.py b/src/allmydata/web/common.py index a2e4b9e8..f11e62a8 100644 --- a/src/allmydata/web/common.py +++ b/src/allmydata/web/common.py @@ -4,7 +4,7 @@ from zope.interface import Interface from nevow import loaders, appserver from nevow.inevow import IRequest from nevow.util import resource_filename -from allmydata.interfaces import ExistingChildError +from allmydata.interfaces import ExistingChildError, FileTooLargeError class IClient(Interface): pass @@ -114,6 +114,8 @@ class MyExceptionHandler(appserver.DefaultExceptionHandler): http.CONFLICT) elif f.check(WebError): return self.simple(ctx, f.value.text, f.value.code) + elif f.check(FileTooLargeError): + return self.simple(ctx, str(f.value), http.REQUEST_ENTITY_TOO_LARGE) elif f.check(server.UnsupportedMethod): # twisted.web.server.Request.render() has support for transforming # this into an appropriate 501 NOT_IMPLEMENTED or 405 NOT_ALLOWED