In a traditional CGI application you do need to take care of parsing parameters and POST bodies yourself. URL query parameters are passed via the QUERY_STRING
variable (os.getenv("QUERY_STRING")
) while POST bodies in either application/x-www-form-urlencoded
or multipart/form-data
format are fed to the invoked program via stdin.
This has nothing to do with uhttpd or lighttpd but with the basic operation principle of plain CGI. Usually you have HTTP parsing support directly built into the language (e.g. with PHP) or it is available as separate library (e.g. CGI.pm
for Perl).
The simplest uhttpd embedded Lua application which does not built upon LuCI but uses LuCI's HTTP abstraction library is this:
root@OpenWrt:~# cat /root/simple-app.lua
require "luci.http"
function handle_request(env)
local renv = {
CONTENT_LENGTH = env.CONTENT_LENGTH,
CONTENT_TYPE = env.CONTENT_TYPE,
REQUEST_METHOD = env.REQUEST_METHOD,
REQUEST_URI = env.REQUEST_URI,
PATH_INFO = env.PATH_INFO,
SCRIPT_NAME = env.SCRIPT_NAME:gsub("/+$", ""),
SCRIPT_FILENAME = env.SCRIPT_NAME,
SERVER_PROTOCOL = env.SERVER_PROTOCOL,
QUERY_STRING = env.QUERY_STRING
}
local k, v
for k, v in pairs(env.headers) do
k = k:upper():gsub("%-", "_")
renv["HTTP_" .. k] = v
end
local len = tonumber(env.CONTENT_LENGTH) or 0
local function recv()
if len > 0 then
local rlen, rbuf = uhttpd.recv(4096)
if rlen >= 0 then
len = len - rlen
return rbuf
end
end
return nil
end
local send = uhttpd.send
local req = luci.http.Request(renv, recv, function(s) io.stderr:write(s) end)
send("Status: 200 OK\r\n")
send("Content-Type: text/html\r\n\r\n")
send("<h1>Headers</h1>\n")
for k, v in pairs(env.headers) do
send(string.format("<strong>%s</strong>: %s<br>\n", k, v))
end
send("<h1>Environment</h1>\n")
for k, v in pairs(env) do
if type(v) == "string" then
send(string.format("<code>%s=%s</code><br>\n", k, v))
end
end
send("<h1>Parameters</h1>\n")
for k, v in pairs(req:formvalue()) do -- invoking :formvalue() without name will return a table of all args
send(string.format("<strong>%s</strong>: %s<br>\n", k, v))
end
end
Register it in uhttpd using
option lua_prefix '/app'
option lua_handler '/root/simple-app.lua'
When you invoke uhttpd from the outside, e.g. using curl -F foo=bar -F bar=baz -v http://192.168.1.1/app
, you should see a response like this:
* Hostname was NOT found in DNS cache
* Trying 192.168.1.1...
* Connected to 192.168.1.1 (192.168.1.1) port 80 (#0)
> POST /app/ HTTP/1.1
> User-Agent: curl/7.38.0
> Host: 192.168.1.1
> Accept: */*
> Content-Length: 236
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------0773a465fc34530a
>
< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< Connection: close
< Transfer-Encoding: chunked
< Content-Type: text/html
<
<h1>Headers</h1>
<strong>host</strong>: 192.168.1.1<br>
<strong>expect</strong>: 100-continue<br>
<strong>URL</strong>: /app/<br>
<strong>user-agent</strong>: curl/7.38.0<br>
<strong>content-type</strong>: multipart/form-data; boundary=------------------------0773a465fc34530a<br>
<strong>content-length</strong>: 236<br>
<strong>accept</strong>: */*<br>
<h1>Environment</h1>
<code>SERVER_NAME=192.168.1.1</code><br>
<code>SCRIPT_NAME=/app</code><br>
<code>QUERY_STRING=</code><br>
<code>SERVER_ADDR=192.168.1.1</code><br>
<code>GATEWAY_INTERFACE=CGI/1.1</code><br>
<code>REMOTE_ADDR=192.168.1.7</code><br>
<code>CONTENT_LENGTH=236</code><br>
<code>SERVER_PORT=80</code><br>
<code>SCRIPT_FILENAME=/root/simple-app.lua</code><br>
<code>REQUEST_URI=/app/</code><br>
<code>SERVER_PROTOCOL=HTTP/1.1</code><br>
<code>REMOTE_HOST=192.168.1.7</code><br>
<code>REDIRECT_STATUS=200</code><br>
<code>SERVER_SOFTWARE=uhttpd</code><br>
<code>HTTP_HOST=192.168.1.1</code><br>
<code>REMOTE_PORT=40280</code><br>
<code>HTTP_ACCEPT=*/*</code><br>
<code>PATH_INFO=/</code><br>
<code>HTTP_USER_AGENT=curl/7.38.0</code><br>
<code>CONTENT_TYPE=multipart/form-data; boundary=------------------------0773a465fc34530a</code><br>
<code>REQUEST_METHOD=POST</code><br>
<h1>Parameters</h1>
<strong>bar</strong>: baz<br>
<strong>foo</strong>: bar<br>
* Closing connection 0
jow@jow:~$