跳转至

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 响应:

  1. HTTP 状态码:number 类型,如 200 表示成功,404 表示未找到资源等;
  2. 响应头:table 类型,包含 HTTP 响应头信息;
  3. 响应体: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)

五、注意事项

  1. 资源占用:HTTP 服务器会占用一定的系统资源,请根据设备实际情况合理使用;
  2. 并发处理:当前版本的 HTTP 服务端模块对并发请求的处理能力有限,建议在低并发场景下使用;
  3. 安全考虑:该模块不提供复杂的安全功能(如 HTTPS、身份验证等),在生产环境中使用时请注意安全防护;
  4. 静态文件路径:当前静态文件默认查找路径为"/luadb/",随后找 "/" 下的文件,此路径暂不可配置;
  5. 端口占用:请确保使用的端口未被其他程序占用,避免端口冲突;
  6. 网络适配器:在使用多网络接口的设备上,请确保正确指定网络适配器参数;

六、产品支持说明

支持 LuatOS 开发的所有产品都支持 httpsrv 核心库。