uhttp luci simple-app
see https://forum.openwrt.org/t/rest-api-supported-in-openwrt/17210/6
You probably do want Luci, or at least to take a look at how Luci does it.
POST data is in the body of the request. Luci creates a ltn12 compatible source to read it and passes it to the http.Request constructor (same with CGI).
The Request class calls protocol.parse_message_body which does most of the work. It stores the results in the params
field of the Request. You can then access them with the familiar formvalue method (source - you can see that the first call calls _parse_input
that we saw earlier).
gives some clues and I spent some time dissecting how Luci is doing it.
Supposedly, you should create a "request", and use that to parse the parameters.
local req = luci.http.Request(
renv, recv, luci.ltn12.sink.file(io.stderr)
)
and then access the parsed parameters as
req.formvalue("email")
This method is working for parameters passed in a GET (i.e. http://192.168.1.70/lua/?email=blah 51)
But not working when passed in a POST (seems req is empty).
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:~$
Given these basic building blocks you can now start doing RESTy stuff:
env.PATH_INFO
refers to the relative path portion afteroption lua_prefix
(/app
in our example)env.REQUEST_METHOD
contains the HTTP method used for requesting, e.g.GET
,POST
,PUT
etc.- to set the response HTTP status, use the
Status
pseudo header:send("Status: 503 Server Error\r\n\r\n")
- to set the response content type, send a
Content-Type
header:send("Content-Type: application/json; charset=UTF-8\r\n")
- to terminate the header block and start with content, send a sole
\r\n
or end the last header with\r\n\r\n
:send("X-Foobar: 1\r\n\r\n")
orsend("\r\n")
- to quickly serialize JSON, you can use LuCI's jsonc library binding 42:
require "luci.jsonc"; send(luci.jsonc.stringify({ some = { complex = { data = "structure", foo = true, bar = { 1, 2, 3 } } } }))
Btw, Lua has some interesting string quoting possibilities which help to unclutter your code. You can wrap your strings in [[
and ]]
which will act like double quotes. Using these, your example above would become:
uhttpd.send("Status: 200 OK\r\n")
uhttpd.send("Content-Type: text/html\r\n\r\n")
uhttpd.send([[
<!DOCTYPE html>
<html>
<body>
<form action="/lua/" method="post">
<input type="email" name="email" placeholder="[email protected]" />
<input type="submit" value="submit" />
</form>
</body>
</html>
]])
At its core, HTTP headers and POST content come in separately into Lua. The HTTP Headers are loaded as environment variables and the POST content is sent in as if by interactive shell into the Lua script after it has run.
Therefore, you can grab an HTTP header such as HTTP_USER_AGENT
using os.getenv()
or nixio.getenv()
on OpenWRT.
A Lua CGI script that reads HTTP headers and prints POST content looks like this:
require "nixio"
-- prepare the browser for content:
print("\r")
-- print http headers
http_headers = nixio.getenv()
for k,v in pairs(http_headers) do
print(k, v)
end
-- print POST output
print(io.read("*all"))
The resulting output will look something like this:
HTTP_ACCEPT text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
SCRIPT_NAME /cgi-bin/test.htm
QUERY_STRING parm1=val&parm2=val2
HTTP_ACCEPT_ENCODING gzip, deflate
SERVER_ADDR 192.168.1.1
GATEWAY_INTERFACE CGI/1.1
HTTP_AUTHORIZATION
CONTENT_LENGTH 23
SERVER_PORT 80
SCRIPT_FILENAME /www/cgi-bin/test.htm
REQUEST_URI /cgi-bin/test.htm?parm1=val&parm2=val2
...
DOCUMENT_ROOT /www
CONTENT_TYPE application/x-www-form-urlencoded
HTTP_CONNECTION keep-alive
HTTP_USER_AGENT Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36
HTTP_ACCEPT_LANGUAGE en-us
REQUEST_METHOD POST
test=value&test1=value1
It is useful to note that uhttpd will output HTTP headers to the browser on behalf of the CGI script, including a Content-Type that matches the file extension of the CGI script. Therefore a .json
will have a Content-Type: application/json
header and a .html
file will have a Content-Type: text/html
header.
=========== End
标签:uhttp,HTTP,1.1,simple,app,send,192.168,env From: https://www.cnblogs.com/lsgxeva/p/17334178.html