[FIX] @charset in CSS files
[odoo/odoo.git] / openerp / http.py
index ac5ad34..c51934f 100644 (file)
@@ -21,7 +21,6 @@ import time
 import traceback
 import urlparse
 import warnings
-from pprint import pformat
 
 import babel.core
 import psutil
@@ -36,9 +35,8 @@ import werkzeug.wrappers
 import werkzeug.wsgi
 
 import openerp
-import openerp.netsvc
 from openerp.service import security, model as service_model
-import openerp.tools
+from openerp.tools.func import lazy_property
 
 _logger = logging.getLogger(__name__)
 
@@ -61,13 +59,6 @@ def replace_request_password(args):
         args[2] = '*'
     return tuple(args)
 
-def log(logger, level, prefix, msg, depth=None):
-    indent=''
-    indent_after=' '*len(prefix)
-    for line in (prefix+pformat(msg, depth=depth)).split('\n'):
-        logger.log(level, indent+line)
-        indent=indent_after
-
 def dispatch_rpc(service_name, method, params):
     """ Handle a RPC call.
 
@@ -84,7 +75,7 @@ def dispatch_rpc(service_name, method, params):
             start_rss, start_vms = 0, 0
             start_rss, start_vms = psutil.Process(os.getpid()).get_memory_info()
             if rpc_request and rpc_response_flag:
-                log(rpc_request, logging.DEBUG, '%s.%s' % (service_name, method), replace_request_password(params))
+                openerp.netsvc.log(rpc_request, logging.DEBUG, '%s.%s' % (service_name, method), replace_request_password(params))
 
         threading.current_thread().uid = None
         threading.current_thread().dbname = None
@@ -106,9 +97,9 @@ def dispatch_rpc(service_name, method, params):
             end_rss, end_vms = psutil.Process(os.getpid()).get_memory_info()
             logline = '%s.%s time:%.3fs mem: %sk -> %sk (diff: %sk)' % (service_name, method, end_time - start_time, start_vms / 1024, end_vms / 1024, (end_vms - start_vms)/1024)
             if rpc_response_flag:
-                log(rpc_response, logging.DEBUG, logline, result)
+                openerp.netsvc.log(rpc_response, logging.DEBUG, logline, result)
             else:
-                log(rpc_request, logging.DEBUG, logline, replace_request_password(params), depth=1)
+                openerp.netsvc.log(rpc_request, logging.DEBUG, logline, replace_request_password(params), depth=1)
 
         return result
     except (openerp.osv.orm.except_orm, openerp.exceptions.AccessError, \
@@ -207,6 +198,10 @@ class WebRequest(object):
         self.auth_method = None
         self._cr_cm = None
         self._cr = None
+
+        # prevents transaction commit, use when you catch an exception during handling
+        self._failed = None
+
         # set db/uid trackers - they're cleaned up at the WSGI
         # dispatching phase in openerp.service.wsgi_server.application
         if self.db:
@@ -240,10 +235,7 @@ class WebRequest(object):
         """
         # some magic to lazy create the cr
         if not self._cr:
-            # Test cursors
-            self._cr = openerp.tests.common.acquire_test_cursor(self.session_id)
-            if not self._cr:
-                self._cr = self.registry.db.cursor()
+            self._cr = self.registry.cursor()
         return self._cr
 
     def __enter__(self):
@@ -254,11 +246,9 @@ class WebRequest(object):
         _request_stack.pop()
 
         if self._cr:
-            # Dont commit test cursors
-            if not openerp.tests.common.release_test_cursor(self.session_id):
-                if exc_type is None:
-                    self._cr.commit()
-                self._cr.close()
+            if exc_type is None and not self._failed:
+                self._cr.commit()
+            self._cr.close()
         # just to be sure no one tries to re-use the request
         self.disable_db = True
         self.uid = None
@@ -272,6 +262,13 @@ class WebRequest(object):
         self.endpoint = endpoint
         self.auth_method = auth
 
+
+    def _handle_exception(self, exception):
+        """Called within an except block to allow converting exceptions
+           to abitrary responses. Anything returned (except None) will
+           be used as response.""" 
+        raise 
+
     def _call_function(self, *args, **kwargs):
         request = self
         if self.endpoint.routing['type'] != self._request_type:
@@ -423,35 +420,15 @@ class JsonRequest(WebRequest):
         self.params = dict(self.jsonrequest.get("params", {}))
         self.context = self.params.pop('context', dict(self.session.context))
 
-    def dispatch(self):
-        """ Calls the method asked for by the JSON-RPC2 or JSONP request
-        """
-        if self.jsonp_handler:
-            return self.jsonp_handler()
-        response = {"jsonrpc": "2.0" }
-        error = None
-
-        try:
-            response['id'] = self.jsonrequest.get('id')
-            response["result"] = self._call_function(**self.params)
-        except AuthenticationError, e:
-            _logger.exception("Exception during JSON request handling.")
-            se = serialize_exception(e)
-            error = {
-                'code': 100,
-                'message': "OpenERP Session Invalid",
-                'data': se
-            }
-        except Exception, e:
-            _logger.exception("Exception during JSON request handling.")
-            se = serialize_exception(e)
-            error = {
-                'code': 200,
-                'message': "OpenERP Server Error",
-                'data': se
-            }
-        if error:
-            response["error"] = error
+    def _json_response(self, result=None, error=None):
+        response = {
+            'jsonrpc': '2.0',
+            'id': self.jsonrequest.get('id')
+        }
+        if error is not None:
+            response['error'] = error
+        if result is not None:
+            response['result'] = result
 
         if self.jsonp:
             # If we use jsonp, that's mean we are called from another host
