From 46ad605226d79bf3a5dd09d2c344ec1fa284d1a3 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Wed, 14 Sep 2011 12:43:00 +0200 Subject: [PATCH] [IMP] wsgi: - added Microsoft specific header for webdav. - serve WSGI handlers with werkzeug when available. - effectively use WSGI instead of netsvc HTTP stack. bzr revid: vmt@openerp.com-20110914104300-n0l3dnmdu3jau7w2 --- openerp-server | 19 ++++------- openerp/modules/module.py | 3 +- openerp/tools/yaml_import.py | 17 ++++++++++ openerp/wsgi.py | 66 ++++++++++++++++++------------------ 4 files changed, 59 insertions(+), 46 deletions(-) diff --git a/openerp-server b/openerp-server index b9404c7361e..e01f0f385c2 100755 --- a/openerp-server +++ b/openerp-server @@ -145,10 +145,7 @@ def start_services(): # Export (for RPC) services. openerp.service.web_services.start_web_services() - # Initialize the HTTP stack. - http_server.init_servers() - http_server.init_xmlrpc() - http_server.init_static_http() + # Start the NET-RPC server. netrpc_server.init_servers() # Start the main cron thread. @@ -157,6 +154,9 @@ def start_services(): # Start the top-level servers threads (normally HTTP, HTTPS, and NETRPC). openerp.netsvc.Server.startAll() + # Start the WSGI server. + threading.Thread(target=openerp.wsgi.serve).start() + # Variable keeping track of the number of calls to the signal handler defined # below. This variable is monitored by ``quit_on_signals()``. quit_signals_received = 0 @@ -268,15 +268,10 @@ if __name__ == "__main__": for m in openerp.conf.server_wide_modules: __import__(m) - # Register a WSGI entry point if any. + # Call any post_load hook. info = openerp.modules.module.load_information_from_description_file(m) - if info['wsgi']: - openerp.wsgi.register_wsgi_handler(getattr(sys.modules[m], info['wsgi'])) - - #openerp.osv.osv.start_object_proxy() - #openerp.service.web_services.start_web_services() - #openerp.wsgi.serve() - + if info['post_load']: + getattr(sys.modules[m], info['post_load'])() setup_pid_file() logger = logging.getLogger('server') diff --git a/openerp/modules/module.py b/openerp/modules/module.py index cd5e6537a6d..a33c2651a85 100644 --- a/openerp/modules/module.py +++ b/openerp/modules/module.py @@ -254,7 +254,8 @@ def load_information_from_description_file(module): info['license'] = info.get('license') or 'AGPL-3' info.setdefault('installable', True) info.setdefault('active', False) - info.setdefault('wsgi', None) # WSGI entry point, given as a string + # If the following is provided, it is called after the module is --loaded. + info.setdefault('post_load', None) for kind in ['data', 'demo', 'test', 'init_xml', 'update_xml', 'demo_xml']: info.setdefault(kind, []) diff --git a/openerp/tools/yaml_import.py b/openerp/tools/yaml_import.py index 00bb79f0499..81fec089fe1 100644 --- a/openerp/tools/yaml_import.py +++ b/openerp/tools/yaml_import.py @@ -1,10 +1,12 @@ # -*- encoding: utf-8 -*- +import threading import types import time # used to eval time.strftime expressions from datetime import datetime, timedelta import logging import openerp.pooler as pooler +import openerp.sql_db as sql_db import misc from config import config import yaml_tag @@ -800,4 +802,19 @@ def yaml_import(cr, module, yamlfile, idref=None, mode='init', noupdate=False): # keeps convention of convert.py convert_yaml_import = yaml_import +def threaded_yaml_import(db_name, module_name, file_name, delay=0): + def f(): + time.sleep(delay) + cr = None + fp = None + try: + cr = sql_db.db_connect(db_name).cursor() + fp = misc.file_open(file_name) + convert_yaml_import(cr, module_name, fp, {}, 'update', True) + finally: + if cr: cr.close() + if fp: fp.close() + threading.Thread(target=f).start() + + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/wsgi.py b/openerp/wsgi.py index 2bfb793f58b..9f374acb374 100644 --- a/openerp/wsgi.py +++ b/openerp/wsgi.py @@ -25,13 +25,12 @@ This module offers a WSGI interface to OpenERP. """ -from wsgiref.simple_server import make_server -from SimpleXMLRPCServer import SimpleXMLRPCDispatcher import httplib import urllib import xmlrpclib import StringIO +import logging import os import signal import sys @@ -109,11 +108,7 @@ def wsgi_jsonrpc(environ, start_response): def wsgi_webdav(environ, start_response): if environ['REQUEST_METHOD'] == 'OPTIONS' and environ['PATH_INFO'] == '*': - return return_options(start_response) - - # Make sure the addons are loaded in the registry, so they have a chance - # to register themselves in the 'service' layer. - openerp.pooler.get_db_and_pool('xx', update_module=[], pooljobs=False) + return return_options(environ, start_response) http_dir = websrv_lib.find_http_service(environ['PATH_INFO']) if http_dir: @@ -124,15 +119,20 @@ def wsgi_webdav(environ, start_response): environ['PATH_INFO'] = '/' + path return http_to_wsgi(http_dir)(environ, start_response) -def return_options(start_response): - # TODO Microsoft specifi header, see websrv_lib do_OPTIONS - options = [('DAV', '1 2'), ('Allow', 'GET HEAD PROPFIND OPTIONS REPORT')] +def return_options(environ, start_response): + # Microsoft specific header, see + # http://www.ibm.com/developerworks/rational/library/2089.html + if 'Microsoft' in environ.get('User-Agent', ''): + option = [('MS-Author-Via', 'DAV')] + else: + option = [] + options += [('DAV', '1 2'), ('Allow', 'GET HEAD PROPFIND OPTIONS REPORT')] start_response("200 OK", [('Content-Length', str(0))] + options) return [] def http_to_wsgi(http_dir): """ - Turn BaseHTTPRequestHandler into a WSGI entry point. + Turn a BaseHTTPRequestHandler into a WSGI entry point. Actually the argument is not a bare BaseHTTPRequestHandler but is wrapped (as a class, so it needs to be instanciated) in a HTTPDir. @@ -219,7 +219,7 @@ def http_to_wsgi(http_dir): # needed. if not hasattr(handler, method_name): if handler.command == 'OPTIONS': - return return_options(start_response) + return return_options(environ, start_response) start_response("501 Unsupported method (%r)" % handler.command, []) return [] @@ -257,7 +257,7 @@ def parse_http_response(s): response.begin() return response -# WSGI handlers provided by modules loaded with the --load command-line option. +# WSGI handlers registered through the register_wsgi_handler() function below. module_handlers = [] def register_wsgi_handler(handler): @@ -284,17 +284,32 @@ def application(environ, start_response): continue return result - # We never returned from the loop. Needs something else than 200 OK. + # We never returned from the loop. response = 'No handler found.\n' - start_response('200 OK', [('Content-Type', 'text/plain'), ('Content-Length', str(len(response)))]) + start_response('404 Not Found', [('Content-Type', 'text/plain'), ('Content-Length', str(len(response)))]) return [response] def serve(): - """ Serve XMLRPC requests via wsgiref's simple_server. + """ Serve XMLRPC requests via werkzeug development server. - Blocking, should probably be called in its own process. + If werkzeug can not be imported, we fall back to wsgiref's simple_server. + + Calling this function is blocking, you might want to call it in its own + thread. """ - httpd = make_server('localhost', config['xmlrpc_port'], application) + + # TODO Change the xmlrpc_port option to http_port. + try: + import werkzeug.serving + httpd = werkzeug.serving.make_server('localhost', + config['xmlrpc_port'], application, threaded=True) + except ImportError, e: + import wsgiref.simple_server + logging.getLogger('wsgi').warn('Can not import werkzeug, ' + 'falling back to wsgiref.') + httpd = wsgiref.simple_server.make_server('localhost', + config['xmlrpc_port'], application) + httpd.serve_forever() # Master process id, can be used for signaling. @@ -310,21 +325,6 @@ def on_starting(server): openerp.netsvc.init_logger() openerp.osv.osv.start_object_proxy() openerp.service.web_services.start_web_services() - #test_in_thread() - -def test_in_thread(): - def f(): - import time - time.sleep(2) - print ">>>> test thread" - cr = openerp.sql_db.db_connect('xx').cursor() - module_name = 'document_webdav' - fp = openerp.tools.file_open('/home/openerp/repos/addons/trunk-xmlrpc/document_webdav/test/webdav_test1.yml') - openerp.tools.convert_yaml_import(cr, module_name, fp, {}, 'update', True) - cr.close() - print "<<<< test thread" - import threading - threading.Thread(target=f).start() # Install our own signal handler on the master process. def when_ready(server):