merge upstream
bzr revid: chs@openerp.com-20140227165817-wdgdl4rbmtnnqudt
This commit is contained in:
commit
70f3ed09c5
171
openerp/http.py
171
openerp/http.py
|
@ -129,12 +129,10 @@ class WebRequest(object):
|
||||||
self.session_id = httprequest.session.sid
|
self.session_id = httprequest.session.sid
|
||||||
self.disable_db = False
|
self.disable_db = False
|
||||||
self.uid = None
|
self.uid = None
|
||||||
self.func = None
|
self.endpoint = None
|
||||||
self.func_arguments = {}
|
|
||||||
self.auth_method = None
|
self.auth_method = None
|
||||||
self._cr_cm = None
|
self._cr_cm = None
|
||||||
self._cr = None
|
self._cr = None
|
||||||
self.func_request_type = None
|
|
||||||
# set db/uid trackers - they're cleaned up at the WSGI
|
# set db/uid trackers - they're cleaned up at the WSGI
|
||||||
# dispatching phase in openerp.service.wsgi_server.application
|
# dispatching phase in openerp.service.wsgi_server.application
|
||||||
if self.db:
|
if self.db:
|
||||||
|
@ -191,37 +189,36 @@ class WebRequest(object):
|
||||||
self.disable_db = True
|
self.disable_db = True
|
||||||
self.uid = None
|
self.uid = None
|
||||||
|
|
||||||
def set_handler(self, func, arguments, auth):
|
def set_handler(self, endpoint, arguments, auth):
|
||||||
# is this needed ?
|
# is this needed ?
|
||||||
arguments = dict((k, v) for k, v in arguments.iteritems()
|
arguments = dict((k, v) for k, v in arguments.iteritems()
|
||||||
if not k.startswith("_ignored_"))
|
if not k.startswith("_ignored_"))
|
||||||
|
|
||||||
self.func = func
|
endpoint.arguments = arguments
|
||||||
self.func_request_type = func.routing['type']
|
self.endpoint = endpoint
|
||||||
self.func_arguments = arguments
|
|
||||||
self.auth_method = auth
|
self.auth_method = auth
|
||||||
|
|
||||||
def _call_function(self, *args, **kwargs):
|
def _call_function(self, *args, **kwargs):
|
||||||
request = self
|
request = self
|
||||||
if self.func_request_type != self._request_type:
|
if self.endpoint.routing['type'] != self._request_type:
|
||||||
raise Exception("%s, %s: Function declared as capable of handling request of type '%s' but called with a request of type '%s'" \
|
raise Exception("%s, %s: Function declared as capable of handling request of type '%s' but called with a request of type '%s'" \
|
||||||
% (self.func, self.httprequest.path, self.func_request_type, self._request_type))
|
% (self.endpoint.original, self.httprequest.path, self.endpoint.routing['type'], self._request_type))
|
||||||
|
|
||||||
kwargs.update(self.func_arguments)
|
kwargs.update(self.endpoint.arguments)
|
||||||
|
|
||||||
# Backward for 7.0
|
# Backward for 7.0
|
||||||
if getattr(self.func.method, '_first_arg_is_req', False):
|
if self.endpoint.first_arg_is_req:
|
||||||
args = (request,) + args
|
args = (request,) + args
|
||||||
# Correct exception handling and concurency retry
|
# Correct exception handling and concurency retry
|
||||||
@service_model.check
|
@service_model.check
|
||||||
def checked_call(___dbname, *a, **kw):
|
def checked_call(___dbname, *a, **kw):
|
||||||
return self.func(*a, **kw)
|
return self.endpoint(*a, **kw)
|
||||||
|
|
||||||
# FIXME: code and rollback management could be cleaned
|
# FIXME: code and rollback management could be cleaned
|
||||||
try:
|
try:
|
||||||
if self.db:
|
if self.db:
|
||||||
return checked_call(self.db, *args, **kwargs)
|
return checked_call(self.db, *args, **kwargs)
|
||||||
return self.func(*args, **kwargs)
|
return self.endpoint(*args, **kwargs)
|
||||||
except Exception:
|
except Exception:
|
||||||
if self._cr:
|
if self._cr:
|
||||||
self._cr.rollback()
|
self._cr.rollback()
|
||||||
|
@ -265,8 +262,23 @@ def route(route=None, **kw):
|
||||||
else:
|
else:
|
||||||
routes = [route]
|
routes = [route]
|
||||||
routing['routes'] = routes
|
routing['routes'] = routes
|
||||||
f.routing = routing
|
@functools.wraps(f)
|
||||||
return f
|
def response_wrap(*args, **kw):
|
||||||
|
response = f(*args, **kw)
|
||||||
|
if isinstance(response, Response) or f.routing_type == 'json':
|
||||||
|
return response
|
||||||
|
elif isinstance(response, werkzeug.wrappers.BaseResponse):
|
||||||
|
response = Response.force_type(response)
|
||||||
|
response.set_default()
|
||||||
|
return response
|
||||||
|
elif isinstance(response, basestring):
|
||||||
|
return Response(response)
|
||||||
|
else:
|
||||||
|
_logger.warn("<function %s.%s> returns an invalid response type for an http request" % (f.__module__, f.__name__))
|
||||||
|
return response
|
||||||
|
response_wrap.routing = routing
|
||||||
|
response_wrap.original_func = f
|
||||||
|
return response_wrap
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
class JsonRequest(WebRequest):
|
class JsonRequest(WebRequest):
|
||||||
|
@ -379,7 +391,7 @@ class JsonRequest(WebRequest):
|
||||||
mime = 'application/json'
|
mime = 'application/json'
|
||||||
body = simplejson.dumps(response)
|
body = simplejson.dumps(response)
|
||||||
|
|
||||||
r = werkzeug.wrappers.Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))])
|
r = Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))])
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def serialize_exception(e):
|
def serialize_exception(e):
|
||||||
|
@ -437,23 +449,16 @@ class HttpRequest(WebRequest):
|
||||||
self.params = params
|
self.params = params
|
||||||
|
|
||||||
def dispatch(self):
|
def dispatch(self):
|
||||||
# TODO: refactor this correctly. This is a quick fix for pos demo.
|
if request.httprequest.method == 'OPTIONS' and request.endpoint and request.endpoint.routing.get('cors'):
|
||||||
if request.httprequest.method == 'OPTIONS' and request.func and request.func.routing.get('cors'):
|
headers = {
|
||||||
response = werkzeug.wrappers.Response(status=200)
|
'Access-Control-Max-Age': 60 * 60 * 24,
|
||||||
response.headers.set('Access-Control-Allow-Origin', request.func.routing['cors'])
|
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
|
||||||
methods = 'GET, POST'
|
}
|
||||||
if request.func_request_type == 'json':
|
return Response(status=200, headers=headers)
|
||||||
methods = 'POST'
|
|
||||||
elif request.func.routing.get('methods'):
|
|
||||||
methods = ', '.join(request.func.routing['methods'])
|
|
||||||
response.headers.set('Access-Control-Allow-Methods', methods)
|
|
||||||
response.headers.set('Access-Control-Max-Age',60*60*24)
|
|
||||||
response.headers.set('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
|
|
||||||
return response
|
|
||||||
|
|
||||||
r = self._call_function(**self.params)
|
r = self._call_function(**self.params)
|
||||||
if not r:
|
if not r:
|
||||||
r = werkzeug.wrappers.Response(status=204) # no content
|
r = Response(status=204) # no content
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def make_response(self, data, headers=None, cookies=None):
|
def make_response(self, data, headers=None, cookies=None):
|
||||||
|
@ -470,12 +475,24 @@ class HttpRequest(WebRequest):
|
||||||
:type headers: ``[(name, value)]``
|
:type headers: ``[(name, value)]``
|
||||||
:param collections.Mapping cookies: cookies to set on the client
|
:param collections.Mapping cookies: cookies to set on the client
|
||||||
"""
|
"""
|
||||||
response = werkzeug.wrappers.Response(data, headers=headers)
|
response = Response(data, headers=headers)
|
||||||
if cookies:
|
if cookies:
|
||||||
for k, v in cookies.iteritems():
|
for k, v in cookies.iteritems():
|
||||||
response.set_cookie(k, v)
|
response.set_cookie(k, v)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def render(self, template, qcontext=None, **kw):
|
||||||
|
""" Lazy render of QWeb template.
|
||||||
|
|
||||||
|
The actual rendering of the given template will occur at then end of
|
||||||
|
the dispatching. Meanwhile, the template and/or qcontext can be
|
||||||
|
altered or even replaced by a static response.
|
||||||
|
|
||||||
|
:param basestring template: template to render
|
||||||
|
:param dict qcontext: Rendering context to use
|
||||||
|
"""
|
||||||
|
return Response(template=template, qcontext=qcontext, **kw)
|
||||||
|
|
||||||
def not_found(self, description=None):
|
def not_found(self, description=None):
|
||||||
""" Helper for 404 response, return its result from the method
|
""" Helper for 404 response, return its result from the method
|
||||||
"""
|
"""
|
||||||
|
@ -530,7 +547,15 @@ class Controller(object):
|
||||||
class EndPoint(object):
|
class EndPoint(object):
|
||||||
def __init__(self, method, routing):
|
def __init__(self, method, routing):
|
||||||
self.method = method
|
self.method = method
|
||||||
|
self.original = getattr(method, 'original_func', method)
|
||||||
self.routing = routing
|
self.routing = routing
|
||||||
|
self.arguments = {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def first_arg_is_req(self):
|
||||||
|
# Backward for 7.0
|
||||||
|
return getattr(self.method, '_first_arg_is_req', False)
|
||||||
|
|
||||||
def __call__(self, *args, **kw):
|
def __call__(self, *args, **kw):
|
||||||
return self.method(*args, **kw)
|
return self.method(*args, **kw)
|
||||||
|
|
||||||
|
@ -553,9 +578,19 @@ def routing_map(modules, nodb_only, converters=None):
|
||||||
if inspect.ismethod(mv) and hasattr(mv, 'routing'):
|
if inspect.ismethod(mv) and hasattr(mv, 'routing'):
|
||||||
routing = dict(type='http', auth='user', methods=None, routes=None)
|
routing = dict(type='http', auth='user', methods=None, routes=None)
|
||||||
methods_done = list()
|
methods_done = list()
|
||||||
|
routing_type = None
|
||||||
for claz in reversed(mv.im_class.mro()):
|
for claz in reversed(mv.im_class.mro()):
|
||||||
fn = getattr(claz, mv.func_name, None)
|
fn = getattr(claz, mv.func_name, None)
|
||||||
if fn and hasattr(fn, 'routing') and fn not in methods_done:
|
if fn and hasattr(fn, 'routing') and fn not in methods_done:
|
||||||
|
fn_type = fn.routing.get('type')
|
||||||
|
if not routing_type:
|
||||||
|
routing_type = fn_type
|
||||||
|
else:
|
||||||
|
if fn_type and routing_type != fn_type:
|
||||||
|
_logger.warn("Subclass re-defines <function %s.%s> with different type than original."
|
||||||
|
" Will use original type: %r", fn.__module__, fn.__name__, routing_type)
|
||||||
|
fn.routing['type'] = routing_type
|
||||||
|
fn.original_func.routing_type = routing_type
|
||||||
methods_done.append(fn)
|
methods_done.append(fn)
|
||||||
routing.update(fn.routing)
|
routing.update(fn.routing)
|
||||||
if not nodb_only or nodb_only == (routing['auth'] == "none"):
|
if not nodb_only or nodb_only == (routing['auth'] == "none"):
|
||||||
|
@ -892,19 +927,51 @@ mimetypes.add_type('application/font-woff', '.woff')
|
||||||
mimetypes.add_type('application/vnd.ms-fontobject', '.eot')
|
mimetypes.add_type('application/vnd.ms-fontobject', '.eot')
|
||||||
mimetypes.add_type('application/x-font-ttf', '.ttf')
|
mimetypes.add_type('application/x-font-ttf', '.ttf')
|
||||||
|
|
||||||
class LazyResponse(werkzeug.wrappers.Response):
|
class Response(werkzeug.wrappers.Response):
|
||||||
""" Lazy werkzeug response.
|
""" Response object passed through controller route chain.
|
||||||
API not yet frozen"""
|
|
||||||
|
|
||||||
def __init__(self, callback, status_code=None, **kwargs):
|
In addition to the werkzeug.wrappers.Response parameters, this
|
||||||
super(LazyResponse, self).__init__(mimetype='text/html')
|
classe's constructor can take the following additional parameters
|
||||||
if status_code:
|
for QWeb Lazy Rendering.
|
||||||
self.status_code = status_code
|
|
||||||
self.callback = callback
|
:param basestring template: template to render
|
||||||
self.params = kwargs
|
:param dict qcontext: Rendering context to use
|
||||||
def process(self):
|
:param int uid: User id to use for the ir.ui.view render call
|
||||||
response = self.callback(**self.params)
|
"""
|
||||||
self.response.append(response)
|
default_mimetype = 'text/html'
|
||||||
|
def __init__(self, *args, **kw):
|
||||||
|
template = kw.pop('template', None)
|
||||||
|
qcontext = kw.pop('qcontext', None)
|
||||||
|
uid = kw.pop('uid', None)
|
||||||
|
super(Response, self).__init__(*args, **kw)
|
||||||
|
self.set_default(template, qcontext, uid)
|
||||||
|
|
||||||
|
def set_default(self, template=None, qcontext=None, uid=None):
|
||||||
|
self.template = template
|
||||||
|
self.qcontext = qcontext or dict()
|
||||||
|
self.uid = uid
|
||||||
|
# Support for Cross-Origin Resource Sharing
|
||||||
|
if request.endpoint and 'cors' in request.endpoint.routing:
|
||||||
|
self.headers.set('Access-Control-Allow-Origin', request.endpoint.routing['cors'])
|
||||||
|
methods = 'GET, POST'
|
||||||
|
if request.endpoint.routing['type'] == 'json':
|
||||||
|
methods = 'POST'
|
||||||
|
elif request.endpoint.routing.get('methods'):
|
||||||
|
methods = ', '.join(request.endpoint.routing['methods'])
|
||||||
|
self.headers.set('Access-Control-Allow-Methods', methods)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_qweb(self):
|
||||||
|
return self.template is not None
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
view_obj = request.registry["ir.ui.view"]
|
||||||
|
uid = self.uid or request.uid or openerp.SUPERUSER_ID
|
||||||
|
return view_obj.render(request.cr, uid, self.template, self.qcontext, context=request.context)
|
||||||
|
|
||||||
|
def flatten(self):
|
||||||
|
self.response.append(self.render())
|
||||||
|
self.template = None
|
||||||
|
|
||||||
class DisableCacheMiddleware(object):
|
class DisableCacheMiddleware(object):
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
|
@ -1018,9 +1085,9 @@ class Root(object):
|
||||||
return HttpRequest(httprequest)
|
return HttpRequest(httprequest)
|
||||||
|
|
||||||
def get_response(self, httprequest, result, explicit_session):
|
def get_response(self, httprequest, result, explicit_session):
|
||||||
if isinstance(result, LazyResponse):
|
if isinstance(result, Response) and result.is_qweb:
|
||||||
try:
|
try:
|
||||||
result.process()
|
result.flatten()
|
||||||
except(Exception), e:
|
except(Exception), e:
|
||||||
if request.db:
|
if request.db:
|
||||||
result = request.registry['ir.http']._handle_exception(e)
|
result = request.registry['ir.http']._handle_exception(e)
|
||||||
|
@ -1028,7 +1095,7 @@ class Root(object):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if isinstance(result, basestring):
|
if isinstance(result, basestring):
|
||||||
response = werkzeug.wrappers.Response(result, mimetype='text/html')
|
response = Response(result, mimetype='text/html')
|
||||||
else:
|
else:
|
||||||
response = result
|
response = result
|
||||||
|
|
||||||
|
@ -1043,16 +1110,6 @@ class Root(object):
|
||||||
if not explicit_session and hasattr(response, 'set_cookie'):
|
if not explicit_session and hasattr(response, 'set_cookie'):
|
||||||
response.set_cookie('session_id', httprequest.session.sid, max_age=90 * 24 * 60 * 60)
|
response.set_cookie('session_id', httprequest.session.sid, max_age=90 * 24 * 60 * 60)
|
||||||
|
|
||||||
# Support for Cross-Origin Resource Sharing
|
|
||||||
if request.func and 'cors' in request.func.routing:
|
|
||||||
response.headers.set('Access-Control-Allow-Origin', request.func.routing['cors'])
|
|
||||||
methods = 'GET, POST'
|
|
||||||
if request.func_request_type == 'json':
|
|
||||||
methods = 'POST'
|
|
||||||
elif request.func.routing.get('methods'):
|
|
||||||
methods = ', '.join(request.func.routing['methods'])
|
|
||||||
response.headers.set('Access-Control-Allow-Methods', methods)
|
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def dispatch(self, environ, start_response):
|
def dispatch(self, environ, start_response):
|
||||||
|
@ -1111,7 +1168,7 @@ def db_list(force=False, httprequest=None):
|
||||||
|
|
||||||
def db_filter(dbs, httprequest=None):
|
def db_filter(dbs, httprequest=None):
|
||||||
httprequest = httprequest or request.httprequest
|
httprequest = httprequest or request.httprequest
|
||||||
h = httprequest.environ['HTTP_HOST'].split(':')[0]
|
h = httprequest.environ.get('HTTP_HOST', '').split(':')[0]
|
||||||
d = h.split('.')[0]
|
d = h.split('.')[0]
|
||||||
r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d)
|
r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d)
|
||||||
dbs = [i for i in dbs if re.match(r, i)]
|
dbs = [i for i in dbs if re.match(r, i)]
|
||||||
|
|
Loading…
Reference in New Issue