[FIX] P3: WSGI applications must return *bytes*
XMLRPC dumps returns text rather than bytes (for reasons unknown), which does not suit Werkzeug's WSGI server, we need to encode the body. For simplicity and future-proofiness, return Werkzeug responses instead, Werkzeug takes care of encoding text and passing bytes through.
This commit is contained in:
+23
-47
@@ -12,12 +12,15 @@ import sys
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
|
||||
try:
|
||||
from xmlrpc import client as xmlrpclib
|
||||
except ImportError:
|
||||
# pylint: disable=bad-python3-import
|
||||
import xmlrpclib
|
||||
|
||||
import werkzeug.exceptions
|
||||
import werkzeug.wrappers
|
||||
import werkzeug.serving
|
||||
import werkzeug.contrib.fixers
|
||||
|
||||
@@ -36,90 +39,54 @@ RPC_FAULT_CODE_WARNING = 2
|
||||
RPC_FAULT_CODE_ACCESS_DENIED = 3
|
||||
RPC_FAULT_CODE_ACCESS_ERROR = 4
|
||||
|
||||
def xmlrpc_return(start_response, service, method, params, string_faultcode=False):
|
||||
"""
|
||||
Helper to call a service's method with some params, using a wsgi-supplied
|
||||
``start_response`` callback.
|
||||
|
||||
This is the place to look at to see the mapping between core exceptions
|
||||
and XML-RPC fault codes.
|
||||
"""
|
||||
# Map OpenERP core exceptions to XML-RPC fault codes. Specific exceptions
|
||||
# defined in ``odoo.exceptions`` are mapped to specific fault codes;
|
||||
# all the other exceptions are mapped to the generic
|
||||
# RPC_FAULT_CODE_APPLICATION_ERROR value.
|
||||
# This also mimics SimpleXMLRPCDispatcher._marshaled_dispatch() for
|
||||
# exception handling.
|
||||
try:
|
||||
result = odoo.http.dispatch_rpc(service, method, params)
|
||||
response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False, encoding=None)
|
||||
except Exception as e:
|
||||
if string_faultcode:
|
||||
response = xmlrpc_handle_exception_string(e)
|
||||
else:
|
||||
response = xmlrpc_handle_exception_int(e)
|
||||
start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)))])
|
||||
return [response]
|
||||
|
||||
def xmlrpc_handle_exception_int(e):
|
||||
if isinstance(e, odoo.exceptions.UserError):
|
||||
fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, odoo.tools.ustr(e.value))
|
||||
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
|
||||
elif isinstance(e, odoo.exceptions.RedirectWarning):
|
||||
fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, str(e))
|
||||
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
|
||||
elif isinstance(e, odoo.exceptions.MissingError):
|
||||
fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, str(e))
|
||||
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
|
||||
elif isinstance (e, odoo.exceptions.AccessError):
|
||||
fault = xmlrpclib.Fault(RPC_FAULT_CODE_ACCESS_ERROR, str(e))
|
||||
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
|
||||
elif isinstance(e, odoo.exceptions.AccessDenied):
|
||||
fault = xmlrpclib.Fault(RPC_FAULT_CODE_ACCESS_DENIED, str(e))
|
||||
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
|
||||
elif isinstance(e, odoo.exceptions.DeferredException):
|
||||
info = e.traceback
|
||||
# Which one is the best ?
|
||||
formatted_info = "".join(traceback.format_exception(*info))
|
||||
#formatted_info = odoo.tools.exception_to_unicode(e) + '\n' + info
|
||||
fault = xmlrpclib.Fault(RPC_FAULT_CODE_APPLICATION_ERROR, formatted_info)
|
||||
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
|
||||
else:
|
||||
info = sys.exc_info()
|
||||
# Which one is the best ?
|
||||
formatted_info = "".join(traceback.format_exception(*info))
|
||||
#formatted_info = odoo.tools.exception_to_unicode(e) + '\n' + info
|
||||
fault = xmlrpclib.Fault(RPC_FAULT_CODE_APPLICATION_ERROR, formatted_info)
|
||||
response = xmlrpclib.dumps(fault, allow_none=None, encoding=None)
|
||||
return response
|
||||
|
||||
return xmlrpclib.dumps(fault, allow_none=None)
|
||||
|
||||
def xmlrpc_handle_exception_string(e):
|
||||
if isinstance(e, odoo.exceptions.UserError):
|
||||
fault = xmlrpclib.Fault('warning -- %s\n\n%s' % (e.name, e.value), '')
|
||||
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
|
||||
elif isinstance(e, odoo.exceptions.RedirectWarning):
|
||||
fault = xmlrpclib.Fault('warning -- Warning\n\n' + str(e), '')
|
||||
elif isinstance(e, odoo.exceptions.MissingError):
|
||||
fault = xmlrpclib.Fault('warning -- MissingError\n\n' + str(e), '')
|
||||
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
|
||||
elif isinstance(e, odoo.exceptions.AccessError):
|
||||
fault = xmlrpclib.Fault('warning -- AccessError\n\n' + str(e), '')
|
||||
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
|
||||
elif isinstance(e, odoo.exceptions.AccessDenied):
|
||||
fault = xmlrpclib.Fault('AccessDenied', str(e))
|
||||
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
|
||||
elif isinstance(e, odoo.exceptions.DeferredException):
|
||||
info = e.traceback
|
||||
formatted_info = "".join(traceback.format_exception(*info))
|
||||
fault = xmlrpclib.Fault(odoo.tools.ustr(e), formatted_info)
|
||||
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
|
||||
#InternalError
|
||||
else:
|
||||
info = sys.exc_info()
|
||||
formatted_info = "".join(traceback.format_exception(*info))
|
||||
fault = xmlrpclib.Fault(odoo.tools.exception_to_unicode(e), formatted_info)
|
||||
response = xmlrpclib.dumps(fault, allow_none=None, encoding=None)
|
||||
return response
|
||||
|
||||
return xmlrpclib.dumps(fault, allow_none=None, encoding=None)
|
||||
|
||||
def wsgi_xmlrpc(environ, start_response):
|
||||
""" Two routes are available for XML-RPC
|
||||
@@ -136,14 +103,25 @@ def wsgi_xmlrpc(environ, start_response):
|
||||
|
||||
# Distinguish betweed the 2 faultCode modes
|
||||
string_faultcode = True
|
||||
service = environ['PATH_INFO'][len('/xmlrpc/'):]
|
||||
if environ['PATH_INFO'].startswith('/xmlrpc/2/'):
|
||||
service = environ['PATH_INFO'][len('/xmlrpc/2/'):]
|
||||
service = service[len('2/'):]
|
||||
string_faultcode = False
|
||||
else:
|
||||
service = environ['PATH_INFO'][len('/xmlrpc/'):]
|
||||
|
||||
params, method = xmlrpclib.loads(data)
|
||||
return xmlrpc_return(start_response, service, method, params, string_faultcode)
|
||||
try:
|
||||
result = odoo.http.dispatch_rpc(service, method, params)
|
||||
response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False)
|
||||
except Exception as e:
|
||||
if string_faultcode:
|
||||
response = xmlrpc_handle_exception_string(e)
|
||||
else:
|
||||
response = xmlrpc_handle_exception_int(e)
|
||||
|
||||
return werkzeug.wrappers.Response(
|
||||
response=response,
|
||||
mimetype='text/xml',
|
||||
)(environ, start_response)
|
||||
|
||||
def application_unproxied(environ, start_response):
|
||||
""" WSGI entry point."""
|
||||
@@ -168,9 +146,7 @@ def application_unproxied(environ, start_response):
|
||||
return result
|
||||
|
||||
# We never returned from the loop.
|
||||
response = 'No handler found.\n'
|
||||
start_response('404 Not Found', [('Content-Type', 'text/plain'), ('Content-Length', str(len(response)))])
|
||||
return [response]
|
||||
return werkzeug.exceptions.NotFound("No handler found.\n")(environ, start_response)
|
||||
|
||||
def application(environ, start_response):
|
||||
if config['proxy_mode'] and 'HTTP_X_FORWARDED_HOST' in environ:
|
||||
|
||||
Reference in New Issue
Block a user