Request routing flow - openwrt/luci GitHub Wiki
In general the request routing flow is:
- uhttpd (or another web server) invokes
/www/cgi-bin/luci
, passes request info via env vars & stdin (basic CGI interface) /www/cgi-bin/luci
instantiates theluci.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 itsrun()
method. There might be others but not relevant for the basic logic flowluci.sgi.cgi
builds an HTTP request object from the provided information, loadsluci.dispatcher
and invokesluci.dispatcher.httpdispatch()
, passing the HTTP request context object as argumentluci.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 inluci.dispatcher.dispatch()
luci.dispatcher.dispatch()
does several things:
- it parses all
/usr/share/luci/menu.d/
JSON files, assembles them into a global tree structure (refmenu_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
- checks uci and fs dependencies for all nodes (ref
- 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" }'
, refcheck_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 theauth: { }
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
- if the node or any of its ancestors specified
- if the session can be found (
- if a session ID can be determined using one of the specified methods then...
- 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 hadcors: 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 theL.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
andrewrite
)
- executes a JavaScript view (type
- depending on the action (ref: Description of the JSON in menu.d and acl.d? - #4 by jow ) it either...
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 theL.fs.read()
JS API which in turn invokesubus call file read { "path": "..." }
via the HTTP-UBUS interface (not strictly part of LuCI) towardsrpcd
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