From c06b42ad3c2850d2795b8bbf1b6d222b7e5d63b0 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:34 +0300 Subject: [PATCH 01/27] HTTP lib: When authentication cannot proceed, issue 403 The http protocol states that 401 should only be used when further authentication would be possible bzr revid: p_christ@hol.gr-20100726093334-qe5midzceivezbxu --- bin/service/websrv_lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index 3f44b6d8314..80f24cf4b2b 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -208,7 +208,7 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler): except AuthRequiredExc,ae: if self.request_version != 'HTTP/1.1': self.log_error("Cannot require auth at %s",self.request_version) - self.send_error(401) + self.send_error(403) return self._get_ignore_body(fore) # consume any body that came, not loose sync with input self.send_response(401,'Authorization required') @@ -221,7 +221,7 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler): return except AuthRejectedExc,e: self.log_error("Rejected auth: %s" % e.args[0]) - self.send_error(401,e.args[0]) + self.send_error(403,e.args[0]) self.close_connection = 1 return mname = 'do_' + fore.command From 3e8a845903f9cfb887a9ac11131708a61a877f2d Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:34 +0300 Subject: [PATCH 02/27] Http services: ensure the order in the MultiHandler If we happen to register the '/' service, and then try to register the '/webdav/' one, the latter must be inserted before the more generic path. bzr revid: p_christ@hol.gr-20100726093334-3w7hyaukhzw9xpup --- bin/service/http_server.py | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index bab38b06609..a61877b20e6 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -148,6 +148,29 @@ class BaseHttpDaemon(threading.Thread, netsvc.Server): raise return True + def append_svc(self, service): + if not isinstance(service, HTTPDir): + raise Exception("Wrong class for http service") + + pos = len(self.server.vdirs) + lastpos = pos + while pos > 0: + pos -= 1 + if self.server.vdirs[pos].matches(service.path): + lastpos = pos + # we won't break here, but search all way to the top, to + # ensure there is no lesser entry that will shadow the one + # we are inserting. + self.server.vdirs.insert(lastpos, service) + + def list_services(self): + ret = [] + for svc in self.server.vdirs: + ret.append( ( svc.path, str(svc.handler)) ) + + return ret + + class HttpDaemon(BaseHttpDaemon): def __init__(self, interface, port): super(HttpDaemon, self).__init__(interface, port, @@ -190,19 +213,26 @@ def reg_http_service(hts, secure_only = False): hts must be an HTTPDir """ global httpd, httpsd - if not isinstance(hts, HTTPDir): - raise Exception("Wrong class for http service") if httpd and not secure_only: - httpd.server.vdirs.append(hts) + httpd.append_svc(hts) if httpsd: - httpsd.server.vdirs.append(hts) + httpsd.append_svc(hts) if (not httpd) and (not httpsd): netsvc.Logger().notifyChannel('httpd',netsvc.LOG_WARNING,"No httpd available to register service %s" % hts.path) return +def list_http_services(protocol=None): + global httpd, httpsd + if httpd and (protocol == 'http' or protocol == None): + return httpd.list_services() + elif httpsd and (protocol == 'https' or protocol == None): + return httpsd.list_services() + else: + raise Exception("Incorrect protocol or no http services") + import SimpleXMLRPCServer class XMLRPCRequestHandler(netsvc.OpenERPDispatcher,FixSendError,SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): rpc_paths = [] From 7d1f26cdd286bb13f05827446955a9391b731fc6 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:34 +0300 Subject: [PATCH 03/27] Export command to list the http services. For debugging reasons, we should know what services we have registered in our multi-dir http server. Conflicts: bin/service/web_services.py bzr revid: p_christ@hol.gr-20100726093334-7074r91i63e4g7jg --- bin/service/web_services.py | 7 ++++++- list-services.sh | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100755 list-services.sh diff --git a/bin/service/web_services.py b/bin/service/web_services.py index 19b178c7caa..1a710708810 100644 --- a/bin/service/web_services.py +++ b/bin/service/web_services.py @@ -385,7 +385,8 @@ class common(_ObjectService): logger.notifyChannel("web-service", netsvc.LOG_INFO,'Logout %s from database %s'%(login,db)) return True elif method in ['about', 'timezone_get', 'get_server_environment', - 'login_message','get_stats', 'check_connectivity']: + 'login_message','get_stats', 'check_connectivity', + 'list_http_services']: pass elif method in ['get_available_updates', 'get_migration_scripts', 'set_loglevel']: passwd = params[0] @@ -567,6 +568,10 @@ GNU Public Licence. res += netsvc.Server.allStats() return res + def exp_list_http_services(self): + from service import http_server + return http_server.list_http_services() + def exp_check_connectivity(self): return bool(sql_db.db_connect('template1')) diff --git a/list-services.sh b/list-services.sh new file mode 100755 index 00000000000..a90e58479d5 --- /dev/null +++ b/list-services.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# ADMIN_PASSWD='admin' +method_1() { + cat '-' << EOF + + + list_http_services + + + +EOF +} + +method_1 | POST -c 'text/xml' http://localhost:8069/xmlrpc/common +#eof From ea4bb981402542f9b00eb69a26c90e810d33bae6 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:34 +0300 Subject: [PATCH 04/27] Feature: serve static http, from a dir Now, it is possible to serve static http from the files of one dir. Just configure like: [static-http] enable = True dir_path = /home/panos/build/openerp/doc/build/html and the contents of that directory will be served as a default at our http port ;) Conflicts: bin/service/http_server.py bzr revid: p_christ@hol.gr-20100726093334-2xr5mg0c9xvm68ej --- bin/openerp-server.py | 1 + bin/service/http_server.py | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/bin/openerp-server.py b/bin/openerp-server.py index c56977fe1f0..5a68440af27 100755 --- a/bin/openerp-server.py +++ b/bin/openerp-server.py @@ -108,6 +108,7 @@ if not ( tools.config["stop_after_init"] or \ tools.config["translate_out"] ): service.http_server.init_servers() service.http_server.init_xmlrpc() + service.http_server.init_static_http() import service.netrpc_server service.netrpc_server.init_servers() diff --git a/bin/service/http_server.py b/bin/service/http_server.py index a61877b20e6..f185549db25 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -34,6 +34,8 @@ import netsvc import errno import threading import tools +import posixpath +import urllib import os import select import socket @@ -273,6 +275,47 @@ def init_xmlrpc(): netsvc.Logger().notifyChannel('web-services', netsvc.LOG_INFO, "Registered XML-RPC over HTTPS") +class StaticHTTPHandler(HTTPHandler): + def __init__(self,request, client_address, server): + HTTPHandler.__init__(self,request,client_address,server) + dir_path = tools.config.get_misc('static-http', 'dir_path', False) + assert dir_path + self.__basepath = dir_path + + def translate_path(self, path): + """Translate a /-separated PATH to the local filename syntax. + + Components that mean special things to the local file system + (e.g. drive or directory names) are ignored. (XXX They should + probably be diagnosed.) + + """ + # abandon query parameters + path = path.split('?',1)[0] + path = path.split('#',1)[0] + path = posixpath.normpath(urllib.unquote(path)) + words = path.split('/') + words = filter(None, words) + path = self.__basepath + for word in words: + if word in (os.curdir, os.pardir): continue + path = os.path.join(path, word) + return path + +def init_static_http(): + if not tools.config.get_misc('static-http','enable', True): + return + + dir_path = tools.config.get_misc('static-http', 'dir_path', False) + assert dir_path + + base_path = tools.config.get_misc('static-http', 'base_path', '/') + + reg_http_service(HTTPDir(base_path,StaticHTTPHandler)) + + netsvc.Logger().notifyChannel("web-services", netsvc.LOG_INFO, + "Registered HTTP dir %s for %s" % (dir_path, base_path)) + class OerpAuthProxy(AuthProxy): """ Require basic authentication.. From 041f949bccbe7f06d402aa73b7a4301f000ca46d Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:34 +0300 Subject: [PATCH 05/27] websrv_lib: one case of cleaner exit. One rare case, where a module is destructed upon python interpreter exit, and we need to check. bzr revid: p_christ@hol.gr-20100726093334-cgirmmldh2mgvovm --- bin/service/websrv_lib.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index 80f24cf4b2b..e091ceb3a44 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -399,6 +399,8 @@ class ConnThreadingMixIn: 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) if self.daemon_threads: t.setDaemon (1) From e6ca1be7c3242d12aed94fc874304ea1d633bd2f Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:34 +0300 Subject: [PATCH 06/27] Http server: better logging Have a symmetric helper class, that defines logging functions for all http handlers. Conflicts: bin/service/http_server.py bzr revid: p_christ@hol.gr-20100726093334-vko7md6hk2004niw --- bin/service/http_server.py | 89 +++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index f185549db25..d28961b9cc6 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -40,6 +40,7 @@ import os import select import socket import xmlrpclib +import logging from SimpleXMLRPCServer import SimpleXMLRPCDispatcher @@ -83,21 +84,31 @@ class ThreadedHTTPServer(ConnThreadingMixIn, SimpleXMLRPCDispatcher, HTTPServer) def handle_error(self, request, client_address): """ Override the error handler """ - import traceback - netsvc.Logger().notifyChannel("init", netsvc.LOG_ERROR,"Server error in request from %s:\n%s" % - (client_address,traceback.format_exc())) + + logging.getLogger("init").exception("Server error in request from %s:" % (client_address,)) -class MultiHandler2(MultiHTTPHandler): +class HttpLogHandler: + """ helper class for uniform log handling + Please define self._logger at each class that is derived from this + """ + _logger = None + def log_message(self, format, *args): - netsvc.Logger().notifyChannel('http',netsvc.LOG_DEBUG,format % args) + self._logger.debug(format % args) # todo: perhaps other level def log_error(self, format, *args): - netsvc.Logger().notifyChannel('http',netsvc.LOG_ERROR,format % args) + self._logger.error(format % args) + + def log_request(self, code='-', size='-'): + self._logger.debug('"%s" %s %s', + self.requestline, str(code), str(size)) + +class MultiHandler2(HttpLogHandler, MultiHTTPHandler): + _logger = logging.getLogger('http') -class SecureMultiHandler2(SecureMultiHTTPHandler): - def log_message(self, format, *args): - netsvc.Logger().notifyChannel('https',netsvc.LOG_DEBUG,format % args) +class SecureMultiHandler2(HttpLogHandler, SecureMultiHTTPHandler): + _logger = logging.getLogger('https') def getcert_fnames(self): tc = tools.config @@ -105,13 +116,9 @@ class SecureMultiHandler2(SecureMultiHTTPHandler): fkey = tc.get_misc('httpsd','sslkey', 'ssl/server.key') return (fcert,fkey) - def log_message(self, format, *args): - netsvc.Logger().notifyChannel('http',netsvc.LOG_DEBUG,format % args) - - def log_error(self, format, *args): - netsvc.Logger().notifyChannel('http',netsvc.LOG_ERROR,format % args) - class BaseHttpDaemon(threading.Thread, netsvc.Server): + _RealProto = '??' + def __init__(self, interface, port, handler): threading.Thread.__init__(self) netsvc.Server.__init__(self) @@ -123,10 +130,11 @@ class BaseHttpDaemon(threading.Thread, netsvc.Server): self.server.vdirs = [] self.server.logRequests = True self.server.timeout = self._busywait_timeout + logging.getLogger("web-services").info( + "starting %s service at %s port %d" % + (self._RealProto, interface or '0.0.0.0', port,)) except Exception, e: - netsvc.Logger().notifyChannel( - 'httpd', netsvc.LOG_CRITICAL, - "Error occur when starting the server daemon: %s" % (e,)) + logging.getLogger("httpd").exception("Error occured when starting the server daemon.") raise @property @@ -174,28 +182,21 @@ class BaseHttpDaemon(threading.Thread, netsvc.Server): class HttpDaemon(BaseHttpDaemon): + _RealProto = 'HTTP' def __init__(self, interface, port): super(HttpDaemon, self).__init__(interface, port, handler=MultiHandler2) - netsvc.Logger().notifyChannel( - "web-services", netsvc.LOG_INFO, - "starting HTTP service at %s port %d" % - (interface or '0.0.0.0', port,)) class HttpSDaemon(BaseHttpDaemon): + _RealProto = 'HTTPS' def __init__(self, interface, port): try: super(HttpSDaemon, self).__init__(interface, port, handler=SecureMultiHandler2) except SSLError, e: - netsvc.Logger().notifyChannel( - 'httpd-ssl', netsvc.LOG_CRITICAL, - "Can not load the certificate and/or the private key files") + logging.getLogger('httpsd').exception( \ + "Can not load the certificate and/or the private key files") raise - netsvc.Logger().notifyChannel( - "web-services", netsvc.LOG_INFO, - "starting HTTPS service at %s port %d" % - (interface or '0.0.0.0', port,)) httpd = None httpsd = None @@ -223,7 +224,7 @@ def reg_http_service(hts, secure_only = False): httpsd.append_svc(hts) if (not httpd) and (not httpsd): - netsvc.Logger().notifyChannel('httpd',netsvc.LOG_WARNING,"No httpd available to register service %s" % hts.path) + logging.getLogger('httpd').warning("No httpd available to register service %s" % hts.path) return def list_http_services(protocol=None): @@ -236,9 +237,11 @@ def list_http_services(protocol=None): raise Exception("Incorrect protocol or no http services") import SimpleXMLRPCServer -class XMLRPCRequestHandler(netsvc.OpenERPDispatcher,FixSendError,SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): +class XMLRPCRequestHandler(netsvc.OpenERPDispatcher,FixSendError,HttpLogHandler,SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): rpc_paths = [] protocol_version = 'HTTP/1.1' + _logger = logging.getLogger('xmlrpc') + def _dispatch(self, method, params): try: service_name = self.path.split("/")[-1] @@ -246,9 +249,6 @@ class XMLRPCRequestHandler(netsvc.OpenERPDispatcher,FixSendError,SimpleXMLRPCSer except netsvc.OpenERPDispatcherException, e: raise xmlrpclib.Fault(tools.exception_to_unicode(e.exception), e.traceback) - def log_message(self, format, *args): - netsvc.Logger().notifyChannel('xmlrpc',netsvc.LOG_DEBUG_RPC,format % args) - def handle(self): pass @@ -267,15 +267,15 @@ def init_xmlrpc(): # Example of http file serving: # reg_http_service(HTTPDir('/test/',HTTPHandler)) reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler)) - netsvc.Logger().notifyChannel("web-services", netsvc.LOG_INFO, - "Registered XML-RPC over HTTP") + logging.getLogger("web-services").info("Registered XML-RPC over HTTP") if tools.config.get('xmlrpcs', False): reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler, True)) - netsvc.Logger().notifyChannel('web-services', netsvc.LOG_INFO, - "Registered XML-RPC over HTTPS") + logging.getLogger("web-services").info("Registered XML-RPC over HTTPS") + +class StaticHTTPHandler(HttpLogHandler, HTTPHandler): + _logger = logging.getLogger('httpd') -class StaticHTTPHandler(HTTPHandler): def __init__(self,request, client_address, server): HTTPHandler.__init__(self,request,client_address,server) dir_path = tools.config.get_misc('static-http', 'dir_path', False) @@ -313,8 +313,8 @@ def init_static_http(): reg_http_service(HTTPDir(base_path,StaticHTTPHandler)) - netsvc.Logger().notifyChannel("web-services", netsvc.LOG_INFO, - "Registered HTTP dir %s for %s" % (dir_path, base_path)) + logging.getLogger("web-services").info("Registered HTTP dir %s for %s" % \ + (dir_path, base_path)) class OerpAuthProxy(AuthProxy): """ Require basic authentication.. @@ -333,7 +333,6 @@ class OerpAuthProxy(AuthProxy): try: if not db: db = handler.get_db_from_path(path) - print "Got db:",db except Exception: if path.startswith('/'): path = path[1:] @@ -378,10 +377,10 @@ class OpenERPAuthProvider(AuthProvider): return False return (user, passwd, db, uid) except Exception,e: - netsvc.Logger().notifyChannel("auth",netsvc.LOG_DEBUG,"Fail auth:"+ str(e)) + logging.getLogger("auth").debug("Fail auth: %s" % e ) return False - def log(self, msg): - netsvc.Logger().notifyChannel("auth",netsvc.LOG_INFO,msg) + def log(self, msg, lvl=logging.INFO): + logging.getLogger("auth").log(lvl,msg) #eof From 0ee47f021c9e8ee4dd61b0b3df9c58d9bdfc29c8 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 07/27] httpd: reduce request logging to debug_rpc level. This brings back the behaviour of 2 commits ago. bzr revid: p_christ@hol.gr-20100726093335-d1rdfwsjhdymv8v3 --- bin/service/http_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index d28961b9cc6..e06fb344f2f 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -100,7 +100,7 @@ class HttpLogHandler: self._logger.error(format % args) def log_request(self, code='-', size='-'): - self._logger.debug('"%s" %s %s', + self._logger.log(netsvc.logging.DEBUG_RPC, '"%s" %s %s', self.requestline, str(code), str(size)) class MultiHandler2(HttpLogHandler, MultiHTTPHandler): From 9613c0c8643c049876d7ca0c76db3a33bfdaa995 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 08/27] Static httpd: by default, disable. Sample config. bzr revid: p_christ@hol.gr-20100726093335-m6kfvk8xn2c6ddrh --- bin/service/http_server.py | 4 ++-- doc/openerp-server.conf | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index e06fb344f2f..2dfcf90ba63 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -279,7 +279,7 @@ class StaticHTTPHandler(HttpLogHandler, HTTPHandler): def __init__(self,request, client_address, server): HTTPHandler.__init__(self,request,client_address,server) dir_path = tools.config.get_misc('static-http', 'dir_path', False) - assert dir_path + assert dir_path, "Please specify static-http/dir_path in config, or disable static-httpd!" self.__basepath = dir_path def translate_path(self, path): @@ -303,7 +303,7 @@ class StaticHTTPHandler(HttpLogHandler, HTTPHandler): return path def init_static_http(): - if not tools.config.get_misc('static-http','enable', True): + if not tools.config.get_misc('static-http','enable', False): return dir_path = tools.config.get_misc('static-http', 'dir_path', False) diff --git a/doc/openerp-server.conf b/doc/openerp-server.conf index 3b58a008329..ac15f2ef255 100644 --- a/doc/openerp-server.conf +++ b/doc/openerp-server.conf @@ -25,3 +25,7 @@ translate_modules = ['all'] demo = {} addons_path = None reportgz = False + +[static-http] +enable = False +dir_path = /var/www/html \ No newline at end of file From 132ffb246e050fe08082b3004640f078c6146252 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 09/27] http: implement stats for http servers. bzr revid: p_christ@hol.gr-20100726093335-syhvyodg1ec0467f --- bin/service/http_server.py | 14 ++++++++++++++ bin/service/websrv_lib.py | 11 +++++++++++ 2 files changed, 25 insertions(+) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index 2dfcf90ba63..cd79230a62f 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -72,6 +72,8 @@ class ThreadedHTTPServer(ConnThreadingMixIn, SimpleXMLRPCDispatcher, HTTPServer) SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) HTTPServer.__init__(self, addr, requestHandler) + + self.numThreads = 0 # [Bug #1222790] If possible, set close-on-exec flag; if a # method spawns a subprocess, the subprocess shouldn't have @@ -87,6 +89,12 @@ class ThreadedHTTPServer(ConnThreadingMixIn, SimpleXMLRPCDispatcher, HTTPServer) logging.getLogger("init").exception("Server error in request from %s:" % (client_address,)) + def _mark_start(self, thread): + self.numThreads += 1 + + def _mark_end(self, thread): + self.numThreads -= 1 + class HttpLogHandler: """ helper class for uniform log handling Please define self._logger at each class that is derived from this @@ -158,6 +166,12 @@ class BaseHttpDaemon(threading.Thread, netsvc.Server): raise return True + def stats(self): + res = "%sd: " % self._RealProto + ((self.running and "running") or "stopped") + if self.server: + res += ", %d threads" % (self.server.numThreads,) + return res + def append_svc(self, service): if not isinstance(service, HTTPDir): raise Exception("Wrong class for http service") diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index e091ceb3a44..07c98c98b15 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -405,6 +405,14 @@ class ConnThreadingMixIn: if self.daemon_threads: t.setDaemon (1) t.start() + + def _mark_start(self, thread): + """ Mark the start of a request thread """ + pass + + def _mark_end(self, thread): + """ Mark the end of a request thread """ + pass def _handle_request2(self): """Handle one request, without blocking. @@ -414,8 +422,10 @@ class ConnThreadingMixIn: no risk of blocking in get_request(). """ try: + self._mark_start(threading.currentThread()) request, client_address = self.get_request() except socket.error: + self._mark_end(threading.currentThread()) return if self.verify_request(request, client_address): try: @@ -423,5 +433,6 @@ class ConnThreadingMixIn: except Exception: self.handle_error(request, client_address) self.close_request(request) + self._mark_end(threading.currentThread()) #eof From a405f9a4ef6bea631c14425b7d658c7b9ef78210 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 10/27] websrv_lib: handle general failures with http 500 status If some http request fails with an unhandled exception, it is better to send back the 500 error and close the connection, not leave the client waiting for a response. bzr revid: p_christ@hol.gr-20100726093335-o0p4ibon2uvfjxxn --- bin/service/websrv_lib.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index 07c98c98b15..ee74512c5ef 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -230,7 +230,17 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler): return fore.close_connection = 0 method = getattr(fore, mname) - method() + try: + method() + except Exception, e: + self.log_error("Could not run %s: %s" % (mname, e)) + fore.send_error(500, "Internal error") + # may not work if method has already sent data + fore.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 From 754c834d427bf3897a562ebe138310f891e70c7e Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 11/27] websrv_lib: improve sending of internal error bzr revid: p_christ@hol.gr-20100726093335-n8s6cpjmi2252myf --- bin/service/websrv_lib.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index ee74512c5ef..e9d7bafdf12 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -169,6 +169,9 @@ class FixSendError: 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) @@ -232,11 +235,15 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler): method = getattr(fore, mname) try: method() + except (AuthRejectedExc, AuthRequiredExc): + raise except Exception, e: self.log_error("Could not run %s: %s" % (mname, e)) - fore.send_error(500, "Internal error") + self._get_ignore_body(fore) + 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 From 60d11da63e68aa1e84a2c74eb454dfb4cfa48b6e Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 12/27] websrv_lib: workaround the send-error of bad handlers If a handler doesn't inherit FixSendError, it will leave the connection hanging on a 501 response. Don't trust the handlers. bzr revid: p_christ@hol.gr-20100726093335-xa0p85vxje63w03k --- bin/service/websrv_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index e9d7bafdf12..b0dd0aa8d21 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -229,7 +229,7 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler): return mname = 'do_' + fore.command if not hasattr(fore, mname): - fore.send_error(501, "Unsupported method (%r)" % fore.command) + self.send_error(501, "Unsupported method (%r)" % fore.command) return fore.close_connection = 0 method = getattr(fore, mname) From 59efa51612d8f85fab2ad503080b353fe4930bde Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 13/27] static http: use FixSendError. bzr revid: p_christ@hol.gr-20100726093335-lfq6x0df09vbubv8 --- bin/service/http_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index cd79230a62f..3311dd249e7 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -287,7 +287,7 @@ def init_xmlrpc(): reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler, True)) logging.getLogger("web-services").info("Registered XML-RPC over HTTPS") -class StaticHTTPHandler(HttpLogHandler, HTTPHandler): +class StaticHTTPHandler(HttpLogHandler, FixSendError, HTTPHandler): _logger = logging.getLogger('httpd') def __init__(self,request, client_address, server): From 4e36352f5ebb7a2bbdddd3a249cc5b2f4a428a96 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 14/27] Http server: support the OPTIONS at the multi-handler. Now, fixes nautilus trying to browse the server for WebDAV, conforms to the http standard. Handlers should better inherit the HttpOptions class and define their supported options. bzr revid: p_christ@hol.gr-20100726093335-xzkqw60uyy87mcnq --- bin/service/websrv_lib.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index b0dd0aa8d21..0022fe6daf4 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -175,7 +175,19 @@ class FixSendError: 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 = 'OPTIONS' + + def do_OPTIONS(self): + """return the list of capabilities """ + + self.send_response(200) + self.send_header("Content-Length", 0) + + self.send_header('Allow', self._HTTP_OPTIONS) + self.end_headers() + +class MultiHTTPHandler(FixSendError, HttpOptions, BaseHTTPRequestHandler): """ this is a multiple handler, that will dispatch each request to a nested handler, iff it matches @@ -229,6 +241,9 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler): return mname = 'do_' + fore.command if not hasattr(fore, mname): + if fore.command == 'OPTIONS': + self.do_OPTIONS() + return self.send_error(501, "Unsupported method (%r)" % fore.command) return fore.close_connection = 0 @@ -332,6 +347,14 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler): 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: From 7d24059c09e043ff8bb005914bbfeb217b48102d Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 15/27] Static http: support OPTIONS bzr revid: p_christ@hol.gr-20100726093335-6tvao6uzp90mmcnk --- bin/service/http_server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index 3311dd249e7..75043c1cef3 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -287,8 +287,9 @@ def init_xmlrpc(): reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler, True)) logging.getLogger("web-services").info("Registered XML-RPC over HTTPS") -class StaticHTTPHandler(HttpLogHandler, FixSendError, HTTPHandler): +class StaticHTTPHandler(HttpLogHandler, FixSendError, HttpOptions, HTTPHandler): _logger = logging.getLogger('httpd') + _HTTP_OPTIONS = 'OPTIONS GET HEAD' def __init__(self,request, client_address, server): HTTPHandler.__init__(self,request,client_address,server) From d0eb1d75164ab218856f107ccfdae06dfd1179b4 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 16/27] websrv_lib: fix handling of method exceptions. They should not consume the body (twice), pass the error to the log(..), since it may contain '%' characters. bzr revid: p_christ@hol.gr-20100726093335-wgh34fxda2nt64ze --- bin/service/websrv_lib.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index 0022fe6daf4..b569ab57acb 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -253,8 +253,7 @@ class MultiHTTPHandler(FixSendError, HttpOptions, BaseHTTPRequestHandler): except (AuthRejectedExc, AuthRequiredExc): raise except Exception, e: - self.log_error("Could not run %s: %s" % (mname, e)) - self._get_ignore_body(fore) + 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 From 40758b52221bde338717c259fdd71b7cd852c0a4 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 17/27] httpd, netrpcd: do not catch any exceptions always only catch Exception. Conflicts: bin/service/netrpc_server.py bin/service/web_services.py bin/service/websrv_lib.py bzr revid: p_christ@hol.gr-20100726093335-08uzfv3cr9ffx1ue --- bin/service/netrpc_server.py | 3 ++- bin/service/web_services.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/bin/service/netrpc_server.py b/bin/service/netrpc_server.py index 7f204751576..fcd6657bdc9 100644 --- a/bin/service/netrpc_server.py +++ b/bin/service/netrpc_server.py @@ -48,7 +48,8 @@ class TinySocketClientThread(threading.Thread, netsvc.OpenERPDispatcher): try: self.socket.shutdown( getattr(socket, 'SHUT_RDWR', 2)) - except Exception: pass + except Exception: + pass # That should garbage-collect and close it, too self.sock = None diff --git a/bin/service/web_services.py b/bin/service/web_services.py index 1a710708810..aa52e030252 100644 --- a/bin/service/web_services.py +++ b/bin/service/web_services.py @@ -305,7 +305,7 @@ class db(netsvc.ExportService): else: cr.execute("select decode(datname, 'escape') from pg_database where datname not in('template0', 'template1','postgres') order by datname") res = [str(name) for (name,) in cr.fetchall()] - except: + except Exception: res = [] finally: cr.close() @@ -497,7 +497,7 @@ GNU Public Licence. try: try: base64_decoded = base64.decodestring(zips[module]) - except: + except Exception: l.notifyChannel('migration', netsvc.LOG_ERROR, 'unable to read the module %s' % (module,)) raise @@ -506,13 +506,13 @@ GNU Public Licence. try: try: tools.extract_zip_file(zip_contents, tools.config['addons_path'] ) - except: + except Exception: l.notifyChannel('migration', netsvc.LOG_ERROR, 'unable to extract the module %s' % (module, )) rmtree(module) raise finally: zip_contents.close() - except: + except Exception: l.notifyChannel('migration', netsvc.LOG_ERROR, 'restore the previous version of the module %s' % (module, )) nmp = os.path.join(backup_directory, module) if os.path.isdir(nmp): From bea04e1fc817099115b7799f61c2d1e0b9dee5d3 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 18/27] Websrv_lib, http_server: have special exception logging the websrv_lib must conform to the BaseHTTPServer API, that does not define an exception logging function, nor does it encourage the use of individual pythonic loggers. So, have a function that is optionally called, upon exception. This improves much over the 'log_error()' call, because it allows us to see all those bugs that would make a http method fail. bzr revid: p_christ@hol.gr-20100726093335-e3hxsri766o2fzxp --- bin/service/http_server.py | 3 +++ bin/service/websrv_lib.py | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index 75043c1cef3..fbac6fb9cf4 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -106,6 +106,9 @@ class HttpLogHandler: def log_error(self, format, *args): self._logger.error(format % args) + + def log_exception(self, format, *args): + self._logger.exception(format, *args) def log_request(self, code='-', size='-'): self._logger.log(netsvc.logging.DEBUG_RPC, '"%s" %s %s', diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index b569ab57acb..b0375085d40 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -253,7 +253,10 @@ class MultiHTTPHandler(FixSendError, HttpOptions, BaseHTTPRequestHandler): except (AuthRejectedExc, AuthRequiredExc): raise except Exception, e: - self.log_error("Could not run %s: %s", mname, 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 From 522df9e236062fbc4d5a6dc25ace40b9c25c02e6 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:35 +0300 Subject: [PATCH 19/27] http server: have names at threads. A minor improvement, http client threads have a name now. Conflicts: bin/service/http_server.py bzr revid: p_christ@hol.gr-20100726093335-0p0v34d276u0ks87 --- bin/service/http_server.py | 5 +++++ bin/service/websrv_lib.py | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index fbac6fb9cf4..498d6cf271b 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -74,6 +74,7 @@ class ThreadedHTTPServer(ConnThreadingMixIn, SimpleXMLRPCDispatcher, HTTPServer) HTTPServer.__init__(self, addr, requestHandler) self.numThreads = 0 + self.__threadno = 0 # [Bug #1222790] If possible, set close-on-exec flag; if a # method spawns a subprocess, the subprocess shouldn't have @@ -95,6 +96,10 @@ class ThreadedHTTPServer(ConnThreadingMixIn, SimpleXMLRPCDispatcher, HTTPServer) def _mark_end(self, thread): self.numThreads -= 1 + + def _get_next_name(self): + self.__threadno += 1 + return 'http-client-%d' % self.__threadno class HttpLogHandler: """ helper class for uniform log handling Please define self._logger at each class that is derived from this diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index b0375085d40..997b7a42e7a 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -439,11 +439,15 @@ class ConnThreadingMixIn: # 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) + n = self._get_next_name() + t = threading.Thread(name=n, target = self._handle_request2) if self.daemon_threads: t.setDaemon (1) t.start() From 4c6baedf13c8b585f55029bf5b0d5c29c21b8f80 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:36 +0300 Subject: [PATCH 20/27] netrpc: names at client threads Have a name for client threads, too. bzr revid: p_christ@hol.gr-20100726093336-8me0gn4gvexuzk5r --- bin/service/netrpc_server.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/service/netrpc_server.py b/bin/service/netrpc_server.py index fcd6657bdc9..80cb968c8e9 100644 --- a/bin/service/netrpc_server.py +++ b/bin/service/netrpc_server.py @@ -36,7 +36,9 @@ import tools class TinySocketClientThread(threading.Thread, netsvc.OpenERPDispatcher): def __init__(self, sock, threads): - threading.Thread.__init__(self) + spn = sock and sock.getpeername() + spn = 'netrpc-client-%s:%s' % spn[0:2] + threading.Thread.__init__(self, name=spn) self.sock = sock # Only at the server side, use a big timeout: close the # clients connection when they're idle for 20min. From 018b13240ba21f95bd8b6b256d696b007f0c8d43 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:36 +0300 Subject: [PATCH 21/27] websrv_lib: Log junk http requests If a request doesn't start with a valid HTTP request line, log that whole line. For some strange reason, kernel 2.6.35-rc3+ triggered xml request lines to appear. bzr revid: p_christ@hol.gr-20100726093336-962hxsxoij0pn2ba --- bin/service/websrv_lib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index 997b7a42e7a..98a1e2b6c08 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -323,6 +323,7 @@ class MultiHTTPHandler(FixSendError, HttpOptions, BaseHTTPRequestHandler): [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 From fab3f04e8b7f8c544d2894c63071fd65dc00ca47 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:36 +0300 Subject: [PATCH 22/27] Netsvc: allow late starting of servers If some server is register after the startAll() (for example, the FTP server of a secondary db), allow it to start(), but give it a buffer of 1sec before this is done. Conflicts: bin/netsvc.py bzr revid: p_christ@hol.gr-20100726093336-10htbz41rqx2alut --- bin/netsvc.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/bin/netsvc.py b/bin/netsvc.py index e859eb01ad4..876bca21e00 100644 --- a/bin/netsvc.py +++ b/bin/netsvc.py @@ -317,6 +317,7 @@ class Server: """ __is_started = False __servers = [] + __starter_threads = [] # we don't want blocking server calls (think select()) to # wait forever and possibly prevent exiting the process, @@ -329,12 +330,25 @@ class Server: __logger = logging.getLogger('server') def __init__(self): - if Server.__is_started: - raise Exception('All instances of servers must be inited before the startAll()') Server.__servers.append(self) + if Server.__is_started: + # raise Exception('All instances of servers must be inited before the startAll()') + # Since the startAll() won't be called again, allow this server to + # init and then start it after 1sec (hopefully). Register that + # timer thread in a list, so that we can abort the start if quitAll + # is called in the meantime + t = threading.Timer(1.0, self._late_start) + Server.__starter_threads.append(t) + t.start() def start(self): self.__logger.debug("called stub Server.start") + + def _late_start(self): + self.start() + for thr in Server.__starter_threads: + if thr.finished.is_set(): + Server.__starter_threads.remove(thr) def stop(self): self.__logger.debug("called stub Server.stop") @@ -357,6 +371,11 @@ class Server: if not cls.__is_started: return cls.__logger.info("Stopping %d services" % len(cls.__servers)) + for thr in cls.__starter_threads: + if not thr.finished.is_set(): + thr.cancel() + cls.__starter_threads.remove(thr) + for srv in cls.__servers: srv.stop() cls.__is_started = False From 726a046bc3af8610a29b8d417fc191f7fcdb31a9 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:36 +0300 Subject: [PATCH 23/27] Netsvc: try to have a name for late-start threads. That may help debugging the threads. bzr revid: p_christ@hol.gr-20100726093336-urtx99as4zliho69 --- bin/netsvc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/netsvc.py b/bin/netsvc.py index 876bca21e00..d3133aebae8 100644 --- a/bin/netsvc.py +++ b/bin/netsvc.py @@ -338,6 +338,7 @@ class Server: # timer thread in a list, so that we can abort the start if quitAll # is called in the meantime t = threading.Timer(1.0, self._late_start) + t.name = 'Late start timer for %s' % str(self.__class__) Server.__starter_threads.append(t) t.start() From e8b1da52ab3b90192e9c67c34b3d77664f6946a3 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:36 +0300 Subject: [PATCH 24/27] websrv_lib: fix noconnection.close(), that stub must exist. bzr revid: p_christ@hol.gr-20100726093336-dom7pcu0a38bxlsb --- bin/service/websrv_lib.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index 98a1e2b6c08..3f27c7f1d02 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -142,6 +142,9 @@ class noconnection: def makefile(self, mode, bufsize): return None + def close(self): + pass + class dummyconn: def shutdown(self, tru): pass From d131f6f42d3b80bde6205ed3b8d2d0cde1baf4e7 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:36 +0300 Subject: [PATCH 25/27] HTTPd, websrv_lib: implement extendable OPTIONS handler. For CalDAV, we need to allow the handler to fine-tune its OPTIONS response. So, better cleanup the API and allow request handlers to define their OPTIONS. bzr revid: p_christ@hol.gr-20100726093336-afzp15rys9oua4a0 --- bin/service/http_server.py | 2 +- bin/service/websrv_lib.py | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index 498d6cf271b..cefca98a897 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -297,7 +297,7 @@ def init_xmlrpc(): class StaticHTTPHandler(HttpLogHandler, FixSendError, HttpOptions, HTTPHandler): _logger = logging.getLogger('httpd') - _HTTP_OPTIONS = 'OPTIONS GET HEAD' + _HTTP_OPTIONS = { 'Allow': ['OPTIONS', 'GET', 'HEAD'] } def __init__(self,request, client_address, server): HTTPHandler.__init__(self,request,client_address,server) diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index 3f27c7f1d02..ba8c17a2e70 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -179,17 +179,37 @@ class FixSendError: self.wfile.write(content) class HttpOptions: - _HTTP_OPTIONS = 'OPTIONS' + _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) - self.send_header('Allow', self._HTTP_OPTIONS) + 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 From a35482142c25a515236fa4aecc5dfb1316ec58bd Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 26 Jul 2010 12:33:36 +0300 Subject: [PATCH 26/27] Httpd: update copyright info bzr revid: p_christ@hol.gr-20100726093336-1wdzs6scdepk6cqz --- bin/service/http_server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index cefca98a897..38b444de0bc 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # -# Copyright P. Christeas 2008,2009 +# Copyright P. Christeas 2008-2010 +# Copyright 2010 OpenERP SA. (http://www.openerp.com) # # # WARNING: This program as such is intended to be used by professional From 9002f4795114b02d9082bccc3f5472acf10fbe98 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Wed, 28 Jul 2010 15:29:09 +0300 Subject: [PATCH 27/27] HTTPd: fix registering of XML-RPC in a ssl server. If it has already been registered for httpd, we don't need to do that again. Otherwise, fix the typo in the args. bzr revid: p_christ@hol.gr-20100728122909-ahctuv3uyasof1df --- bin/service/http_server.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index 38b444de0bc..519b8fca675 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -292,9 +292,11 @@ def init_xmlrpc(): reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler)) logging.getLogger("web-services").info("Registered XML-RPC over HTTP") - if tools.config.get('xmlrpcs', False): - reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler, True)) - logging.getLogger("web-services").info("Registered XML-RPC over HTTPS") + if tools.config.get('xmlrpcs', False) \ + and not tools.config.get('xmlrpc', False): + # only register at the secure server + reg_http_service(HTTPDir('/xmlrpc/', XMLRPCRequestHandler), True) + logging.getLogger("web-services").info("Registered XML-RPC over HTTPS only") class StaticHTTPHandler(HttpLogHandler, FixSendError, HttpOptions, HTTPHandler): _logger = logging.getLogger('httpd')