Skip to content

Request routing flow

Yurt Page edited this page Jan 9, 2024 · 1 revision

In general the request routing flow is:

  1. uhttpd (or another web server) invokes /www/cgi-bin/luci, passes request info via env vars & stdin (basic CGI interface)
  2. /www/cgi-bin/luci instantiates the luci.sgi.cgi class (sgi = Server Gateway Interface, a module to translate the server specific env to a common LuCI-internal HTTP request state) and invokes its run() method. There might be others but not relevant for the basic logic flow
  3. luci.sgi.cgi builds an HTTP request object from the provided information, loads luci.dispatcher and invokes luci.dispatcher.httpdispatch() , passing the HTTP request context object as argument
  4. luci.dispatcher.httpdispatch() parses the requested path, determines the preferred browser language and sets them in the global state along with the received HTTP request context, then proceeds in luci.dispatcher.dispatch()
  5. luci.dispatcher.dispatch() does several things:
  • it parses all /usr/share/luci/menu.d/ JSON files, assembles them into a global tree structure (ref menu_json() )
    • checks uci and fs dependencies for all nodes (ref check_depends() )
    • does some cleanup and post-processing
    • then caches the resulting tree structure on disk in /tmp. In case a fresh cache file is found, it is used instead and the JSON menu file processing is skipped
  • it resolves the requested node by looking up the determined path from the HTTP request context in the assembled menu tree structure (ref resolve_page() )
    • if no matching node is found it yields a 404 error
    • if node dependencies are not fulfilled, it yields a 404 error
    • if the node or any of its ancestors require a login (specify a non-empty auth: {} object in their JSON), then...
      • if a session ID can be determined using one of the specified methods then...
        • if the session can be found (ubus call session get '{ "ubus_rpc_session": "the_ID" }', ref check_authentication() ) then continue
        • if the session can not be found or is expired, then...
          • if the node or any of its ancestors specified login: true in the auth: { } object then present an HTML login dialog (by rendering /usr/lib/lua/luci/view/sysauth.htm)
          • if no node along the ancestor chain specifies login: true then yield a 403 error
  • it checks the acl dependencies along the ancestor chain
    • if a node along the chain depends on an ACL which is not granted in the active session, then yield a 403 error
    • if a node along the chain depends on an ACL for which the active session only has read but not write permission, then mark the node as readonly
  • does CORS processing if the HTTP request type is OPTIONS and the page (or one of its ancestory had cors: true set
  • does setuid/setgid dropping if configured in the ancestor chain
  • it evaluates the action: { } object of the resolved node
    • depending on the action (ref: Description of the JSON in menu.d and acl.d? - #4 by jow ) it either...
      • executes a JavaScript view (type view) in /www/luci-static/resources/view/**.js
        • this is the most common and preferred node action
        • it is implemented by rendering a server side /usr/lib/lua/luci/view/view.htm template which in turn instantiates the /www/luci-static/resources/view/**.js JS class from the rendered HTML code using the L.ui.instantiateView() API.
      • calls a class method (type call) - the method is responsible for producing HTTP output, setting status headers etc. If the mode encounters an exception, then yield a 500 error
      • renders a server side Lua template (type template) in /usr/lib/lua/luci/view/**.htm according to the node action specification - this action type is deprecated/being faded out
      • renders an abstract CBI form (type cbi) in /usr/lib/lua/luci/model/cbi/**.lua - this action type is deprecated/being faded out
      • renders a non-UCI CBI form (type form) in /usr/lib/lua/luci/model/cbi/**.lua - this action type is deprecated/being faded out
      • executes a summary or detail sub-action (type arcombine) depending on whether an ID argument is present as last path segment - this action type is deprecated/being faded out
      • redirects or rewrites the request (types alias and rewrite)

A typical JavaScript based view is composed of several requests though:

  • The main request to access a LuCI URL produces a server-side rendered HTML page frame that includes JS code that...
    • Requests the session-specific menu tree as JSON from the server and renders it locally into an HTML menu
    • Requests various static resources such as luci.js, CSS files etc.
    • For the most common view action type, requests and instantiates the actual view .js file which in turn...
      • produces markup and incrementally renders the content area of the server side rendered page frame
      • optionally makes a number of ubus-rpc API requests to fetch more information from the server, e.g. to load uci config values or system runtime information
      • in the specific example of crontab.js, the file contents are fetched via the L.fs.read() JS API which in turn invokes ubus call file read { "path": "..." } via the HTTP-UBUS interface (not strictly part of LuCI) towards rpcd file module

The HTML page frame template is /usr/lib/lua/luci/view/view.htm for JS based views, for other actions (CBI maps, server side Lua templates) it is other *.htm templates including header.htm and footer.htm themselves


The header.htm and footer.htm templates in luci-base are just stubs including the theme specific header.htm and footer.htm

Source https://forum.openwrt.org/t/redesigning-luci-openwrt/133469/6

Clone this wiki locally