Skip to content

Commit

Permalink
clean up http Requests and WSGI layer, this allow non json route to h…
Browse files Browse the repository at this point in the history
…andle http requests
  • Loading branch information
antonylesuisse committed Feb 28, 2020
1 parent 743d551 commit 0f0fd69
Show file tree
Hide file tree
Showing 8 changed files with 696 additions and 880 deletions.
22 changes: 0 additions & 22 deletions addons/web/controllers/main.py
Expand Up @@ -223,7 +223,6 @@ def module_boot(db=None):
addons = serverside + dbside
return addons


def fs2web(path):
"""convert FS path into web path"""
return '/'.join(path.split(os.path.sep))
Expand All @@ -249,7 +248,6 @@ def manifest_glob(extension, addons=None, db=None, include_remotes=False):
r.append((path, fs2web(path[len(addons_path):]), addon))
return r


def manifest_list(extension, mods=None, db=None, debug=None):
""" list resources to load specifying either:
mods: a comma separated string listing modules
Expand Down Expand Up @@ -1033,24 +1031,6 @@ def test_mobile_suite(self, mod=None, **kwargs):
def benchmarks(self, mod=None, **kwargs):
return request.render('web.benchmark_suite')


class Proxy(http.Controller):

@http.route('/web/proxy/post/<path:path>', type='http', auth='user', methods=['GET'])
def post(self, path):
"""Effectively execute a POST request that was hooked through user login"""
with request.session.load_request_data() as data:
if not data:
raise werkzeug.exceptions.BadRequest()
from werkzeug.test import Client
from werkzeug.wrappers import BaseResponse
base_url = request.httprequest.base_url
query_string = request.httprequest.query_string
client = Client(request.httprequest.app, BaseResponse)
headers = {'X-Openerp-Session-Id': request.session.sid}
return client.post('/' + path, base_url=base_url, query_string=query_string,
headers=headers, data=data)

class Database(http.Controller):

def _render_template(self, **d):
Expand Down Expand Up @@ -1268,7 +1248,6 @@ def logout(self, redirect='/web'):
request.session.logout(keep_db=True)
return werkzeug.utils.redirect(redirect, 303)


class DataSet(http.Controller):

@http.route('/web/dataset/search_read', type='json', auth="user")
Expand Down Expand Up @@ -1946,7 +1925,6 @@ def from_data(self, fields, rows):

return xlsx_writer.value


class ReportController(http.Controller):

#------------------------------------------------------
Expand Down
100 changes: 85 additions & 15 deletions odoo/addons/base/controllers/rpc.py
@@ -1,47 +1,115 @@
from datetime import date, datetime
from xmlrpc.client import dumps, loads
import xmlrpc.client

from werkzeug.wrappers import Response

from odoo.http import Controller, dispatch_rpc, request, route
from odoo.service import wsgi_server
from odoo.http import Controller, request, route
from odoo.fields import Date, Datetime

#-----------------------------------------------------------
# XML-RPC helpers
#-----------------------------------------------------------

# XML-RPC fault codes. Some care must be taken when changing these: the
# constants are also defined client-side and must remain in sync.
# User code must use the exceptions defined in ``odoo.exceptions`` (not
# create directly ``xmlrpc.client.Fault`` objects).
RPC_FAULT_CODE_CLIENT_ERROR = 1 # indistinguishable from app. error.
RPC_FAULT_CODE_APPLICATION_ERROR = 1
RPC_FAULT_CODE_WARNING = 2
RPC_FAULT_CODE_ACCESS_DENIED = 3
RPC_FAULT_CODE_ACCESS_ERROR = 4

def xmlrpc_handle_exception_int(e):
if isinstance(e, odoo.exceptions.UserError):
fault = xmlrpc.client.Fault(RPC_FAULT_CODE_WARNING, odoo.tools.ustr(e.name))
elif isinstance(e, odoo.exceptions.RedirectWarning):
fault = xmlrpc.client.Fault(RPC_FAULT_CODE_WARNING, str(e))
elif isinstance(e, odoo.exceptions.MissingError):
fault = xmlrpc.client.Fault(RPC_FAULT_CODE_WARNING, str(e))
elif isinstance (e, odoo.exceptions.AccessError):
fault = xmlrpc.client.Fault(RPC_FAULT_CODE_ACCESS_ERROR, str(e))
elif isinstance(e, odoo.exceptions.AccessDenied):
fault = xmlrpc.client.Fault(RPC_FAULT_CODE_ACCESS_DENIED, str(e))
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 = xmlrpc.client.Fault(RPC_FAULT_CODE_APPLICATION_ERROR, formatted_info)
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 = xmlrpc.client.Fault(RPC_FAULT_CODE_APPLICATION_ERROR, formatted_info)

return xmlrpc.client.dumps(fault, allow_none=None)

def xmlrpc_handle_exception_string(e):
if isinstance(e, odoo.exceptions.UserError):
fault = xmlrpc.client.Fault('warning -- %s\n\n%s' % (e.name, e.value), '')
elif isinstance(e, odoo.exceptions.RedirectWarning):
fault = xmlrpc.client.Fault('warning -- Warning\n\n' + str(e), '')
elif isinstance(e, odoo.exceptions.MissingError):
fault = xmlrpc.client.Fault('warning -- MissingError\n\n' + str(e), '')
elif isinstance(e, odoo.exceptions.AccessError):
fault = xmlrpc.client.Fault('warning -- AccessError\n\n' + str(e), '')
elif isinstance(e, odoo.exceptions.AccessDenied):
fault = xmlrpc.client.Fault('AccessDenied', str(e))
elif isinstance(e, odoo.exceptions.DeferredException):
info = e.traceback
formatted_info = "".join(traceback.format_exception(*info))
fault = xmlrpc.client.Fault(odoo.tools.ustr(e), formatted_info)
#InternalError
else:
info = sys.exc_info()
formatted_info = "".join(traceback.format_exception(*info))
fault = xmlrpc.client.Fault(odoo.tools.exception_to_unicode(e), formatted_info)

return xmlrpc.client.dumps(fault, allow_none=None, encoding=None)

class OdooMarshaller(xmlrpc.client.Marshaller):

"""
XMLRPC Marshaller that converts date(time) objects to strings in iso8061 format.
"""

dispatch = dict(xmlrpc.client.Marshaller.dispatch)

# By default, in xmlrpc, bytes are converted to xmlrpc.client.Binary object.
# Historically, odoo is sending binary as base64 string.
# In python 3, base64.b64{de,en}code() methods now works on bytes.
# Convert them to str to have a consistent behavior between python 2 and python 3.
# TODO? Create a `/xmlrpc/3` route prefix that respect the standard and uses xmlrpc.client.Binary.
def dump_bytes(marshaller, value, write):
marshaller.dump_unicode(odoo.tools.ustr(value), write)
dispatch[bytes] = dump_bytes

# convert datetime objects to strings in iso8061 format.
def dump_datetime(self, value, write):
# override to marshall as a string for backwards compatibility
value = Datetime.to_string(value)
self.dump_unicode(value, write)
dispatch[datetime] = dump_datetime

# convert date objects to strings in iso8061 format.
def dump_date(self, value, write):
value = Date.to_string(value)
self.dump_unicode(value, write)
dispatch[date] = dump_date


# monkey-patch xmlrpc.client's marshaller
xmlrpc.client.Marshaller = OdooMarshaller


#-----------------------------------------------------------
# RPC Controller
#-----------------------------------------------------------
class RPC(Controller):
"""Handle RPC connections."""

def _xmlrpc(self, service):
"""Common method to handle an XML-RPC request."""
data = request.httprequest.get_data()
params, method = loads(data)
result = dispatch_rpc(service, method, params)
return dumps((result,), methodresponse=1, allow_none=False)
params, method = xmlrpc.client.loads(data)
result = request.rpc_dispatch(service, method, params)
return xmlrpc.client.dumps((result,), methodresponse=1, allow_none=False)

@route("/xmlrpc/<service>", auth="none", methods=["POST"], csrf=False, save_session=False)
def xmlrpc_1(self, service):
Expand All @@ -53,7 +121,7 @@ def xmlrpc_1(self, service):
try:
response = self._xmlrpc(service)
except Exception as error:
response = wsgi_server.xmlrpc_handle_exception_string(error)
response = xmlrpc_handle_exception_string(error)
return Response(response=response, mimetype='text/xml')

@route("/xmlrpc/2/<service>", auth="none", methods=["POST"], csrf=False, save_session=False)
Expand All @@ -62,10 +130,12 @@ def xmlrpc_2(self, service):
try:
response = self._xmlrpc(service)
except Exception as error:
response = wsgi_server.xmlrpc_handle_exception_int(error)
response = xmlrpc_handle_exception_int(error)
return Response(response=response, mimetype='text/xml')

@route('/jsonrpc', type='json', auth="none", save_session=False)
def jsonrpc(self, service, method, args):
""" Method used by client APIs to contact OpenERP. """
return dispatch_rpc(service, method, args)
return request.rpc_dispatch(service, method, args)

#

0 comments on commit 0f0fd69

Please sign in to comment.