httpsrv - HTTP 服务端
作者:拓毅恒
一、概述
HTTPSRV 核心库允许在 LuatOS 平台上创建一个轻量级的 HTTP 服务器,用于处理来自客户端的 HTTP 请求。该模块支持基本的 HTTP 方法(GET、POST、PUT、DELETE 等),并可以处理静态文件和动态请求。
支持网络适配器包括:
- WiFi AP 模式( socket.LWIP_AP ):适用于本地设备通过连接 WiFi 热点访问局域网内的 HTTP 服务器
- 以太网模式( socket.LWIP_ETH ):在支持以太网的设备上使用
- 产品自带的网络协议栈(默认值):如果不指定适配器参数,则使用设备默认的网络协议栈
需要注意的是:HTTP 服务器同一时间最多支持 1 个客户端连接,最多支持同时启动 16 个独立的 HTTP 服务实例
LuatOS 提供的 HTTPSRV 核心库适合用于本地设备调试、简单的 Web 控制界面、数据上报接口等场景,为设备提供便捷的 Web 访问能力。
二、核心示例
1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;
2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;
3、更加完整和详细的 demo,请参考 LuatOS 仓库 中各个产品目录下的相关示例
function http_server_callback(fd, method, uri, headers, body)
log.info("httpsrv", method, uri, json.encode(headers), body)
-- /led是控制灯的API
if uri == "/led/1" then
LEDA(1)
return 200, {}, "ok"
elseif uri == "/led/0" then
LEDA(0)
return 200, {}, "ok"
-- 扫描AP
elseif uri == "/scan/go" then
wlan.scan()
return 200, {}, "ok"
-- 前端获取AP列表
elseif uri == "/scan/list" then
return 200, {["Content-Type"]="applaction/json"}, (json.encode({data=_G.scan_result, ok=true}))
-- 前端填好了ssid和密码, 那就连接吧
elseif uri == "/connect" then
if method == "POST" and body and #body > 2 then
local jdata = json.decode(body)
if jdata and jdata.ssid then
-- 开启一个定时器联网, 否则这个情况可能会联网完成后才执行完
sys.timerStart(wlan.connect, 500, jdata.ssid, jdata.passwd)
return 200, {}, "ok"
end
end
return 400, {}, "ok"
-- 根据ip地址来判断是否已经连接成功
elseif uri == "/connok" then
return 200, {["Content-Type"]="applaction/json"}, json.encode({ip=socket.localIP()})
end
-- 其他情况就是找不到了
return 404, {}, "Not Found" .. uri
end
httpsrv.start(80, http_server_callback, socket.LWIP_AP)
log.info("web", "pls open url http://192.168.4.1/")
三、常量详解
核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;
当前 httpsrv 核心库暂无专用常量定义,可结合 socket 核心库的常量使用。
四、函数详解
httpsrv.start(port, func, adapter)
功能
启动并监听一个 HTTP 端口,处理来自客户端的 HTTP 请求。
参数
port
参数含义:HTTP服务器监听的端口号;
数据类型:number;
取值范围:1-65535;
是否必选:必须传入此参数;
注意事项:请确保端口未被其他程序占用,最多支持同时启动16个独立的 HTTP 服务实例,但HTTP 服务器同一时间最多支持1个客户端连接;
参数示例:80 表示监听80端口;
func
参数含义:HTTP请求处理回调函数;当收到HTTP请求时会被调用,回调函数接收客户端连接对象、请求方法、请求URI、请求头和请求体等参数,并可以返回响应状态码、响应头和响应体。
数据类型:function;
取值范围:无特别限制;
是否必选:必须传入此参数;
注意事项:回调函数会在每次收到HTTP请求时被调用;回调函数内部无法使用sys.wait(timeout)、sys.waitUntil(msg, timeout)、sys.waitMsg(task_name, msg, timeout)等必须用在task中的函数。
参数示例:如下所示,定义了一个完整的HTTP请求处理回调函数,该函数可以接收HTTP请求并返回响应:
function http_server_callback(client, method, uri, headers, body)
-- client:客户端连接对象,userdata类型,一般无需直接操作
-- method:HTTP请求方法,string类型,如"GET"、"POST"、"PUT"、"DELETE"、"HEAD"等
-- uri:请求URI,string类型,如"/api/data"、"/index.html"等
-- headers:请求头,table类型,以键值对形式存储,如headers["content-type"]
-- body:请求体,string类型,如JSON字符串、表单数据等
log.info("HTTP请求", method, uri)
-- 返回HTTP状态码、响应头和响应体
return 200, {["Content-Type"] = "application/json"}, '"status":"success"'
end
adapter
参数含义:网络适配器编号;
数据类型:number;
取值范围:根据具体产品不同,支持的网络适配器编号不同;
是否必选:可选传入此参数;如果没有传入此参数或者传入了nil类型,则使用默认值,即产品自带的网络协议栈;
注意事项:可用于指定使用WiFi、以太网等不同网络接口,详情见httpsrv核心库中常量;
参数示例:socket.LWIP_AP 表示使用AP模式下的LWIP协议栈;
返回值
local result = httpsrv.start(port, func, adapter)
result
含义说明:HTTP服务器启动是否成功;
数据类型:boolean;
取值范围:true 表示成功,false 表示失败;
注意事项:暂无;
返回示例:true 表示服务器启动成功;
回调函数参数说明
当收到 HTTP 请求时,传入的回调函数会被调用,并接收以下参数:
client
参数含义:客户端连接对象;
数据类型:userdata;
注意事项:一般情况下无需直接操作此对象;
method
参数含义:HTTP请求方法;
数据类型:string;
取值范围:"GET"、"POST"、"PUT"、"DELETE"等;
uri
参数含义:HTTP请求的统一资源标识符;
数据类型:string;
取值范围:无特别限制;
示例:"/"、"/api/data"、"/index.html";
headers
参数含义:HTTP请求头信息;
数据类型:table;
取值范围:无特别限制;
示例:{["Content-Type"] = "application/json", ["User-Agent"] = "Mozilla/5.0"}
body
参数含义:HTTP请求体内容;
数据类型:string;
取值范围:无特别限制;
注意事项:对于GET请求,此参数通常为空字符串;
回调函数返回值说明
回调函数可以返回三个值,用于构造 HTTP 响应:
- HTTP 状态码:number 类型,如 200 表示成功,404 表示未找到资源等;
- 响应头:table 类型,包含 HTTP 响应头信息;
- 响应体:string 类型,包含 HTTP 响应体内容;
如果回调函数没有返回值,则默认返回 404, {}, ""。
静态文件处理说明
HTTP 服务端模块支持自动处理静态文件请求:
- 当请求 URI 为 "/" 时,会自动映射为 /index.html,只要 /luadb/index.html 存在,浏览器输入服务器地址(如 http://192.168.4.1)即可直接打开首页。
- 如果需要映射到其他目录,若要访问其他页面,请在 URI 中给出完整的文件名,例如 http://192.168.4.1/explorer.html,服务器会查找 /luadb/explorer.html,如果找不到,会再尝试 /luadb/explorer.html.gz,存在时自动以 gzip 编码返回。
- 下载设备内的任意文件(如 123.txt)时,请使用单层路径,例如 http://192.168.4.1/123.txt,服务器仅查找 /luadb/123.txt 及 /luadb/123.txt.gz;
- 把文件放在“子目录”形式的 URI(如 /explorer.html/123.txt)不会触发 gzip 重试规则,且要求 /luadb/explorer.html/123.txt 必须真实存在。
- 当前默认查找 "/luadb/xxx" 下的文件,随后找 "/" 下的文件,此路径暂不可配置
示例
-- 基本用法:启动HTTP服务器监听80端口
httpsrv.start(80, function(client, method, uri, headers, body)
log.info("httpsrv", method, uri)
return 200, {["Content-Type"] = "text/html"}, "<h1>Hello, World!</h1>"
end)
-- 指定网络适配器的用法
httpsrv.start(8080, function(client, method, uri, headers, body)
return 200, {}, "Response from specific adapter"
end, socket.LWIP_AP)
httpsrv.stop(port, no_used, adapter)
功能
停止指定端口上的 HTTP 服务。
参数
port
参数含义:要停止的HTTP服务器监听的端口号;
数据类型:number;
取值范围:1-65535;
是否必选:必须传入此参数;
注意事项:必须与之前调用httpsrv.start时使用的端口号一致;
参数示例:80 表示停止监听80端口的HTTP服务器;
no_used
参数含义:占位参数,暂无实际用途;
数据类型:nil;
取值范围:固定为nil;
是否必选:必须传入此参数;
注意事项:调用时请固定写nil;
参数示例:nil;
adapter
参数含义:网络适配器编号;
数据类型:number;
取值范围:根据具体产品不同,支持的网络适配器编号不同;
是否必选:必须传入此参数;
注意事项:必须与之前调用httpsrv.start时使用的网络适配器一致;
参数示例:socket.LWIP_AP 表示停止使用AP模式下LWIP协议栈的HTTP服务器;
返回值
local result = httpsrv.stop(port, no_used, adapter)
result
含义说明:HTTP服务器停止是否成功;
数据类型:boolean;
取值范围:true 表示成功,false 表示失败;
注意事项:暂无;
返回示例:true 表示服务器停止成功;
示例
-- 停止监听80端口的HTTP服务器
local ret = httpsrv.stop(80, nil, socket.LWIP_AP)
log.info("httpsrv", "服务器停止结果", ret)
五、注意事项
- 资源占用:HTTP 服务器会占用一定的系统资源,请根据设备实际情况合理使用;
- 并发处理:当前版本的 HTTP 服务端模块对并发请求的处理能力有限,建议在低并发场景下使用;
- 安全考虑:该模块不提供复杂的安全功能(如 HTTPS、身份验证等),在生产环境中使用时请注意安全防护;
- 静态文件路径:当前静态文件默认查找路径为"/luadb/",随后找 "/" 下的文件,此路径暂不可配置;
- 端口占用:请确保使用的端口未被其他程序占用,避免端口冲突;
- 网络适配器:在使用多网络接口的设备上,请确保正确指定网络适配器参数;
六、产品支持说明
支持 LuatOS 开发的所有产品都支持 httpsrv 核心库。