From 5f26c407789a2f8fc51d89b6d0c590253b50c754 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 1 Jan 2010 14:43:03 -0500 Subject: Added libbe.command.serve and libbe.storage.http for HTTP backend. Now the following works: some-BE-dir$ ./be serve $ ./be --repo http://localhost:8000 list I haven't come up with a clean idea for testing this yet, so other commands may be broken, but once we get the testing working, it shouldn't be too hard to get everything working over HTTP :). --- libbe/command/serve.py | 346 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 libbe/command/serve.py (limited to 'libbe/command/serve.py') diff --git a/libbe/command/serve.py b/libbe/command/serve.py new file mode 100644 index 0000000..3f96d00 --- /dev/null +++ b/libbe/command/serve.py @@ -0,0 +1,346 @@ +# Copyright (C) 2005-2010 Aaron Bentley and Panometrics, Inc. +# Gianluca Montecchi +# Oleg Romanyshyn +# W. Trevor King +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import BaseHTTPServer as server +import posixpath +import urllib +import urlparse + +import libbe +import libbe.command +import libbe.command.util +import libbe.version + +HTTP_USER_ERROR = 418 +STORAGE = None +COMMAND = None + +class BERequestHandler (server.BaseHTTPRequestHandler): + """Simple HTTP request handler for serving the + libbe.storage.http.HTTP backend with GET, POST, and HEAD commands. + + This serves files from a connected storage instance, usually + a VCS-based repository located on the local machine. + + The GET and HEAD requests are identical except that the HEAD + request omits the actual content of the file. + """ + + server_version = "BE-server/" + libbe.version.version() + + def do_GET(self, head=False): + """Serve a GET (or HEAD, if head==True) request.""" + self.s = STORAGE + self.c = COMMAND + request = 'GET' + if head == True: + request = 'HEAD' + self.log_request(request) + path,query,fragment = self.parse_path(self.path) + if fragment != '': + self.send_error(406, + '%s implementation does not allow fragment URL portion' + % request) + 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') + return None + if content != None: + self.send_header('Content-type', ctype) + self.send_header('Content-Length', len(content)) + self.end_headers() + if request == 'GET' and content != None: + self.wfile.write(content) + + def do_HEAD(self): + """Serve a HEAD request.""" + return self.do_GET(head=True) + + def do_POST(self): + """Serve a POST request.""" + self.s = STORAGE + self.c = COMMAND + self.log_request('POST') + post_data = self.rfile.read() + print 'got post', post_data + data = self.parse_post(post_data) + + path,query,fragment = self.parse_path(self.path) + if query != '': + self.send_error( + 406, 'POST implementation does not allow query URL portion') + return None + if fragment != '': + self.send_error( + 406, 'POST implementation does not allow fragment URL portion') + return None + if path == ['add']: + content,ctype = self.handle_add(data) + elif path == ['remove']: + content,ctype = self.handle_remove(data) + elif len(path) > 1 and path[0] == 'set': + content,ctype = self.handle_set('/'.join(path[1:]), data) + elif path == ['commit']: + content,ctype = self.handle_commit(data) + else: + self.send_error(400, 'File not found') + return None + if content != None: + self.send_header('Content-type', ctype) + self.send_header('Content-Length', len(content)) + self.end_headers() + if content != None: + self.wfile.write(content) + + def handle_add(self, data): + if not 'id' in data: + self.send_error(406, 'Missing query key id') + return None + elif data['id'] == 'None': + data['id'] = None + id = data['id'] + if not 'parent' in data or data['parent'] == None: + data['parent'] = None + parent = data['parent'] + if not 'directory' in data: + directory = False + elif data['directory'] == 'True': + directory = True + else: + directory = False + self.s.add(id, parent, directory) + self.send_response(200) + return (None,None) + + def handle_remove(self, data): + if not 'id' in data: + self.send_error(406, 'Missing query key id') + return None + elif data['id'] == 'None': + data['id'] = None + id = data['id'] + if not 'recursive' in data: + recursive = False + elif data['recursive'] == 'True': + recursive = True + else: + recursive = False + if recursive == True: + self.s.recursive_remove(id) + else: + self.s.remove(id) + self.send_response(200) + return (None,None) + + def handle_children(self, data): + if not 'id' in data: + self.send_error(406, 'Missing query key id') + return None + elif data['id'] == 'None': + data['id'] = None + id = data['id'] + if not 'revision' in data or data['revision'] == 'None': + data['revision'] = None + revision = data['revision'] + content = '\n'.join(self.s.children(id, revision)) + ctype = 'application/octet-stream' + self.send_response(200) + return content,ctype + + def handle_get(self, id, data): + 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 + be_version = self.s.storage_version(revision) + ctype = 'application/octet-stream' + self.send_response(200) + self.send_header('X-BE-Version', be_version) + return content,ctype + + def handle_set(self, id, data): + if not 'value' in data: + self.send_error(406, 'Missing query key value') + return None + self.s.set(id, value) + self.send_response(200) + return None + + def handle_commit(self, data): + if not 'summary' in data: + self.send_error(406, 'Missing query key summary') + return None + summary = data['summary'] + if not body in data or data['body'] == 'None': + data['body'] = None + body = data['body'] + if not allow_empty in data \ + or data['allow_empty'] == 'True': + allow_empty = True + else: + allow_empty = False + self.s.commit(summary, body, allow_empty) + self.send_response(200) + return None + + def handle_revision_id(self, data): + if not 'index' in data: + self.send_error(406, 'Missing query key index') + return None + index = int(data['index']) + content = self.s.revision_id(index) + ctype = 'application/octet-stream' + self.send_response(200) + return content,ctype + + def handle_version(self, data): + if not 'revision' in data or data['revision'] == 'None': + data['revision'] = None + revision = data['revision'] + content = self.s.storage_version(revision) + ctype = 'application/octet-stream' + self.send_response(200) + return content,ctype + + def parse_path(self, path): + """Parse a url to path,query,fragment parts.""" + # abandon query parameters + scheme,netloc,path,query,fragment = urlparse.urlsplit(path) + path = posixpath.normpath(urllib.unquote(path)).split('/') + assert path[0] == '', path + path = path[1:] + return (path,query,fragment) + + def log_request(self, request): + print >> self.c.stdout, request, self.path + + def parse_query(self, query): + if len(query) == 0: + return {} + data = urlparse.parse_qs( + query, keep_blank_values=True, strict_parsing=True) + for k,v in data.items(): + if len(v) == 1: + data[k] = v[0] + return data + + def parse_post(self, post): + return self.parse_query(post) + +class Serve (libbe.command.Command): + """Serve a Storage backend for the HTTP storage client + + >>> import libbe.bugdir + >>> bd = libbe.bugdir.SimpleBugDir(memory=False) + >>> io = libbe.command.StringInputOutput() + >>> io.stdout = sys.stdout + >>> ui = libbe.command.UserInterface(io=io) + >>> ui.storage_callbacks.set_storage(bd.storage) + >>> cmd = List(ui=ui) + + >>> ret = ui.run(cmd) + abc/a:om: Bug A + >>> ret = ui.run(cmd, {'status':'closed'}) + abc/b:cm: Bug B + >>> bd.storage.writeable + True + >>> ui.cleanup() + >>> bd.cleanup() + """ + + name = 'serve' + + def __init__(self, *args, **kwargs): + libbe.command.Command.__init__(self, *args, **kwargs) + self.options.extend([ + libbe.command.Option(name='port', + help='Bind server to port (%default)', + arg=libbe.command.Argument( + name='port', metavar='INT', type='int', default=8000)), + libbe.command.Option(name='host', + help='Set host string (blank for localhost, %default)', + arg=libbe.command.Argument( + name='host', metavar='HOST', default='')), + ]) + + def _run(self, **params): + global STORAGE, COMMAND + STORAGE = self._get_storage() + COMMAND = self + server_class = server.HTTPServer + handler_class = BERequestHandler + server_address = (params['host'], params['port']) + httpd = server_class(server_address, handler_class) + sa = httpd.socket.getsockname() + print >> self.stdout, 'Serving HTTP on', sa[0], 'port', sa[1], '...' + print >> self.stdout, 'BE repository', STORAGE.repo + try: + httpd.serve_forever() + except KeyboardInterrupt: + pass + print >> self.stdout, 'Closing server' + httpd.server_close() + + def _long_help(self): + return """ +This command lists bugs. Normally it prints a short string like + 576:om: Allow attachments +Where + 576 the bug id + o the bug status is 'open' (first letter) + m the bug severity is 'minor' (first letter) + Allo... the bug summary string + +You can optionally (-u) print only the bug ids. + +There are several criteria that you can filter by: + * status + * severity + * assigned (who the bug is assigned to) +Allowed values for each criterion may be given in a comma seperated +list. The special string "all" may be used with any of these options +to match all values of the criterion. As with the --status and +--severity options for `be depend`, starting the list with a minus +sign makes your selections a blacklist instead of the default +whitelist. + +status + %s +severity + %s +assigned + free form, with the string '-' being a shortcut for yourself. + +In addition, there are some shortcut options that set boolean flags. +The boolean options are ignored if the matching string option is used. +""" -- cgit From d7a8c4bc0ad6b96bad5ee1ff71602fcd3c70b678 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 1 Jan 2010 15:33:23 -0500 Subject: Improved POST parsing, fixed Serve._long_help(), added --read-only. POST parsing via cgi.FieldStorage from Doug Hellmann's http://blog.doughellmann.com/2007/12/pymotw-basehttpserver.html --- libbe/command/serve.py | 89 +++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 49 deletions(-) (limited to 'libbe/command/serve.py') diff --git a/libbe/command/serve.py b/libbe/command/serve.py index 3f96d00..9afaa90 100644 --- a/libbe/command/serve.py +++ b/libbe/command/serve.py @@ -18,6 +18,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import BaseHTTPServer as server +import cgi import posixpath import urllib import urlparse @@ -87,10 +88,12 @@ class BERequestHandler (server.BaseHTTPRequestHandler): self.s = STORAGE self.c = COMMAND self.log_request('POST') - post_data = self.rfile.read() - print 'got post', post_data - data = self.parse_post(post_data) - + data = cgi.FieldStorage( + fp=self.rfile, + headers=self.headers, + environ={'REQUEST_METHOD':'POST', + 'CONTENT_TYPE':self.headers['Content-Type'], + }) path,query,fragment = self.parse_path(self.path) if query != '': self.send_error( @@ -100,16 +103,20 @@ class BERequestHandler (server.BaseHTTPRequestHandler): self.send_error( 406, 'POST implementation does not allow fragment URL portion') return None - if path == ['add']: - content,ctype = self.handle_add(data) - elif path == ['remove']: - content,ctype = self.handle_remove(data) - elif len(path) > 1 and path[0] == 'set': - content,ctype = self.handle_set('/'.join(path[1:]), data) - elif path == ['commit']: - content,ctype = self.handle_commit(data) - else: - self.send_error(400, 'File not found') + try: + if path == ['add']: + content,ctype = self.handle_add(data) + elif path == ['remove']: + content,ctype = self.handle_remove(data) + elif len(path) > 1 and path[0] == 'set': + content,ctype = self.handle_set('/'.join(path[1:]), data) + elif path == ['commit']: + content,ctype = self.handle_commit(data) + else: + self.send_error(400, 'File not found') + return None + except libbe.storage.NotWriteable, e: + self.send_error(403, 'Write permission denied') return None if content != None: self.send_header('Content-type', ctype) @@ -192,6 +199,7 @@ class BERequestHandler (server.BaseHTTPRequestHandler): if not 'value' in data: self.send_error(406, 'Missing query key value') return None + value = data['value'] self.s.set(id, value) self.send_response(200) return None @@ -254,8 +262,6 @@ class BERequestHandler (server.BaseHTTPRequestHandler): data[k] = v[0] return data - def parse_post(self, post): - return self.parse_query(post) class Serve (libbe.command.Command): """Serve a Storage backend for the HTTP storage client @@ -291,16 +297,21 @@ class Serve (libbe.command.Command): help='Set host string (blank for localhost, %default)', arg=libbe.command.Argument( name='host', metavar='HOST', default='')), + libbe.command.Option(name='read-only', short_name='r', + help='Dissable operations that require writing'), ]) def _run(self, **params): global STORAGE, COMMAND - STORAGE = self._get_storage() COMMAND = self + STORAGE = self._get_storage() + if params['read-only'] == True: + writeable = STORAGE.writeable + STORAGE.writeable = False server_class = server.HTTPServer handler_class = BERequestHandler - server_address = (params['host'], params['port']) - httpd = server_class(server_address, handler_class) + httpd = server_class( + (params['host'], params['port']), handler_class) sa = httpd.socket.getsockname() print >> self.stdout, 'Serving HTTP on', sa[0], 'port', sa[1], '...' print >> self.stdout, 'BE repository', STORAGE.repo @@ -310,37 +321,17 @@ class Serve (libbe.command.Command): pass print >> self.stdout, 'Closing server' httpd.server_close() + if params['read-only'] == True: + STORAGE.writeable = writeable def _long_help(self): return """ -This command lists bugs. Normally it prints a short string like - 576:om: Allow attachments -Where - 576 the bug id - o the bug status is 'open' (first letter) - m the bug severity is 'minor' (first letter) - Allo... the bug summary string - -You can optionally (-u) print only the bug ids. - -There are several criteria that you can filter by: - * status - * severity - * assigned (who the bug is assigned to) -Allowed values for each criterion may be given in a comma seperated -list. The special string "all" may be used with any of these options -to match all values of the criterion. As with the --status and ---severity options for `be depend`, starting the list with a minus -sign makes your selections a blacklist instead of the default -whitelist. - -status - %s -severity - %s -assigned - free form, with the string '-' being a shortcut for yourself. - -In addition, there are some shortcut options that set boolean flags. -The boolean options are ignored if the matching string option is used. +Example usage: + $ be serve +And in another terminal (or after backgrounding the server) + $ be --repo http://localhost:8000 list + +If you bind your server to a public interface, you should probably use +the --read-only option so other people can't mess with your +repository. """ -- cgit From ecc2dd71287b46db316d8ef050a0bdb22bfc0fa5 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 1 Jan 2010 16:11:48 -0500 Subject: 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(-) (limited to 'libbe/command/serve.py') 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 -- cgit From 0679e0b1fd941d6aefef146fb4289472edee9b62 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 1 Jan 2010 16:33:39 -0500 Subject: Use more kwargs in libbe.command.serve kwargs make things easier to maintain. Also make sure the .handle_*() methods return two items (content,ctype) even when both are None. --- libbe/command/serve.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'libbe/command/serve.py') diff --git a/libbe/command/serve.py b/libbe/command/serve.py index 1aa2305..555a7ab 100644 --- a/libbe/command/serve.py +++ b/libbe/command/serve.py @@ -125,7 +125,6 @@ class BERequestHandler (server.BaseHTTPRequestHandler): 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: @@ -138,7 +137,7 @@ class BERequestHandler (server.BaseHTTPRequestHandler): def handle_add(self, data): if not 'id' in data: self.send_error(406, 'Missing query key id') - return None + return (None,None) elif data['id'] == 'None': data['id'] = None id = data['id'] @@ -151,14 +150,14 @@ class BERequestHandler (server.BaseHTTPRequestHandler): directory = True else: directory = False - self.s.add(id, parent, directory) + self.s.add(id, parent=parent, directory=directory) self.send_response(200) return (None,None) def handle_remove(self, data): if not 'id' in data: self.send_error(406, 'Missing query key id') - return None + return (None,None) elif data['id'] == 'None': data['id'] = None id = data['id'] @@ -178,7 +177,7 @@ class BERequestHandler (server.BaseHTTPRequestHandler): def handle_children(self, data): if not 'id' in data: self.send_error(406, 'Missing query key id') - return None + return (None,None) elif data['id'] == 'None': data['id'] = None id = data['id'] @@ -194,7 +193,7 @@ class BERequestHandler (server.BaseHTTPRequestHandler): if not 'revision' in data or data['revision'] == 'None': data['revision'] = None revision = data['revision'] - content = self.s.get(id, revision) + content = self.s.get(id, revision=revision) be_version = self.s.storage_version(revision) ctype = 'application/octet-stream' self.send_response(200) @@ -204,16 +203,16 @@ class BERequestHandler (server.BaseHTTPRequestHandler): def handle_set(self, id, data): if not 'value' in data: self.send_error(406, 'Missing query key value') - return None + return (None,None) value = data['value'] self.s.set(id, value) self.send_response(200) - return None + return (None,None) def handle_commit(self, data): if not 'summary' in data: self.send_error(406, 'Missing query key summary') - return None + return (None,None) summary = data['summary'] if not body in data or data['body'] == 'None': data['body'] = None @@ -225,12 +224,12 @@ class BERequestHandler (server.BaseHTTPRequestHandler): allow_empty = False self.s.commit(summary, body, allow_empty) self.send_response(200) - return None + return (None,None) def handle_revision_id(self, data): if not 'index' in data: self.send_error(406, 'Missing query key index') - return None + return (None,None) index = int(data['index']) content = self.s.revision_id(index) ctype = 'application/octet-stream' -- cgit From f114d9add0610c025110681c09a2f2eb4acedfa5 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 1 Jan 2010 16:48:03 -0500 Subject: Fixed commit handling in commands.serve and storage.http --- libbe/command/serve.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'libbe/command/serve.py') diff --git a/libbe/command/serve.py b/libbe/command/serve.py index 555a7ab..e98787f 100644 --- a/libbe/command/serve.py +++ b/libbe/command/serve.py @@ -214,15 +214,19 @@ class BERequestHandler (server.BaseHTTPRequestHandler): self.send_error(406, 'Missing query key summary') return (None,None) summary = data['summary'] - if not body in data or data['body'] == 'None': + if not 'body' in data or data['body'] == 'None': data['body'] = None body = data['body'] - if not allow_empty in data \ + if not 'allow_empty' in data \ or data['allow_empty'] == 'True': allow_empty = True else: allow_empty = False - self.s.commit(summary, body, allow_empty) + try: + self.s.commit(summary, body, allow_empty) + except libbe.storage.EmptyCommit, e: + self.send_error(HTTP_USER_ERROR, 'EmptyCommit') + return (None,None) self.send_response(200) return (None,None) -- cgit From 65287e2dfe5c4958bfc5d06f99ea4371e4c67fe7 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 1 Jan 2010 16:55:49 -0500 Subject: Serve.handle_*() now raise _HandlerError so the .do_*() methods know. Before there wasn't a good way to tell if the handler had experienced an error, or just didn't want to return anything. --- libbe/command/serve.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'libbe/command/serve.py') diff --git a/libbe/command/serve.py b/libbe/command/serve.py index e98787f..920a147 100644 --- a/libbe/command/serve.py +++ b/libbe/command/serve.py @@ -35,6 +35,10 @@ COMMAND = None # 0 ==> unlimited input MAXLEN = 0 + +class _HandlerError (Exception): + pass + class BERequestHandler (server.BaseHTTPRequestHandler): """Simple HTTP request handler for serving the libbe.storage.http.HTTP backend with GET, POST, and HEAD commands. @@ -82,6 +86,9 @@ class BERequestHandler (server.BaseHTTPRequestHandler): except libbe.storage.InvalidID, e: self.send_error(HTTP_USER_ERROR, 'InvalidID %s' % e) return None + except _HandlerError: + return None + if content != None: self.send_header('Content-type', ctype) self.send_header('Content-Length', len(content)) @@ -127,6 +134,8 @@ class BERequestHandler (server.BaseHTTPRequestHandler): except libbe.storage.InvalidID, e: self.send_error(HTTP_USER_ERROR, 'InvalidID %s' % e) return None + except _HandlerError: + return None if content != None: self.send_header('Content-type', ctype) self.send_header('Content-Length', len(content)) @@ -137,7 +146,7 @@ class BERequestHandler (server.BaseHTTPRequestHandler): def handle_add(self, data): if not 'id' in data: self.send_error(406, 'Missing query key id') - return (None,None) + raise _HandlerError() elif data['id'] == 'None': data['id'] = None id = data['id'] @@ -157,7 +166,7 @@ class BERequestHandler (server.BaseHTTPRequestHandler): def handle_remove(self, data): if not 'id' in data: self.send_error(406, 'Missing query key id') - return (None,None) + raise _HandlerError() elif data['id'] == 'None': data['id'] = None id = data['id'] @@ -177,7 +186,7 @@ class BERequestHandler (server.BaseHTTPRequestHandler): def handle_children(self, data): if not 'id' in data: self.send_error(406, 'Missing query key id') - return (None,None) + raise _HandlerError() elif data['id'] == 'None': data['id'] = None id = data['id'] @@ -203,7 +212,7 @@ class BERequestHandler (server.BaseHTTPRequestHandler): def handle_set(self, id, data): if not 'value' in data: self.send_error(406, 'Missing query key value') - return (None,None) + raise _HandlerError() value = data['value'] self.s.set(id, value) self.send_response(200) @@ -212,7 +221,7 @@ class BERequestHandler (server.BaseHTTPRequestHandler): def handle_commit(self, data): if not 'summary' in data: self.send_error(406, 'Missing query key summary') - return (None,None) + raise _HandlerError() summary = data['summary'] if not 'body' in data or data['body'] == 'None': data['body'] = None @@ -226,14 +235,14 @@ class BERequestHandler (server.BaseHTTPRequestHandler): self.s.commit(summary, body, allow_empty) except libbe.storage.EmptyCommit, e: self.send_error(HTTP_USER_ERROR, 'EmptyCommit') - return (None,None) + raise _HandlerError() self.send_response(200) return (None,None) def handle_revision_id(self, data): if not 'index' in data: self.send_error(406, 'Missing query key index') - return (None,None) + raise _HandlerError() index = int(data['index']) content = self.s.revision_id(index) ctype = 'application/octet-stream' @@ -285,7 +294,7 @@ class BERequestHandler (server.BaseHTTPRequestHandler): 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 -- cgit From 1676f2f2951739a276e071ef7821609193fe15b0 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 1 Jan 2010 16:51:13 -0500 Subject: Fixed update_copyright.py's subproc import and updated copyrights --- libbe/command/serve.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'libbe/command/serve.py') diff --git a/libbe/command/serve.py b/libbe/command/serve.py index 920a147..5dbd2b4 100644 --- a/libbe/command/serve.py +++ b/libbe/command/serve.py @@ -1,7 +1,4 @@ -# Copyright (C) 2005-2010 Aaron Bentley and Panometrics, Inc. -# Gianluca Montecchi -# Oleg Romanyshyn -# W. Trevor King +# Copyright (C) 2010 W. Trevor King # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by -- cgit