]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
web: transform FileTooLargeError into a friendlier '413 Request Entity Too Large...
authorBrian Warner <warner@allmydata.com>
Tue, 3 Jun 2008 07:03:16 +0000 (00:03 -0700)
committerBrian Warner <warner@allmydata.com>
Tue, 3 Jun 2008 07:03:16 +0000 (00:03 -0700)
src/allmydata/test/common.py
src/allmydata/test/test_web.py
src/allmydata/web/common.py

index cf709d760a154439f9ed065362afb42c2684bf65..694000c9609be6e08afdd17b915ddd5511511216 100644 (file)
@@ -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()
index 90a7be0de3019529e58b949aa000686d5851fc85..35379ed5bba7d03805f1c54c3f9f7c8f0c0b3c98 100644 (file)
@@ -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
index a2e4b9e8f511a5da91ba3b0300468cb13fd95a11..f11e62a839236745263c88efda1937f4a60502dc 100644 (file)
@@ -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