[IMP] wsgi: WSGI-to-BaseHTTPRequestHandler, there are
probably some shortcomings but the document_webdav/test/webdav_test1.yml tests pass. bzr revid: vmt@openerp.com-20110907141318-4gxmyztv1zp6sk7q
This commit is contained in:
parent
0a6fe4d1f7
commit
14a82cdf9f
|
@ -3,7 +3,7 @@ import openerp
|
|||
bind = '127.0.0.1:8069'
|
||||
pidfile = '.gunicorn.pid'
|
||||
# This is the big TODO: safely use more than a single worker.
|
||||
workers = 2
|
||||
workers = 1
|
||||
# Some application-wide initialization is needed.
|
||||
on_starting = openerp.wsgi.on_starting
|
||||
when_ready = openerp.wsgi.when_ready
|
||||
|
|
185
openerp/wsgi.py
185
openerp/wsgi.py
|
@ -27,7 +27,10 @@ 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 os
|
||||
import signal
|
||||
|
@ -89,6 +92,8 @@ def wsgi_xmlrpc(environ, start_response):
|
|||
elif service == 'report':
|
||||
return xmlrpc_return(start_response, 'report', method, params)
|
||||
|
||||
# TODO the body has been read, need to raise an exception (not return None).
|
||||
|
||||
def legacy_wsgi_xmlrpc(environ, start_response):
|
||||
if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith('/xmlrpc/'):
|
||||
length = int(environ['CONTENT_LENGTH'])
|
||||
|
@ -105,6 +110,157 @@ def wsgi_modules(environ, start_response):
|
|||
""" WSGI handler dispatching to addons-provided entry points."""
|
||||
pass
|
||||
|
||||
def wsgi_webdav(environ, start_response):
|
||||
if environ['REQUEST_METHOD'] == 'OPTIONS' and environ['PATH_INFO'] == '*':
|
||||
return return_options(start_response)
|
||||
if environ['PATH_INFO'].startswith('/webdav'): # TODO depends on config
|
||||
environ['PATH_INFO'] = '/' + environ['PATH_INFO'][len('/webdav'):]
|
||||
return wsgi_to_http(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')]
|
||||
start_response("200 OK", [('Content-Length', str(0))] + options)
|
||||
return []
|
||||
|
||||
webdav = None
|
||||
|
||||
def wsgi_to_http(environ, start_response):
|
||||
"""
|
||||
Forward a WSGI request to a BaseHTTPRequestHandler.
|
||||
|
||||
This code is adapted from wbsrv_lib.MultiHTTPHandler._handle_one_foreign().
|
||||
It is a temporary solution: the HTTP sub-handlers (in particular the
|
||||
document_webdav addon) have to be WSGIfied.
|
||||
"""
|
||||
global webdav
|
||||
# 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)
|
||||
|
||||
scheme = environ['wsgi.url_scheme']
|
||||
|
||||
headers = {}
|
||||
for key, value in environ.items():
|
||||
if key.startswith('HTTP_'):
|
||||
key = key[5:].replace('_', '-').title()
|
||||
headers[key] = value
|
||||
if key == 'CONTENT_LENGTH':
|
||||
key = key.replace('_', '-').title()
|
||||
headers[key] = value
|
||||
if environ.get('Content-Type'):
|
||||
headers['Content-Type'] = environ['Content-Type']
|
||||
|
||||
path = urllib.quote(environ.get('PATH_INFO', ''))
|
||||
if environ.get('QUERY_STRING'):
|
||||
path += '?' + environ['QUERY_STRING']
|
||||
|
||||
class Dummy():
|
||||
pass
|
||||
server = Dummy()
|
||||
server.server_name = environ['SERVER_NAME']
|
||||
server.server_port = int(environ['SERVER_PORT'])
|
||||
con = openerp.service.websrv_lib.noconnection(environ['gunicorn.socket']) # None
|
||||
fore = webdav.handler(openerp.service.websrv_lib.noconnection(con), environ['REMOTE_ADDR'], server)
|
||||
|
||||
# let's pretend we are a Multi handler
|
||||
class M():
|
||||
def __init__(self):
|
||||
self.sec_realms = {}
|
||||
self.shared_headers = []
|
||||
self.shared_response = ''
|
||||
self.shared_body = ''
|
||||
def send_error(self, code, msg):
|
||||
self.shared_response = str(code) + ' ' + msg
|
||||
def send_response(self, code, msg):
|
||||
self.shared_response = str(code) + ' ' + msg
|
||||
def send_header(self, a, b):
|
||||
self.shared_headers.append((a, b))
|
||||
def end_headers(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
multi = M()
|
||||
|
||||
webdav.auth_provider.setupAuth(multi, fore)
|
||||
|
||||
request_version = 'HTTP/1.1' # TODO
|
||||
fore.wfile = StringIO.StringIO()
|
||||
fore.rfile = environ['wsgi.input']
|
||||
fore.headers = headers
|
||||
fore.command = environ['REQUEST_METHOD']
|
||||
fore.path = path
|
||||
fore.request_version = request_version
|
||||
fore.close_connection = 1
|
||||
|
||||
fore.raw_requestline = "%s %s %s\n" % (environ['REQUEST_METHOD'], path, request_version)
|
||||
fore.requestline = fore.raw_requestline
|
||||
|
||||
from openerp.service.websrv_lib import AuthRequiredExc, AuthRejectedExc
|
||||
|
||||
def go():
|
||||
auth_provider = webdav.auth_provider
|
||||
if auth_provider and auth_provider.realm:
|
||||
try:
|
||||
multi.sec_realms[auth_provider.realm].checkRequest(fore, path)
|
||||
except AuthRequiredExc, ae:
|
||||
# Darwin 9.x.x webdav clients will report "HTTP/1.0" to us, while they support (and need) the
|
||||
# authorisation features of HTTP/1.1
|
||||
if request_version != 'HTTP/1.1' and ('Darwin/9.' not in fore.headers.get('User-Agent', '')):
|
||||
print 'self.log_error("Cannot require auth at %s", self.request_version)'
|
||||
multi.send_error(403)
|
||||
return
|
||||
#self._get_ignore_body(fore) # consume any body that came, not loose sync with input
|
||||
multi.send_response(401,'Authorization required')
|
||||
multi.send_header('WWW-Authenticate','%s realm="%s"' % (ae.atype,ae.realm))
|
||||
multi.send_header('Connection', 'keep-alive')
|
||||
multi.send_header('Content-Type','text/html')
|
||||
multi.send_header('Content-Length', 4) # len(self.auth_required_msg))
|
||||
multi.end_headers()
|
||||
#self.wfile.write(self.auth_required_msg)
|
||||
multi.shared_body = 'Blah'
|
||||
return
|
||||
except AuthRejectedExc,e:
|
||||
print '("Rejected auth: %s" % e.args[0])'
|
||||
multi.send_error(403,e.args[0])
|
||||
return
|
||||
mname = 'do_' + fore.command
|
||||
if not hasattr(fore, mname):
|
||||
if fore.command == 'OPTIONS':
|
||||
return return_options(start_response)
|
||||
multi.send_error(501, "Unsupported method (%r)" % fore.command)
|
||||
return
|
||||
method = getattr(fore, mname)
|
||||
try:
|
||||
method()
|
||||
if hasattr(fore, '_flush'):
|
||||
fore._flush()
|
||||
response = fore.wfile.getvalue()
|
||||
class DummySocket(StringIO.StringIO):
|
||||
"""
|
||||
This is used to provide a StringIO to httplib.HTTPResponse
|
||||
which, instead of taking a file object, expects a socket and
|
||||
uses its makefile() method.
|
||||
"""
|
||||
def makefile(self, *args, **kw):
|
||||
return self
|
||||
response = httplib.HTTPResponse(DummySocket(response))
|
||||
response.begin()
|
||||
response_headers = response.getheaders()
|
||||
body = response.read()
|
||||
start_response(str(response.status) + ' ' + response.reason, response_headers)
|
||||
return [body]
|
||||
except (AuthRejectedExc, AuthRequiredExc):
|
||||
raise
|
||||
except Exception, e:
|
||||
multi.send_error(500, "Internal error")
|
||||
return
|
||||
res = go()
|
||||
if res is None:
|
||||
start_response(multi.shared_response, multi.shared_headers)
|
||||
return [multi.shared_body]
|
||||
else:
|
||||
return res
|
||||
|
||||
# WSGI handlers provided by modules loaded with the --load command-line option.
|
||||
module_handlers = []
|
||||
|
||||
|
@ -121,11 +277,12 @@ def application(environ, start_response):
|
|||
|
||||
# Try all handlers until one returns some result (i.e. not None).
|
||||
wsgi_handlers = [
|
||||
wsgi_xmlrpc,
|
||||
wsgi_jsonrpc,
|
||||
legacy_wsgi_xmlrpc,
|
||||
wsgi_modules,
|
||||
] + module_handlers
|
||||
#wsgi_xmlrpc,
|
||||
#wsgi_jsonrpc,
|
||||
#legacy_wsgi_xmlrpc,
|
||||
#wsgi_modules,
|
||||
wsgi_webdav
|
||||
] #+ module_handlers
|
||||
for handler in wsgi_handlers:
|
||||
result = handler(environ, start_response)
|
||||
if result is None:
|
||||
|
@ -154,11 +311,27 @@ def on_starting(server):
|
|||
global arbiter_pid
|
||||
arbiter_pid = os.getpid() # TODO check if this is true even after replacing the executable
|
||||
config = openerp.tools.config
|
||||
config['addons_path'] = '/home/openerp/repos/addons/trunk-xmlrpc-no-osv-memory' # need a config file
|
||||
config['addons_path'] = '/home/openerp/repos/addons/trunk-xmlrpc' # need a config file
|
||||
#config['log_level'] = 10 # debug
|
||||
#openerp.tools.cache = kill_workers_cache
|
||||
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):
|
||||
|
|
Loading…
Reference in New Issue