From ecc2dd71287b46db316d8ef050a0bdb22bfc0fa5 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 1 Jan 2010 16:11:48 -0500 Subject: [PATCH] Improved POST and error handling in `be serve` POST handling: Drop the cgi.FieldStorage() in favor of the old urlparse.parse_qs(). We need a dictionary, which FieldStorage is not. However, I added .read_post_data() since my old self.rfile.read() was hanging. The read_post_data() implementation comes from the FieldStorage.__init__(). Error handling: wrap .handle_*() blocks in try/except to handle Storage errors --- libbe/command/serve.py | 65 ++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/libbe/command/serve.py b/libbe/command/serve.py index 9afaa90..1aa2305 100644 --- a/libbe/command/serve.py +++ b/libbe/command/serve.py @@ -18,7 +18,6 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import BaseHTTPServer as server -import cgi import posixpath import urllib import urlparse @@ -32,6 +31,10 @@ HTTP_USER_ERROR = 418 STORAGE = None COMMAND = None +# Maximum input we will accept when REQUEST_METHOD is POST +# 0 ==> unlimited input +MAXLEN = 0 + class BERequestHandler (server.BaseHTTPRequestHandler): """Simple HTTP request handler for serving the libbe.storage.http.HTTP backend with GET, POST, and HEAD commands. @@ -61,16 +64,23 @@ class BERequestHandler (server.BaseHTTPRequestHandler): return None data = self.parse_query(query) - if path == ['children']: - content,ctype = self.handle_children(data) - elif len(path) > 1 and path[0] == 'get': - content,ctype = self.handle_get('/'.join(path[1:]), data) - elif path == ['revision-id']: - content,ctype = self.handle_revision_id(data) - elif path == ['version']: - content,ctype = self.handle_version(data) - else: - self.send_error(400, 'File not found') + try: + if path == ['children']: + content,ctype = self.handle_children(data) + elif len(path) > 1 and path[0] == 'get': + content,ctype = self.handle_get('/'.join(path[1:]), data) + elif path == ['revision-id']: + content,ctype = self.handle_revision_id(data) + elif path == ['version']: + content,ctype = self.handle_version(data) + else: + self.send_error(400, 'File not found') + return None + except libbe.storage.NotReadable, e: + self.send_error(403, 'Read permission denied') + return None + except libbe.storage.InvalidID, e: + self.send_error(HTTP_USER_ERROR, 'InvalidID %s' % e) return None if content != None: self.send_header('Content-type', ctype) @@ -88,12 +98,8 @@ class BERequestHandler (server.BaseHTTPRequestHandler): self.s = STORAGE self.c = COMMAND self.log_request('POST') - data = cgi.FieldStorage( - fp=self.rfile, - headers=self.headers, - environ={'REQUEST_METHOD':'POST', - 'CONTENT_TYPE':self.headers['Content-Type'], - }) + post_data = self.read_post_data() + data = self.parse_post(post_data) path,query,fragment = self.parse_path(self.path) if query != '': self.send_error( @@ -118,6 +124,10 @@ class BERequestHandler (server.BaseHTTPRequestHandler): except libbe.storage.NotWriteable, e: self.send_error(403, 'Write permission denied') return None + except libbe.storage.InvalidID, e: + raise + self.send_error(HTTP_USER_ERROR, 'InvalidID %s' % e) + return None if content != None: self.send_header('Content-type', ctype) self.send_header('Content-Length', len(content)) @@ -184,11 +194,7 @@ class BERequestHandler (server.BaseHTTPRequestHandler): if not 'revision' in data or data['revision'] == 'None': data['revision'] = None revision = data['revision'] - try: - content = self.s.get(id, revision) - except libbe.storage.InvalidID, e: - self.send_error(HTTP_USER_ERROR, 'InvalidID %s' % e) - return None + content = self.s.get(id, revision) be_version = self.s.storage_version(revision) ctype = 'application/octet-stream' self.send_response(200) @@ -262,6 +268,21 @@ class BERequestHandler (server.BaseHTTPRequestHandler): data[k] = v[0] return data + def parse_post(self, post): + return self.parse_query(post) + + def read_post_data(self): + clen = -1 + if 'content-length' in self.headers: + try: + clen = int(self.headers['content-length']) + except ValueError: + pass + if MAXLEN > 0 and clen > MAXLEN: + raise ValueError, 'Maximum content length exceeded' + post_data = self.rfile.read(clen) + return post_data + class Serve (libbe.command.Command): """Serve a Storage backend for the HTTP storage client -- 2.26.2