| <!DOCTYPE html SYSTEM "about:legacy-compat"> | |
| <html lang="en"><head><META http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
| <meta content="width=device-width, initial-scale=1" name="viewport"> | |
| <!-- | |
| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
| This file is generated from xml source: DO NOT EDIT | |
| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
| --> | |
| <title>Creating hooks and scripts with mod_lua - Apache HTTP Server Version 2.5</title> | |
| <link href="../style/css/manual.css" rel="stylesheet" media="all" type="text/css" title="Main stylesheet"> | |
| <link href="../style/css/manual-loose-100pc.css" rel="alternate stylesheet" media="all" type="text/css" title="No Sidebar - Default font size"> | |
| <link href="../style/css/manual-print.css" rel="stylesheet" media="print" type="text/css"><link rel="stylesheet" type="text/css" href="../style/css/prettify.css"> | |
| <script src="../style/scripts/prettify.min.js"> | |
| </script> | |
| <link href="../images/favicon.png" rel="shortcut icon"></head> | |
| <body id="manual-page"><div id="page-header"> | |
| <p class="menu"><a href="../mod/">Modules</a> | <a href="../mod/quickreference.html">Directives</a> | <a href="https://cwiki.apache.org/confluence/display/httpd/FAQ">FAQ</a> | <a href="../glossary.html">Glossary</a> | <a href="../sitemap.html">Sitemap</a> | <a href="https://bz.apache.org/bugzilla/enter_bug.cgi?product=Apache%20httpd-2">Report a bug</a></p> | |
| <p class="apache">Apache HTTP Server Version 2.5</p> | |
| <img alt="" src="../images/feather.png"></div> | |
| <div class="up"><a href="./"><img title="<-" alt="<-" src="../images/left.gif"></a></div> | |
| <div id="path"> | |
| <a href="https://www.apache.org/">Apache</a> > <a href="https://httpd.apache.org/">HTTP Server</a> > <a href="https://httpd.apache.org/docs/">Documentation</a> > <a href="../">Version 2.5</a> > <a href="./">Developer</a></div><div id="page-content"><div id="preamble"><h1>Creating hooks and scripts with mod_lua</h1> | |
| <button aria-label="Toggle language list" class="lang-toggle"><svg xmlns="http://www.w3.org/2000/svg" stroke-width="2" stroke="currentColor" fill="none" viewBox="0 0 24 24" height="16" width="16"><circle r="10" cy="12" cx="12"/><line y2="12" x2="22" y1="12" x1="2"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg></button> | |
| <div class="toplang"> | |
| <p><span>Available Languages: </span><a href="../en/developer/lua.html" title="English"> en </a></p> | |
| </div> | |
| <p>This document expands on the <code class="module"><a href="../mod/mod_lua.html">mod_lua</a></code> documentation and explores | |
| additional ways of using mod_lua for writing hooks and scripts.</p> | |
| </div> | |
| <div id="quickview"><ul id="toc"><li><img alt="" src="../images/down.gif"> <a href="#introduction">Introduction</a></li> | |
| <li><img alt="" src="../images/down.gif"> <a href="#enabling">Optimizing mod_lua for production servers</a></li> | |
| <li><img alt="" src="../images/down.gif"> <a href="#basic_remap">Example 1: A basic remapping module</a></li> | |
| <li><img alt="" src="../images/down.gif"> <a href="#mass_vhost">Example 2: Mass virtual hosting</a></li> | |
| <li><img alt="" src="../images/down.gif"> <a href="#basic_auth">Example 3: A basic authorization hook</a></li> | |
| <li><img alt="" src="../images/down.gif"> <a href="#authz">Example 4: Authorization using LuaAuthzProvider</a></li> | |
| <li><img alt="" src="../images/down.gif"> <a href="#loadbalancing">Example 5: A rudimentary load balancer</a></li> | |
| <li><img alt="" src="../images/down.gif"> <a href="#map_handler">Example 6: Overlays using LuaMapHandler</a></li> | |
| <li><img alt="" src="../images/down.gif"> <a href="#mod_status_lua">Example 6: Basic Lua scripts</a></li> | |
| </ul><h3>See also</h3><ul class="seealso"><li><a href="../mod/mod_lua.html">mod_lua</a></li><li><a href="modguide.html">Developing modules for Apache 2.4</a></li><li><a href="request.html">Request Processing in Apache 2.4</a></li><li><a href="hooks.html">Apache 2.x Hook Functions</a></li></ul></div> | |
| <div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="introduction">Introduction <a title="Permanent link" href="#introduction" class="permalink">¶</a></h2> | |
| <h3 id="what">What is mod_lua</h3> | |
| <p> | |
| Stuff about what <code class="module"><a href="../mod/mod_lua.html">mod_lua</a></code> is goes here. | |
| </p> | |
| <h3 id="contents">What we will be discussing in this document</h3> | |
| <p> | |
| This document will discuss several cases where <code class="module"><a href="../mod/mod_lua.html">mod_lua</a></code> can be used | |
| to either ease up a phase of the request processing or create more transparency in | |
| the logic behind a decision made in a phase. | |
| </p> | |
| <h3 id="prerequisites">Prerequisites</h3> | |
| <p> | |
| First and foremost, you are expected to have a basic knowledge of how the Lua | |
| programming language works. In most cases, we will try to be as pedagogical | |
| as possible and link to documents describing the functions used in the | |
| examples, but there are also many cases where it is necessary to either | |
| just assume that "it works" or do some digging yourself into what the hows | |
| and whys of various function calls. | |
| </p> | |
| </div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="enabling">Optimizing mod_lua for production servers <a title="Permanent link" href="#enabling" class="permalink">¶</a></h2> | |
| <h3>Setting a scope for Lua states</h3> | |
| <p> | |
| Setting the right <code class="directive"><a href="../mod/mod_lua.html#luascope">LuaScope</a></code> setting | |
| for your Lua scripts can be essential to your server's | |
| performance. By default, the scope is set to <code>once</code>, which means | |
| that every call to a Lua script will spawn a new Lua state that handles that | |
| script and is destroyed immediately after. This option keeps the memory | |
| footprint of mod_lua low, but also affects the processing speed of a request. | |
| If you have the memory to spare, you can set the scope to <code>thread</code>, | |
| which will make mod_lua spawn a Lua state that lasts the entirety of a thread's | |
| lifetime, speeding up request processing by 2-3 times. Since mod_lua will create | |
| a state for each script, this may be an expensive move, memory-wise, so to | |
| compromise between speed and memory usage, you can choose the <code>server</code> | |
| option to create a pool of Lua states to be used. Each request for a Lua script or | |
| a hook function will then acquire a state from the pool and release it back when it's | |
| done using it, allowing you to still gain a significant performance increase, while | |
| keeping your memory footprint low. Some examples of possible settings are: | |
| </p> | |
| <pre class="prettyprint lang-config">LuaScope once | |
| LuaScope thread | |
| LuaScope server 5 40</pre> | |
| <p> | |
| As a general rule of thumb: If your server has none to low usage, use <code>once</code> | |
| or <code>request</code>, if your server has low to medium usage, use the <code>server</code> | |
| pool, and if it has high usage, use the <code>thread</code> setting. As your server's | |
| load increases, so will the number of states being actively used, and having your scope | |
| set to <code>once/request/conn</code> will stop being beneficial to your memory footprint. | |
| </p> | |
| <div class="note">The <code>min</code> and <code>max</code> settings for the | |
| <code>server</code> scope denotes the minimum and maximum states to keep in a pool per | |
| server <em>process</em>, so keep this below your <code>ThreadsPerChild</code> limit. | |
| </div> | |
| <h3>Using code caching</h3> | |
| <p> | |
| By default, <code class="module"><a href="../mod/mod_lua.html">mod_lua</a></code> stats each Lua script to determine whether a reload | |
| (and thus, a re-interpretation and re-compilation) of a script is required. This is managed | |
| through the <code class="directive"><a href="../mod/mod_lua.html#luacodecache">LuaCodeCache</a></code> directive. If you are running | |
| your scripts on a production server, and you do not need to update them regularly, it may be | |
| advantageous to set this directive to the <code>forever</code> value, which will cause mod_lua | |
| to skip the stat process and always reuse the compiled byte-code from the first access to the | |
| script, thus speeding up the processing. For Lua hooks, this can prove to increase performance, | |
| while for scripts handled by the <code>lua-script</code> handler, the increase in performance | |
| may be negligible, as files httpd will stat the files regardless. | |
| </p> | |
| <h3>Keeping the scope clean</h3> | |
| <p> | |
| For maximum performance, it is generally recommended that any initialization of libraries, | |
| constants and master tables be kept outside the handle's scope: | |
| </p> | |
| <pre class="prettyprint lang-lua">--[[ This is good practice ]]-- | |
| require "string" | |
| require "someLibrary" | |
| local masterTable = {} | |
| local constant = "Foo bar baz" | |
| function handle(r) | |
| do_stuff() | |
| end</pre> | |
| <pre class="prettyprint lang-lua">--[[ This is bad practice ]]-- | |
| require "string" | |
| function handle(r) | |
| require "someLibrary" | |
| local masterTable = {} | |
| local constant = "Foo bar baz" | |
| do_stuff() | |
| end</pre> | |
| </div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="basic_remap">Example 1: A basic remapping module <a title="Permanent link" href="#basic_remap" class="permalink">¶</a></h2> | |
| <p> | |
| These first examples show how mod_lua can be used to rewrite URIs in the same | |
| way that one could do using <code class="directive"><a href="../mod/mod_alias.html#alias">Alias</a></code> or | |
| <code class="directive"><a href="../mod/mod_rewrite.html#rewriterule">RewriteRule</a></code>, but with more clarity | |
| on how the decision-making takes place, as well as allowing for more complex | |
| decisions than would otherwise be allowed with said directives. | |
| </p> | |
| <pre class="prettyprint lang-config">LuaHookTranslateName /path/too/foo.lua remap</pre> | |
| <pre class="prettyprint lang-lua">--[[ | |
| Simple remap example. | |
| This example will rewrite /foo/test.bar to the physical file | |
| /internal/test, somewhat like how mod_alias works. | |
| ]]-- | |
| function remap(r) | |
| -- Test if the URI matches our criteria | |
| local barFile = r.uri:match("/foo/([a-zA-Z0-9]+)%.bar") | |
| if barFile then | |
| r.filename = "/internal/" .. barFile | |
| end | |
| return apache2.OK | |
| end</pre> | |
| <pre class="prettyprint lang-lua">--[[ | |
| Advanced remap example. | |
| This example will evaluate some conditions, and based on that, | |
| remap a file to one of two destinations, using a rewrite map. | |
| This is similar to mixing AliasMatch and ProxyPass, but | |
| without them clashing in any way. Assuming we are on example.com, then: | |
| http://example.com/photos/test.png will be rewritten as /uploads/www/test.png | |
| http://example.com/ext/foo.html will be proxied to http://www.external.com/foo.html | |
| URIs that do not match, will be served by their respective default handlers | |
| ]]-- | |
| local map = { | |
| photos = { | |
| source = [[^/photos/(.+)\.png$]], | |
| destination = [[/uploads/www/$1.png]], | |
| proxy = false | |
| }, | |
| externals = { | |
| source = [[^/ext/(.*)$]], | |
| destination = [[http://www.external.com/$1]], | |
| proxy = true | |
| } | |
| } | |
| function interpolateString(s,v) | |
| return s:gsub("%$(%d+)", function(a) return v[tonumber(a)] end) | |
| end | |
| function remap(r) | |
| -- browse through the rewrite map | |
| for key, entry in pairs(map) do | |
| -- Match source regex against URI | |
| local match = r:regex(entry.source, r.uri) then | |
| if match and match[0] then | |
| r.filename = interpolateString(entry.destination, match) | |
| -- Is this a proxied remap? | |
| if entry.proxy then | |
| r.handler = "proxy-server" -- tell mod_proxy to handle this | |
| r.proxyreq = apache2.PROXYREQ_REVERSE -- We'll want to do a reverse proxy | |
| r.filename = "proxy:" .. r.filename -- Add the proxy scheme to the destination | |
| end | |
| return apache2.OK | |
| end | |
| end | |
| return apache2.DECLINED | |
| end</pre> | |
| <p> | |
| bla bla | |
| </p> | |
| </div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="mass_vhost">Example 2: Mass virtual hosting <a title="Permanent link" href="#mass_vhost" class="permalink">¶</a></h2> | |
| <p> | |
| As with simple and advanced rewriting, you can use mod_lua for dynamically | |
| assigning a hostname to a specific document root, much like | |
| <code class="module"><a href="../mod/mod_vhost_alias.html">mod_vhost_alias</a></code> does, but with more control over what goes | |
| where. This could be as simple as a table holding the information about which | |
| host goes into which folder, or more advanced, using a database holding the | |
| document roots of each hostname. | |
| </p> | |
| <pre class="prettyprint lang-config">LuaHookTranslateName /path/too/foo.lua mass_vhost</pre> | |
| <pre class="prettyprint lang-lua">--[[ | |
| Simple mass vhost script | |
| This example will check a map for a virtual host and rewrite filename and | |
| document root accordingly. | |
| ]]-- | |
| local vhosts = { | |
| { domain = "example.com", home = "/www/example.com" }, | |
| { domain = "example.org", home = "/nfs/ext1/example.org" } | |
| } | |
| function mass_vhost(r) | |
| -- Match against our hostname | |
| for key, entry in pairs(vhosts) do | |
| -- match against either host or *.host: | |
| if apache2.strcmp_match(r.hostname, entry.domain) or | |
| apache2.strcmp_match(r.hostname, "*." .. entry.domain) then | |
| -- If it matches, rewrite filename and set document root | |
| local filename = r.filename:sub(r.document_root:len()+1) | |
| r.filename = entry.home .. filename | |
| apahce2.set_document_root(entry.home) | |
| return apache2.OK | |
| end | |
| end | |
| return apache2.DECLINED | |
| end</pre> | |
| <pre class="prettyprint lang-lua">--[[ | |
| Advanced mass virtual hosting | |
| This example will query a database for vhost entries and save them for | |
| 60 seconds before checking for updates. For best performance, such scripts | |
| should generally be run with LuaScope set to 'thread' or 'server' | |
| ]]-- | |
| local cached_vhosts = {} | |
| local timeout = 60 | |
| -- Function for querying the database for saved vhost entries | |
| function query_vhosts(r) | |
| local host = r.hostname | |
| if not cached_vhosts[host] or (cached_vhosts[host] and cached_vhosts[host].updated < os.time() - timeout) then | |
| local db,err = ap.dbopen(r,"mod_dbd") | |
| local _host = db:escape(r,host) | |
| local res, err = db:query(r, ("SELECT `destination` FROM `vhosts` WHERE `hostname` = '%s' LIMIT 1"):format(_host) ) | |
| if res and #res == 1 then | |
| cached_vhosts[host] = { updated = os.time(), destination = res[1][1] } | |
| else | |
| cached_vhosts[host] = { updated = os.time(), destination = nil } -- don't re-query whenever there's no result, wait a while. | |
| end | |
| db:close() | |
| end | |
| if cached_vhosts[host] then | |
| return cached_vhosts[host].destination | |
| else | |
| return nil | |
| end | |
| end | |
| function mass_vhost(r) | |
| -- Check whether the hostname is in our database | |
| local destination = query_vhosts(r) | |
| if destination then | |
| -- If found, rewrite and change document root | |
| local filename = r.filename:sub(r.document_root:len()+1) | |
| r.filename = destination .. filename | |
| ap.set_document_root(r,destination) | |
| return apache2.OK | |
| end | |
| return apache2.DECLINED | |
| end</pre> | |
| <p> | |
| </p> | |
| </div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="basic_auth">Example 3: A basic authorization hook <a title="Permanent link" href="#basic_auth" class="permalink">¶</a></h2> | |
| <p> | |
| With the authorization hooks, you can add custom auth phases to your request | |
| processing, allowing you to either add new requirements that were not previously | |
| supported by httpd, or tweaking existing ones to accommodate your needs. | |
| </p> | |
| <pre class="prettyprint lang-config">LuaHookAuthChecker /path/too/foo.lua check_auth</pre> | |
| <pre class="prettyprint lang-lua">--[[ | |
| A simple authentication hook that checks a table containing usernames and | |
| passwords of two accounts. | |
| ]]-- | |
| local accounts = { | |
| bob = 'somePassword', | |
| jane = 'Iloveponies' | |
| } | |
| -- Function for parsing the Authorization header into a username and a password | |
| function parse_auth(str) | |
| local user,pass = nil, nil | |
| if str and str:len() > 0 then | |
| str = apache2.base64_decode(auth):sub(7)); | |
| user, pass = auth:match("([^:]+)%:([^:]+)") | |
| end | |
| return user, pass | |
| end | |
| -- The authentication hook | |
| function check_auth(r) | |
| local user, pass = parse_auth(r.headers_in['Authorization']) | |
| local authenticated = false | |
| if user and pass then | |
| if accounts[user] and accounts[user] == pass then | |
| authenticated = true | |
| r.user = user | |
| end | |
| end | |
| r.headers_out["WWW-Authenticate"] = 'Basic realm="Super secret zone"' | |
| if not authenticated then | |
| return 401 | |
| else | |
| return apache2.OK | |
| end | |
| end</pre> | |
| <pre class="prettyprint lang-lua">--[[ | |
| An advanced authentication checker with a database backend, | |
| caching account entries for 1 minute | |
| ]]-- | |
| local timeout = 60 -- Set account info to be refreshed every minute | |
| local accounts = {} | |
| -- Function for parsing the Authorization header into a username and a password | |
| function parse_auth(str) | |
| local user,pass = nil, nil | |
| if str and str:len() > 0 then | |
| str = apache2.base64_decode(auth):sub(7)); | |
| user, pass = auth:match("([^:]+)%:([^:]+)") | |
| end | |
| return user, pass | |
| end | |
| -- Function for querying the database for the account's password (stored as a salted SHA-1 hash) | |
| function fetch_password(user) | |
| if not accounts[user] or (accounts[user] and accounts[user].updated < os.time() - timeout) then | |
| local db = apache2.dbopen(r, "mod_dbd") | |
| local usr = db:escape(user) | |
| local res, err = db:query( ("SELECT `password` FROM `accounts` WHERE `user` = '%s' LIMIT 1"):format(usr) ) | |
| if res and #res == 1 then | |
| accounts[user] = { updated = os.time(), password = res[1][1] } | |
| else | |
| accounts[user] = nil | |
| end | |
| db:close() | |
| end | |
| if accounts[user] then | |
| return accounts[user].password | |
| else | |
| return nil | |
| end | |
| end | |
| -- The authentication hook | |
| function check_auth(r) | |
| local user, pass = parse_auth(r.headers_in['Authorization']) | |
| local authenticated = false | |
| if user and pass then | |
| pass = apache2.sha1("addSomeSalt" .. pass) | |
| local stored_pass = fetch_password(user) | |
| if stored_pass and pass == stored_pass then | |
| authenticated = true | |
| r.user = user | |
| end | |
| end | |
| r.headers_out["WWW-Authenticate"] = 'Basic realm="Super secret zone"' | |
| if not authenticated then | |
| return 401 | |
| else | |
| return apache2.OK | |
| end | |
| end</pre> | |
| </div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="authz">Example 4: Authorization using LuaAuthzProvider <a title="Permanent link" href="#authz" class="permalink">¶</a></h2> | |
| <p> | |
| If you require even more advanced control over your authorization phases, | |
| you can add custom authz providers to help you manage your server. The | |
| example below shows you how you can split a single htpasswd file into | |
| groups with different permissions: | |
| </p> | |
| <pre class="prettyprint lang-config">LuaAuthzProvider rights /path/to/lua/script.lua rights_handler | |
| <Directory "/www/private"> | |
| Require rights member | |
| </Directory> | |
| <Directory "/www/admin"> | |
| Require rights admin | |
| </Directory></pre> | |
| <pre class="prettyprint lang-lua">--[[ | |
| This script has two user groups; members and admins, and whichever | |
| is referred to by the "Require rights" directive is checked to see | |
| if the authenticated user belongs to this group. | |
| ]]-- | |
| local members = { "rbowen", "humbedooh", "igalic", "covener" } | |
| local admins = { "humbedooh" } | |
| function rights_handler(r, what) | |
| if r.user == nil then | |
| return apache2.AUTHZ_AUTHZ_DENIED_NO_USER | |
| end | |
| if what == "member" then | |
| for k, v in pairs(members) do | |
| if r.user == v then | |
| return apache2.AUTHZ_GRANTED | |
| end | |
| end | |
| elseif what == "admin" then | |
| for k, v in pairs(admins) do | |
| if r.user == v then | |
| return apache2.AUTHZ_GRANTED | |
| end | |
| end | |
| end | |
| return apache2.AUTHZ_DENIED | |
| end</pre> | |
| </div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="loadbalancing">Example 5: A rudimentary load balancer <a title="Permanent link" href="#loadbalancing" class="permalink">¶</a></h2> | |
| <p> | |
| This is an example of how you can create a load balancing mechanism. | |
| In this example, we will be setting/getting the number of requests served | |
| by each backend using IVM variables, and preferring the backend with least | |
| requests served in total: | |
| </p> | |
| <pre class="prettyprint lang-config">LuaHookTranslateName /path/to/script.lua proxy_handler</pre> | |
| <pre class="prettyprint lang-lua">--[[ | |
| This script uses a basic IVM table to determine where to | |
| send the request. | |
| ]]-- | |
| local backends = { | |
| "http://backend1.foo.com/", | |
| "http://backend2.foo.com/", | |
| "http://backend3.foo.com/" | |
| } | |
| function pick_backend(r) | |
| local chosen_backend = 1 -- default to backend1 | |
| local lowest_count = nil | |
| for i = 1, #backends, 1 do -- Loop through all backends | |
| local count = r:ivm_get("proxy_request_count_" .. i) | |
| if not count then -- If this backend hasn't been used at all, prefer it | |
| chosen_backend = i | |
| lowest_count = 0 | |
| break | |
| end | |
| if not lowest_count or lowest_count > count then -- If this backend has had less requests, pick it for now | |
| chosen_backend = i | |
| lowest_count = count | |
| end | |
| end | |
| lowest_count = lowest_count + 1 | |
| r:ivm_set("proxy_request_count_" .. chosen_backend, lowest_count) | |
| return chosen_backend | |
| end | |
| function proxy_handler(r) | |
| local backend = pick_backend(r) -- Pick a backend based on no. of requests served | |
| r.handler = "proxy-server" | |
| r.proxyreq = apache2.PROXYREQ_REVERSE | |
| r.filename = "proxy:" .. backends[backend] .. r.uri | |
| return apache2.DECLINED -- let the proxy handler do this instead | |
| end</pre> | |
| </div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="map_handler">Example 6: Overlays using LuaMapHandler <a title="Permanent link" href="#map_handler" class="permalink">¶</a></h2> | |
| <p> | |
| Coming soon! | |
| </p> | |
| <pre class="prettyprint lang-config">LuaMapHandler ^/portal/([a-z]+)/ /path/to/lua/script.lua handle_$1</pre> | |
| </div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif"></a></div> | |
| <div class="section"> | |
| <h2 id="mod_status_lua">Example 6: Basic Lua scripts <a title="Permanent link" href="#mod_status_lua" class="permalink">¶</a></h2> | |
| <p> | |
| Also coming soon | |
| </p> | |
| </div></div> | |
| <div class="bottomlang"> | |
| <p><span>Available Languages: </span><a href="../en/developer/lua.html" title="English"> en </a></p> | |
| </div><div id="footer"> | |
| <p class="apache">Copyright 2026 The Apache Software Foundation.<br>Licensed under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>.</p> | |
| <p class="menu"><a href="../mod/">Modules</a> | <a href="../mod/quickreference.html">Directives</a> | <a href="https://cwiki.apache.org/confluence/display/httpd/FAQ">FAQ</a> | <a href="../glossary.html">Glossary</a> | <a href="../sitemap.html">Sitemap</a> | <a href="https://bz.apache.org/bugzilla/enter_bug.cgi?product=Apache%20httpd-2">Report a bug</a></p></div><script><!--//--><![CDATA[//><!-- | |
| if (typeof(prettyPrint) !== 'undefined') { | |
| prettyPrint(); | |
| } | |
| var langToggle = document.querySelector('.lang-toggle'); | |
| var topLang = document.querySelector('.toplang'); | |
| if (langToggle && topLang) { | |
| langToggle.addEventListener('click', function() { topLang.classList.toggle('open'); }); | |
| } | |
| var qv = document.getElementById('quickview'); | |
| if (qv) { | |
| document.body.appendChild(qv); | |
| var qvBtn = document.createElement('button'); | |
| qvBtn.className = 'qv-toggle'; | |
| qvBtn.setAttribute('aria-label', 'Toggle page navigation'); | |
| qvBtn.innerHTML = '☰'; | |
| document.body.appendChild(qvBtn); | |
| qvBtn.addEventListener('click', function() { | |
| var isOpen = qv.classList.toggle('open'); | |
| if (isOpen) { | |
| qv.style.top = window.scrollY + 10 + 'px'; | |
| } | |
| }); | |
| window.addEventListener('scroll', function() { qv.classList.remove('open'); }); | |
| } | |
| //--><!]]></script> | |
| </body></html> |