首页 > 其他分享 >uhttp luci simple-app

uhttp luci simple-app

时间:2023-04-19 18:02:23浏览次数:51  
标签:uhttp HTTP 1.1 simple app send 192.168 env

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 after option lua_prefix (/app in our example)
  • env.REQUEST_METHOD contains the HTTP method used for requesting, e.g. GETPOSTPUT 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\nsend("X-Foobar: 1\r\n\r\n") or send("\r\n")
  • to quickly serialize JSON, you can use LuCI's jsonc library binding 42require "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

相关文章

  • kubectl apply -f --record 是否将当前创建对象创建命令保存到Annotation注解中中。
    kubectlcreate-ftomcat-app1.yaml--save-config--recordkubectlapply-ftomcat-app1.yaml--record#推荐命令 --record  #是否将当前对象创建命令保存至Annotation中,布尔型数据(true或false) --save-config  #是否将当前对象配置信息保存至......
  • 顶象uni-app版设备指纹上线,满足企业多平台服务需求
    某旅游公司,在短视频平台上发布了自己的小程序服务。用户观看精彩旅游直播或视频时,可以转到视频平台的小程序上查阅详细路线套餐。该旅游公司想详细了解来访者的更多情况,以便于进行深度营销,为用户提供个性化服务。经过多番比较,他们选用了顶象uni-app版设备指纹。顶象uni-app版设......
  • HappyNewYear
    title:新年快乐cover:https://s4.ax1x.com/2022/01/30/H9zRET.pngdate:2022-01-3017:22:43tags:-Newscategories:-拜年copyright:truelanguage:zh-CN新年快乐Hello,这里是我的个人博客,祝大家在新的一年里:身体健康,万事如意。......
  • 可以提升效率的待办清单APP
    办事效率高的人,都有什么样的共同特征呢?很多人都发现,他们办事都很有条理,知道自己每个时间段应该完成的事情有哪些。而我们每天要处理的工作任务、生活事项也是非常多的,如何提高办事效率,让自己在有限的时间内完成更多的事情呢?今天我们要为大家介绍的就是一款提升效率必备的待办清单......
  • @SpringBootApplication等四个爆红
    在黑马的上面学习,按步骤做,出现爆红问题,之后尝试过很多方法,后发现没导包。导包后可以 ......
  • MIT6.5830-2022 Lab 1: SimpleDB
    SimpleDB组成:classes:表示fields,tuples,tupleschemas。classes:作用于tuples的谓词和条件类。methods:在硬盘上存储关系(如heapfiles),处理并发控制和事务。classes:处理tuples的操作类(select,join,insert,delete等)。bufferpool:在内存中缓存活跃的......
  • uniapp兼容微信小程序和支付宝小程序遇见的坑
    1、获取当前帐号信息getAccountInfoSync兼容;my.getOpenUserInfo 无效的授权关系微信小程序:wx.getAccountInfoSync()支付宝小程序:<buttonclass="popup-btn"@click="openAuth"type="primary"size="mini">获取</button>my.getOpenUserI......
  • 在App开发中如何实现灰度发布?
    ​灰度发布是指将新版本应用程序推送给一部分用户进行测试和反馈的过程,而小程序容器技术则是将小程序运行在应用程序内部的技术,可以更快速、更安全、更好地发布和优化小程序。在本文中,我们将探讨在App开发中如何实现灰度发布,以及如何利用小程序容器技术进行更加快速、安全、优化......
  • MAUI Blazor 实战:开发界面跟随系统主题切换的App
    ​1、安装MasaBlazor参考:MASABlazorhttps://blazor.masastack.com/getting-started/installation2、编写代码新建Service目录,并添加ThemeService.cs该 RequestedTheme 属性返回 AppTheme 枚举成员。 AppTheme 枚举定义下列成员:Unspecified,指示设备使用的是未指......
  • 【uniapp】【外包杯】学习笔记day06 | 微信小程序导航栏的制作并推送的到码云【黑】
    先创建分支 格式化快捷键shift+alt+f ......