# -*- coding: utf-8 -*-
#
-# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
-# A part of the code comes from the ganeti project: http://www.mail-archive.com/ganeti-devel@googlegroups.com/msg00713.html#
+# Copyright P. Christeas <p_christ@hol.gr> 2008-2010
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
return self.path
return False
-class noconnection:
+class noconnection(object):
""" a class to use instead of the real connection
"""
+ def __init__(self, realsocket=None):
+ self.__hidden_socket = realsocket
+
def makefile(self, mode, bufsize):
return None
+ def close(self):
+ pass
+
+ def getsockname(self):
+ """ We need to return info about the real socket that is used for the request
+ """
+ if not self.__hidden_socket:
+ raise AttributeError("No-connection class cannot tell real socket")
+ return self.__hidden_socket.getsockname()
+
class dummyconn:
def shutdown(self, tru):
pass
self.send_header('Connection', 'close')
self.send_header('Content-Length', len(content) or 0)
self.end_headers()
+ if hasattr(self, '_flush'):
+ self._flush()
+
if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
self.wfile.write(content)
-class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler):
+class HttpOptions:
+ _HTTP_OPTIONS = {'Allow': ['OPTIONS' ] }
+
+ def do_OPTIONS(self):
+ """return the list of capabilities """
+
+ opts = self._HTTP_OPTIONS
+ nopts = self._prep_OPTIONS(opts)
+ if nopts:
+ opts = nopts
+
+ self.send_response(200)
+ self.send_header("Content-Length", 0)
+ if 'Microsoft' in self.headers.get('User-Agent', ''):
+ self.send_header('MS-Author-Via', 'DAV')
+ # Microsoft's webdav lib ass-umes that the server would
+ # be a FrontPage(tm) one, unless we send a non-standard
+ # header that we are not an elephant.
+ # http://www.ibm.com/developerworks/rational/library/2089.html
+
+ for key, value in opts.items():
+ if isinstance(value, basestring):
+ self.send_header(key, value)
+ elif isinstance(value, (tuple, list)):
+ self.send_header(key, ', '.join(value))
+ self.end_headers()
+
+ def _prep_OPTIONS(self, opts):
+ """Prepare the OPTIONS response, if needed
+
+ Sometimes, like in special DAV folders, the OPTIONS may contain
+ extra keywords, perhaps also dependant on the request url.
+ @param the options already. MUST be copied before being altered
+ @return the updated options.
+
+ """
+ return opts
+
+class MultiHTTPHandler(FixSendError, HttpOptions, BaseHTTPRequestHandler):
""" this is a multiple handler, that will dispatch each request
to a nested handler, iff it matches
fore.raw_requestline = "%s %s %s\n" % (self.command, path, self.version)
if not fore.parse_request(): # An error code has been sent, just exit
return
+ if fore.headers.status:
+ self.log_error("Parse error at headers: %s", fore.headers.status)
+ self.close_connection = 1
+ self.send_error(400,"Parse error at HTTP headers")
+ return
+
self.request_version = fore.request_version
if auth_provider and auth_provider.realm:
try:
self.sec_realms[auth_provider.realm].checkRequest(fore,path)
except AuthRequiredExc,ae:
- if self.request_version != 'HTTP/1.1':
- self.log_error("Cannot require auth at %s",self.request_version)
+ if self.request_version != 'HTTP/1.1' and ('Darwin/9.' not in fore.headers.get('User-Agent', '')):
+ self.log_error("Cannot require auth at %s", self.request_version)
self.send_error(403)
return
self._get_ignore_body(fore) # consume any body that came, not loose sync with input
return
mname = 'do_' + fore.command
if not hasattr(fore, mname):
- fore.send_error(501, "Unsupported method (%r)" % fore.command)
+ if fore.command == 'OPTIONS':
+ self.do_OPTIONS()
+ return
+ self.send_error(501, "Unsupported method (%r)" % fore.command)
return
fore.close_connection = 0
method = getattr(fore, mname)
- method()
+ try:
+ method()
+ except (AuthRejectedExc, AuthRequiredExc):
+ raise
+ except Exception, e:
+ if hasattr(self, 'log_exception'):
+ self.log_exception("Could not run %s", mname)
+ else:
+ self.log_error("Could not run %s: %s", mname, e)
+ self.send_error(500, "Internal error")
+ # may not work if method has already sent data
+ fore.close_connection = 1
+ self.close_connection = 1
+ if hasattr(fore, '_flush'):
+ fore._flush()
+ return
+
if fore.close_connection:
# print "Closing connection because of handler"
self.close_connection = fore.close_connection
[command, path] = words
self.close_connection = 1
if command != 'GET':
+ self.log_error("Junk http request: %s", self.raw_requestline)
self.send_error(400,
"Bad HTTP/0.9 request type (%r)" % command)
return False
self.log_message("Could not parse rawline.")
return
# self.parse_request(): # Do NOT parse here. the first line should be the only
+
+ if self.path == '*' and self.command == 'OPTIONS':
+ # special handling of path='*', must not use any vdir at all.
+ if not self.parse_request():
+ return
+ self.do_OPTIONS()
+ return
+
for vdir in self.server.vdirs:
p = vdir.matches(self.path)
if p == False:
npath = '/' + npath
if not self.in_handlers.has_key(p):
- self.in_handlers[p] = vdir.handler(noconnection(),self.client_address,self.server)
+ self.in_handlers[p] = vdir.handler(noconnection(self.request),self.client_address,self.server)
if vdir.auth_provider:
vdir.auth_provider.setupAuth(self, self.in_handlers[p])
hnd = self.in_handlers[p]
hnd.rfile = self.rfile
hnd.wfile = self.wfile
self.rlpath = self.raw_requestline
- self._handle_one_foreign(hnd,npath, vdir.auth_provider)
- # print "Handled, closing = ", self.close_connection
+ try:
+ self._handle_one_foreign(hnd,npath, vdir.auth_provider)
+ except IOError, e:
+ if e.errno == errno.EPIPE:
+ self.log_message("Could not complete request %s," \
+ "client closed connection", self.rlpath.rstrip())
+ else:
+ raise
return
# if no match:
self.send_error(404, "Path not found: %s" % self.path)
# main process
daemon_threads = False
+ def _get_next_name(self):
+ return None
+
def _handle_request_noblock(self):
"""Start a new thread to process the request."""
if not threading: # happens while quitting python
return
- t = threading.Thread(target = self._handle_request2)
+ t = threading.Thread(name=self._get_next_name(), target=self._handle_request2)
if self.daemon_threads:
t.setDaemon (1)
t.start()
-
+
def _mark_start(self, thread):
""" Mark the start of a request thread """
pass
try:
self._mark_start(threading.currentThread())
request, client_address = self.get_request()
+ if self.verify_request(request, client_address):
+ try:
+ self.process_request(request, client_address)
+ except Exception:
+ self.handle_error(request, client_address)
+ self.close_request(request)
except socket.error:
- self._mark_end(threading.currentThread())
return
- if self.verify_request(request, client_address):
- try:
- self.process_request(request, client_address)
- except Exception:
- self.handle_error(request, client_address)
- self.close_request(request)
- self._mark_end(threading.currentThread())
+ finally:
+ self._mark_end(threading.currentThread())
#eof