[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
This commit is contained in:
Vo Minh Thu 2011-09-14 12:43:00 +02:00
parent d593e484c4
commit 46ad605226
4 changed files with 59 additions and 46 deletions

View File

@ -145,10 +145,7 @@ def start_services():
# Export (for RPC) services. # Export (for RPC) services.
openerp.service.web_services.start_web_services() openerp.service.web_services.start_web_services()
# Initialize the HTTP stack. # Start the NET-RPC server.
http_server.init_servers()
http_server.init_xmlrpc()
http_server.init_static_http()
netrpc_server.init_servers() netrpc_server.init_servers()
# Start the main cron thread. # Start the main cron thread.
@ -157,6 +154,9 @@ def start_services():
# Start the top-level servers threads (normally HTTP, HTTPS, and NETRPC). # Start the top-level servers threads (normally HTTP, HTTPS, and NETRPC).
openerp.netsvc.Server.startAll() 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 # Variable keeping track of the number of calls to the signal handler defined
# below. This variable is monitored by ``quit_on_signals()``. # below. This variable is monitored by ``quit_on_signals()``.
quit_signals_received = 0 quit_signals_received = 0
@ -268,15 +268,10 @@ if __name__ == "__main__":
for m in openerp.conf.server_wide_modules: for m in openerp.conf.server_wide_modules:
__import__(m) __import__(m)
# Register a WSGI entry point if any. # Call any post_load hook.
info = openerp.modules.module.load_information_from_description_file(m) info = openerp.modules.module.load_information_from_description_file(m)
if info['wsgi']: if info['post_load']:
openerp.wsgi.register_wsgi_handler(getattr(sys.modules[m], info['wsgi'])) getattr(sys.modules[m], info['post_load'])()
#openerp.osv.osv.start_object_proxy()
#openerp.service.web_services.start_web_services()
#openerp.wsgi.serve()
setup_pid_file() setup_pid_file()
logger = logging.getLogger('server') logger = logging.getLogger('server')

View File

@ -254,7 +254,8 @@ def load_information_from_description_file(module):
info['license'] = info.get('license') or 'AGPL-3' info['license'] = info.get('license') or 'AGPL-3'
info.setdefault('installable', True) info.setdefault('installable', True)
info.setdefault('active', False) 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', for kind in ['data', 'demo', 'test',
'init_xml', 'update_xml', 'demo_xml']: 'init_xml', 'update_xml', 'demo_xml']:
info.setdefault(kind, []) info.setdefault(kind, [])

View File

@ -1,10 +1,12 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
import threading
import types import types
import time # used to eval time.strftime expressions import time # used to eval time.strftime expressions
from datetime import datetime, timedelta from datetime import datetime, timedelta
import logging import logging
import openerp.pooler as pooler import openerp.pooler as pooler
import openerp.sql_db as sql_db
import misc import misc
from config import config from config import config
import yaml_tag import yaml_tag
@ -800,4 +802,19 @@ def yaml_import(cr, module, yamlfile, idref=None, mode='init', noupdate=False):
# keeps convention of convert.py # keeps convention of convert.py
convert_yaml_import = yaml_import 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: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -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 httplib
import urllib import urllib
import xmlrpclib import xmlrpclib
import StringIO import StringIO
import logging
import os import os
import signal import signal
import sys import sys
@ -109,11 +108,7 @@ def wsgi_jsonrpc(environ, start_response):
def wsgi_webdav(environ, start_response): def wsgi_webdav(environ, start_response):
if environ['REQUEST_METHOD'] == 'OPTIONS' and environ['PATH_INFO'] == '*': if environ['REQUEST_METHOD'] == 'OPTIONS' and environ['PATH_INFO'] == '*':
return return_options(start_response) return return_options(environ, 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)
http_dir = websrv_lib.find_http_service(environ['PATH_INFO']) http_dir = websrv_lib.find_http_service(environ['PATH_INFO'])
if http_dir: if http_dir:
@ -124,15 +119,20 @@ def wsgi_webdav(environ, start_response):
environ['PATH_INFO'] = '/' + path environ['PATH_INFO'] = '/' + path
return http_to_wsgi(http_dir)(environ, start_response) return http_to_wsgi(http_dir)(environ, start_response)
def return_options(start_response): def return_options(environ, start_response):
# TODO Microsoft specifi header, see websrv_lib do_OPTIONS # Microsoft specific header, see
options = [('DAV', '1 2'), ('Allow', 'GET HEAD PROPFIND OPTIONS REPORT')] # 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) start_response("200 OK", [('Content-Length', str(0))] + options)
return [] return []
def http_to_wsgi(http_dir): 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 Actually the argument is not a bare BaseHTTPRequestHandler but is wrapped
(as a class, so it needs to be instanciated) in a HTTPDir. (as a class, so it needs to be instanciated) in a HTTPDir.
@ -219,7 +219,7 @@ def http_to_wsgi(http_dir):
# needed. # needed.
if not hasattr(handler, method_name): if not hasattr(handler, method_name):
if handler.command == 'OPTIONS': if handler.command == 'OPTIONS':
return return_options(start_response) return return_options(environ, start_response)
start_response("501 Unsupported method (%r)" % handler.command, []) start_response("501 Unsupported method (%r)" % handler.command, [])
return [] return []
@ -257,7 +257,7 @@ def parse_http_response(s):
response.begin() response.begin()
return response 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 = [] module_handlers = []
def register_wsgi_handler(handler): def register_wsgi_handler(handler):
@ -284,17 +284,32 @@ def application(environ, start_response):
continue continue
return result 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' 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] return [response]
def serve(): 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() httpd.serve_forever()
# Master process id, can be used for signaling. # Master process id, can be used for signaling.
@ -310,21 +325,6 @@ def on_starting(server):
openerp.netsvc.init_logger() openerp.netsvc.init_logger()
openerp.osv.osv.start_object_proxy() openerp.osv.osv.start_object_proxy()
openerp.service.web_services.start_web_services() 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. # Install our own signal handler on the master process.
def when_ready(server): def when_ready(server):