[MERGE] merged long-polling branch:
[odoo/odoo.git] / openerp / service / wsgi_server.py
index 00ad545..e9e391d 100644 (file)
@@ -34,6 +34,7 @@ import errno
 import logging
 import os
 import signal
+import socket
 import sys
 import threading
 import traceback
@@ -90,10 +91,10 @@ def xmlrpc_return(start_response, service, method, params, legacy_exceptions=Fal
     return [response]
 
 def xmlrpc_handle_exception(e):
-    if isinstance(e, openerp.osv.osv.except_osv): # legacy
+    if isinstance(e, openerp.osv.orm.except_orm): # legacy
         fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, openerp.tools.ustr(e.value))
         response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
-    elif isinstance(e, openerp.exceptions.Warning):
+    elif isinstance(e, openerp.exceptions.Warning) or isinstance(e, openerp.exceptions.RedirectWarning):
         fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, str(e))
         response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
     elif isinstance (e, openerp.exceptions.AccessError):
@@ -123,7 +124,7 @@ def xmlrpc_handle_exception(e):
     return response
 
 def xmlrpc_handle_exception_legacy(e):
-    if isinstance(e, openerp.osv.osv.except_osv):
+    if isinstance(e, openerp.osv.orm.except_orm):
         fault = xmlrpclib.Fault('warning -- ' + e.name + '\n\n' + e.value, '')
         response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
     elif isinstance(e, openerp.exceptions.Warning):
@@ -311,7 +312,7 @@ def http_to_wsgi(http_dir):
                 handler.auth_provider.checkRequest(handler, path)
             except websrv_lib.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 
+                # authorisation features of HTTP/1.1
                 if request_version != 'HTTP/1.1' and ('Darwin/9.' not in handler.headers.get('User-Agent', '')):
                     start_response("403 Forbidden", [])
                     return []
@@ -373,6 +374,8 @@ def parse_http_response(s):
 
 # WSGI handlers registered through the register_wsgi_handler() function below.
 module_handlers = []
+# RPC endpoints registered through the register_rpc_endpoint() function below.
+rpc_handlers = {}
 
 def register_wsgi_handler(handler):
     """ Register a WSGI handler.
@@ -382,6 +385,11 @@ def register_wsgi_handler(handler):
     """
     module_handlers.append(handler)
 
+def register_rpc_endpoint(endpoint, handler):
+    """ Register a handler for a given RPC enpoint.
+    """
+    rpc_handlers[endpoint] = handler
+
 def application_unproxied(environ, start_response):
     """ WSGI entry point."""
     openerp.service.start_internal()
@@ -410,41 +418,63 @@ def application(environ, start_response):
 # The WSGI server, started by start_server(), stopped by stop_server().
 httpd = None
 
-def serve():
+def serve(interface, port, threaded):
     """ Serve HTTP requests via werkzeug development server.
 
-    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.
     """
 
     global httpd
-
-    # TODO Change the xmlrpc_* options to http_*
-    interface = config['xmlrpc_interface'] or '0.0.0.0'
-    port = config['xmlrpc_port']
-    if not openerp.tools.config.options["gevent"]:
-        httpd = werkzeug.serving.make_server(interface, port, application, threaded=True)
+    if not openerp.evented:
+        httpd = werkzeug.serving.make_server(interface, port, application, threaded=threaded)
     else:
         from gevent.wsgi import WSGIServer
         httpd = WSGIServer((interface, port), application)
-    _logger.info('HTTP service (werkzeug) running on %s:%s', interface, port)
     httpd.serve_forever()
 
-def start_server():
+def start_service():
     """ Call serve() in its own thread.
 
     The WSGI server can be shutdown with stop_server() below.
     """
-    threading.Thread(target=serve).start()
+    # TODO Change the xmlrpc_* options to http_*
+    interface = config['xmlrpc_interface'] or '0.0.0.0'
+    port = config['xmlrpc_port']
+    _logger.info('HTTP service (werkzeug) running on %s:%s', interface, port)
+    threading.Thread(target=serve, args=(interface, port, True)).start()
 
-def stop_server():
+def stop_service():
     """ Initiate the shutdown of the WSGI server.
 
     The server is supposed to have been started by start_server() above.
     """
     if httpd:
-        httpd.shutdown()
+        if not openerp.evented:
+            httpd.shutdown()
+            close_socket(httpd.socket)
+        else:
+            import gevent
+            httpd.stop()
+            gevent.shutdown()
+
+def close_socket(sock):
+    """ Closes a socket instance cleanly
+
+    :param sock: the network socket to close
+    :type sock: socket.socket
+    """
+    try:
+        sock.shutdown(socket.SHUT_RDWR)
+    except socket.error, e:
+        # On OSX, socket shutdowns both sides if any side closes it
+        # causing an error 57 'Socket is not connected' on shutdown
+        # of the other side (or something), see
+        # http://bugs.python.org/issue4397
+        # note: stdlib fixed test, not behavior
+        if e.errno != errno.ENOTCONN or platform.system() not in ['Darwin', 'Windows']:
+            raise
+    sock.close()
+
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: