Odoo source code learning

HTTP

The Root in the http class in Odoo is the entry main program of the wsgi application.

The entry is as follows: wsgi_server is called as follows:

  def application(environ, start_response):
    if config['proxy_mode'] and '_X_FORWARDED_HOST' in environ:
        return werkzeug.contrib.fixers.ProxyFix(application_unproxied)(environ, start_response)
    else:
        return application_unproxied(environ, start_response)

  def application_unproxied(environ, start_response):
      ......
        wsgi_handlers = [wsgi_xmlrpc]
        wsgi_handlers += module_handlers   # module_handlers 处理器注册表,在http.py注册了root处理器。
        for handler in wsgi_handlers:
            result = handler(environ, start_response)
            if result is None:
                continue
            return result

The registered root handler is a singleton object, and the module import is a singleton. The handler is a callable object, and module_handlers maintains such a list.

  # register main wsgi handler
  root = Root()  # 这是一个可调用对象。
  openerp.service.wsgi_server.register_wsgi_handler(root) 
    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)

In the source code, the dispath method is further wrapped. Werkzeug is a WSGI toolkit, environ contains all the information, and werkzeug.wrappers.Request further encapsulates this environment. In Odoo, this native werkzeug.wrappers.Request object is further encapsulated, and you can view the self.get_request method.

  • dispatch

      def dispatch(self, environ, start_response):
    """
    Performs the actual WSGI dispatching for the application.
    """
    try:
        httprequest = werkzeug.wrappers.Request(environ)
        httprequest.app = self
    
        explicit_session = self.setup_session(httprequest)
        self.setup_db(httprequest)
        self.setup_lang(httprequest)
    
        request = self.get_request(httprequest)
    
        def _dispatch_nodb():
            try:
                func, arguments = self.nodb_routing_map.bind_to_environ(request.httprequest.environ).match()
            except werkzeug.exceptions.HTTPException, e:
                return request._handle_exception(e)
            request.set_handler(func, arguments, "none")
            result = request.dispatch()
            return result
    
        with request:
            db = request.session.db
            if db:
                openerp.modules.registry.RegistryManager.check_registry_signaling(db)
                try:
                    with openerp.tools.mute_logger('openerp.sql_db'):
                        ir_http = request.registry['ir.http']
                except (AttributeError, psycopg2.OperationalError):
                    # psycopg2 error or attribute error while constructing
                    # the registry. That means the database probably does
                    # not exists anymore or the code doesnt match the db.
                    # Log the user out and fall back to nodb
                    request.session.logout()
                    result = _dispatch_nodb()
                else:
                    result = ir_http._dispatch()
                    openerp.modules.registry.RegistryManager.signal_caches_change(db)
            else:
                result = _dispatch_nodb()
    
            response = self.get_response(httprequest, result, explicit_session)
        return response(environ, start_response)

    The above encapsulated request is a context manager, that is, the __enter__ and __exit__ methods are defined. In the WebRequest parent class, you can see the leopard, as follows:

    def __enter__(self):
        _request_stack.push(self)  # 压入请求栈中
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        _request_stack.pop() # 弹出请求栈

        if self._cr:
            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

_request_stack is a werkzeug.local.LocalStack() object. LocalStack uses the stack structure implemented by Local (similar to threading.local), which can push and pop objects, or quickly get to the top of the stack. All modifications are made in this thread ( Visible within the green thread with the greenlet's ID). _request_stack is an instance of a callable object, _request_stack(), which can quickly get the top element of the stack, request=_request_stack, thus accessing the imported request object, which is always the top element of the stack, that is, the current request object.

The following is the real schedule, but nothing can be seen, Command+left click to enter.

result = ir_http._dispatch()
  def _find_handler(self, return_rule=False):
    return self.routing_map().bind_to_environ(request.httprequest.environ).match(return_rule=return_rule)

The instance of ir_http holds the route map of all installed modules, route_map. _find_handler will find the corresponding processing rules in this routing map according to the request (Odoo stack top request request).httprequest(werkzerg request object).environ information.

    def _dispatch(self):
        # locate the controller method
        try:
            rule, arguments = self._find_handler(return_rule=True)
            func = rule.endpoint
        except werkzeug.exceptions.NotFound, e:
            return self._handle_exception(e)

        # check authentication level
        try:
            auth_method = self._authenticate(func.routing["auth"])
        except Exception as e:
            return self._handle_exception(e)

        processing = self._postprocess_args(arguments, rule)
        if processing:
            return processing


        # set and execute handler
        try:
            request.set_handler(func, arguments, auth_method)
            result = request.dispatch()
            if isinstance(result, Exception):
                raise result
        except Exception, e:
            return self._handle_exception(e)

        return result
  • get_request speculates and further encapsulates it. According to the classification, it is packaged into JsonRequest and HttpRequest objects, which are all subclasses of Odoo WebRequest, including uid, environment, user context and other information.

      def get_request(self, httprequest):
    # deduce type of request
    if httprequest.args.get('jsonp'):
        return JsonRequest(httprequest)
    if httprequest.mimetype in ("application/json", "application/json-rpc"):
        return JsonRequest(httprequest)
    else:
        return HttpRequest(httprequest)

    RPC

Odoo WSGI Server should also notice that there is also a wsgi_xmlrpc, that is, in order to be compatible with xml-rpc, the conversion between xml and json, it can be seen in the source code that it is the most primitive wsgi application.

  • wsgi_xmlrpc

    def wsgi_xmlrpc(environ, start_response):
    if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith('/xmlrpc/'):
        length = int(environ['CONTENT_LENGTH'])
        data = environ['wsgi.input'].read(length)
        string_faultcode = True
        if environ['PATH_INFO'].startswith('/xmlrpc/2/'):
            service = environ['PATH_INFO'][len('/xmlrpc/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)
  • xmlrpc_return

      def xmlrpc_return(start_response, service, method, params, string_faultcode=False):
    try:
        result = openerp.http.dispatch_rpc(service, method, params)
        response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False, encoding=None)
    except Exception, 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]

    Common RPC

    Define CommonController under the http.py module, and the corresponding route is /jsonrpc.
    Specifically, it is called through dispath_rpc.
    There are the following four files in the Odoo Service directory, corresponding to the four services, and they all have the dispatch() method to call the methods under their respective modules.
  • common.py
    login、authenticate、version、about、set_loglevel
  • db.py
    create_database、duplicate_database、drop、dump、restore、rename、change_admin_password、migrate_database、db_exist、list、list_lang、list_countries、server_version
  • model.py
    execute、execute_kw、execute_workflow
  • report.py
    report、report_get、render_report

    DataSet RPC

    Routes defined in the web DataSet controller.

/web/dataset/search_read

/web/dataset/load

/web/dataset/call

/web/dataset/call_kw

/web/dataset/call_buttion

/web/dataset/exec_workflow

/web/dataset/resequence

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325378841&siteId=291194637