@@ -464,8 +441,36 @@ class JsonRequest(WebRequest):
             mime = 'application/json'
             body = simplejson.dumps(response)
 
-        r = Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))])
-        return r
+        return Response(
+                    body, headers=[('Content-Type', mime),
+                                   ('Content-Length', len(body))])
+
+    def _handle_exception(self, exception):
+        """Called within an except block to allow converting exceptions
+           to abitrary responses. Anything returned (except None) will
+           be used as response.""" 
+        _logger.exception("Exception during JSON request handling.")
+        self._failed = exception # prevent tx commit            
+        error = {
+                'code': 200,
+                'message': "OpenERP Server Error",
+                'data': serialize_exception(exception)
+        }
+        if isinstance(exception, AuthenticationError):
+            error['code'] = 100
+            error['message'] = "OpenERP Session Invalid"
+        return self._json_response(error=error)
+
+    def dispatch(self):
+        """ Calls the method asked for by the JSON-RPC2 or JSONP request
+        """
+        if self.jsonp_handler:
+            return self.jsonp_handler()
+        try:
+            result = self._call_function(**self.params)
+            return self._json_response(result)
+        except Exception, e:
+            return self._handle_exception(e)
 
 def serialize_exception(e):
     tmp = {
@@ -554,7 +559,7 @@ class HttpRequest(WebRequest):
                 response.set_cookie(k, v)
         return response
 
-    def render(self, template, qcontext=None, **kw):
+    def render(self, template, qcontext=None, lazy=True, **kw):
         """ Lazy render of QWeb template.
 
         The actual rendering of the given template will occur at then end of
@@ -563,8 +568,12 @@ class HttpRequest(WebRequest):
 
         :param basestring template: template to render
         :param dict qcontext: Rendering context to use
+        :param dict lazy: Lazy rendering is processed later in wsgi response layer (default True)
         """
-        return Response(template=template, qcontext=qcontext, **kw)
+        response = Response(template=template, qcontext=qcontext, **kw)
+        if not lazy:
+            return response.render()
+        return response
 
     def not_found(self, description=None):
         """ Helper for 404 response, return its result from the method
@@ -1076,22 +1085,25 @@ class Root(object):
         path = openerp.tools.config.session_dir
         _logger.debug('HTTP sessions stored in: %s', path)
         self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession)
+        self._loaded = False
 
-        # TODO should we move this to ir.http so that only configured modules are served ?
-        _logger.info("HTTP Configuring static files")
-        self.load_addons()
-
+    @lazy_property
+    def nodb_routing_map(self):
         _logger.info("Generating nondb routing")
-        self.nodb_routing_map = routing_map([''] + openerp.conf.server_wide_modules, True)
+        return routing_map([''] + openerp.conf.server_wide_modules, True)
 
     def __call__(self, environ, start_response):
         """ Handle a WSGI request
         """
+        if not self._loaded:
+            self._loaded = True
+            self.load_addons()
         return self.dispatch(environ, start_response)
 
     def load_addons(self):
-        """ Load all addons from addons patch containg static files and
+        """ Load all addons from addons path containing static files and
         controllers and configure them.  """
+        # TODO should we move this to ir.http so that only configured modules are served ?
         statics = {}
 
         for addons_path in openerp.modules.module.ad_paths:
@@ -1105,12 +1117,16 @@ class Root(object):
                         _logger.debug("Loading %s", module)
                         if 'openerp.addons' in sys.modules:
                             m = __import__('openerp.addons.' + module)
+                        else:
+                            m = None
                         addons_module[module] = m
                         addons_manifest[module] = manifest
                         statics['/%s/static' % module] = path_static
 
-        app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, statics)
-        self.dispatch = DisableCacheMiddleware(app)
+        if statics:
+            _logger.info("HTTP Configuring static files")
+            app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, statics)
+            self.dispatch = DisableCacheMiddleware(app)
 
     def setup_session(self, httprequest):
         # recover or create session
@@ -1283,16 +1299,8 @@ class CommonController(Controller):
         """ Method used by client APIs to contact OpenERP. """
         return dispatch_rpc(service, method, args)
 
-    @route('/gen_session_id', type='json', auth="none")
-    def gen_session_id(self):
-        nsession = root.session_store.new()
-        return nsession.sid
-
-root = None
-
-def wsgi_postload():
-    global root
-    root = Root()
-    openerp.service.wsgi_server.register_wsgi_handler(root)
+# register main wsgi handler
+root = Root()
+openerp.service.wsgi_server.register_wsgi_handler(root)
 
 # vim:et:ts=4:sw=4: