http2: NGINX模块lua的HTTP/2协议(客户端)实现
安装
如果您尚未设置RPM仓库订阅,请注册。然后您可以继续以下步骤。
CentOS/RHEL 7或Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-http2
CentOS/RHEL 8+、Fedora Linux、Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-http2
要在NGINX中使用此Lua库,请确保已安装nginx-module-lua。
本文档描述了lua-resty-http2 v1.0,于2019年11月20日发布。
local http2 = require "resty.http2"
local host = "127.0.0.1"
local port = 8080
local sock = ngx.socket.tcp()
local ok, err = sock:connect(host, port)
if not ok then
ngx.log(ngx.ERR, "failed to connect ", host, ":", port, ": ", err)
return
end
local headers = {
{ name = ":authority", value = "test.com" },
{ name = ":method", value = "GET" },
{ name = ":path", value = "/index.html" },
{ name = ":scheme", value = "http" },
{ name = "accept-encoding", value = "gzip" },
{ name = "user-agent", value = "example/client" },
}
local on_headers_reach = function(ctx, headers)
-- 处理响应头
end
local on_data_reach = function(ctx, data)
-- 处理响应体
end
local opts = {
ctx = sock,
recv = sock.receive,
send = sock.send,
}
local client, err = http2.new(opts)
if not client then
ngx.log(ngx.ERR, "failed to create HTTP/2 client: ", err)
return
end
local ok, err = client:request(headers, nil, on_headers_reach, on_data_reach)
if not ok then
ngx.log(ngx.ERR, "client:process() failed: ", err)
return
end
sock:close()
作为一个更正式的示例,请阅读util/example.lua。
描述
这个纯Lua库实现了客户端HTTP/2协议,但并未涵盖所有细节,例如,流依赖关系被维护但从未使用。
然而,有一些固有的限制尚未解决。
不能在SSL/TLS握手连接上使用。tcpsock:sslhandshake不支持ALPN或NPN扩展,因此当前只能使用明文连接,该库将通过发送连接前言来启动HTTP/2会话,即字符串:
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
这个库提供了一个应用层协议协商的补丁。如果需要,请使用这个。
只能提交一个HTTP请求。目前实现的API仅支持提交一个HTTP请求。欢迎提交PR以解决此问题。
HTTP/2会话重用。HTTP/2协议被设计为持久的,而Cosocket对象绑定到特定的HTTP请求。在请求结束之前,必须关闭Cosocket对象或将其设置为存活,这种模型与HTTP/2会话的重用相冲突,只有一种变通方法可以解决此问题,详情请参见client:keepalive。
实现的API
resty.http2
要加载此模块,只需执行以下操作:
local http2 = require "resty.http2"
http2.new
语法:local client, err = http2.new(opts)
通过指定选项创建HTTP/2客户端。如果失败,将返回nil和错误消息字符串。
唯一的参数opts是一个Lua表,包含以下字段:
-
recv,用于读取字节的Lua函数; -
send,用于发送字节的Lua函数; -
ctx,一个不透明数据,作为调用者的上下文;
recv和send函数将像这样被调用:
local data, err = recv(ctx, size)
local ok, err = send(ctx, data)
-
preread_size,一个Lua数字,影响对等方的初始发送窗口大小(通过SETTINGS帧进行广告),默认值为65535; -
max_concurrent_stream,一个Lua数字,限制HTTP/2会话中的最大并发流,默认值为128; -
max_frame_size,一个Lua数字,限制对等方可以发送的最大帧大小,默认值为16777215。 -
key,一个Lua字符串,表示调用者希望重用的缓存HTTP/2会话,如果未找到,将创建新的HTTP/2会话。有关更多详细信息,请参见client:keepalive。
client:acknowledge_settings
语法:local ok, err = client:acknowledge_settings()
确认对等方的SETTINGS帧,设置将自动应用。
如果失败,将返回nil和描述错误原因的Lua字符串。
client:request
语法:local ok, err = client:request(headers, body?, on_headers_reach, on_data_reach)
向对等方发送HTTP请求,
如果失败,将返回nil和描述错误原因的Lua字符串。
headers应为一个类数组的Lua表,表示HTTP请求头,每个条目类似于{ name = "header1", value = "value1" }。
值得注意的是,该库不处理HTTP头的语义,因此调用者有责任提供这些,并且调用者应实现任何必要的转换,例如,Host应转换为:authority。此外,以下头将被忽略,因为它们是连接特定的。
ConnectionKeep-AliveProxy-ConnectionUpgradeTransfer-Encoding
body可以是表示HTTP请求体的Lua字符串。它也可以是一个Lua函数以实现流式上传。当body是一个Lua函数时,它将像这样被调用:
local part_data, last, err = body(size)
如果失败,body应提供第三个返回值err以告知该库发生了一些致命错误,然后该方法将立即中止,并将向对等方发送一个带有错误代码INTERNAL_ERROR的GOAWAY帧。
当所有数据生成完毕时,第二个返回值last应提供,并且其值必须为true。
on_headers_reach应为一个Lua函数,作为回调,当完整的HTTP响应头被接收时将被调用,它将像这样被调用:
local abort = on_headers_reach(ctx, headers)
第二个参数headers是一个类哈希的Lua表,表示从对等方接收到的HTTP响应头。
on_headers_reach可以通过返回布尔值abort来决定是否中止HTTP/2会话,如果on_headers_reach返回一个真值,则HTTP/2会话将被中止。
最后一个参数on_data_reach是一个Lua函数,作为回调,当每次接收到响应体时将被调用,它将像这样被调用:
local abort = on_data_reach(ctx, data)
第二个参数data是一个Lua字符串,表示这次接收到的HTTP响应体。
返回值的含义与on_headers_reach相同。
在此方法返回后,HTTP/2会话仍然存活,可以通过调用client:close来决定关闭此会话或继续执行其他操作。
client:send_request
语法:local stream, err = client:send_request(headers, body?)
将头和主体(如果有)发送到对等方。
headers和body的含义与client:request中的相同。
当此方法返回时,将提供相应创建的流对象。
如果失败,将返回nil和描述错误原因的Lua字符串。
client:read_headers
语法:local headers, err = client:read_headers(stream)
从对等方读取响应头,参数stream是由client:send_request创建的。
返回的headers是一个类哈希的Lua表,包含整个HTTP响应头,可能包含一些伪头,例如":status",如果必要,调用者应进行一些转换。
如果失败,将返回nil和描述错误原因的Lua字符串。
client:read_body
语法:local body, err = client:read_body(stream)
从对等方读取一个DATA帧,参数stream是由client:send_request创建的。
返回的数据是一个Lua字符串,表示一段响应体。如果整个主体已读取完,将返回空字符串。
如果失败,将返回nil和描述错误原因的Lua字符串。
client:close
语法:local ok, err = client:close(code)
使用错误代码code关闭当前HTTP/2会话。
请参见resty.http2.error以了解错误代码。
如果失败,将返回nil和描述错误原因的Lua字符串。
client:keepalive
语法:client:keepalive(key)
缓存当前HTTP/2会话以供重用,请注意,格式不正确的HTTP/2会话将永远不会被缓存。HTTP/2会话将与连接分离,准确地说,是当前的Cosocket对象。
分离的HTTP/2会话将保存在一个内部类哈希的Lua表中,唯一的参数key将在调用者希望重用此会话时用于索引此会话。
在将此会话设置为存活后,调用者还应将Cosocket对象设置为保持活动状态。
在HTTP/2会话与底层连接之间存在固有的限制。HTTP/2会话只能在TCP连接中使用,因为它是有状态的,如果调用者将连接存储到缓存多个连接的池中,则绑定关系将丢失,因为将哪个连接分配给Cosocket对象是不确定的,因此也不知道哪个HTTP/2会话应匹配。
除非Cosocket模型可以为底层连接分配标识符,否则没有优雅的方法来解决此问题。现在调用者可以做的是使用单一大小的连接池来绕过此限制,例如:
...
sock:connect(host, port, { pool = "h2" })
...
sock:setkeepalive(75, 1)
client:keepalive("test")
resty.http2.protocol
此模块实现了一些低级协议相关的API。
要加载此模块,只需执行以下操作:
local protocol = require "resty.http2.protocol"
protocol.session
语法:local session, err = protocol.session(recv, send, ctx, preread_size?, max_concurrent_stream?, max_frame_size?)
创建一个新的HTTP/2会话,如果失败,将返回nil和描述错误原因的Lua字符串。
每个参数的含义与http2.new中描述的相同。
在此函数返回之前,将发送初始的SETTINGS帧和WINDOW_UPDATE帧。
session:adjust_window
语法:local ok = session:adjust_window(delta)
调整每个流的发送窗口大小,如果更改后的发送窗口大小超过MAX_WINDOW_SIZE,则流将被重置,在这种情况下,ok将为nil。
session:frame_queue
语法:session:frame_queue(frame)
将frame附加到当前会话的输出队列。
session:flush_queue
语法:local ok, err = session:flush_queue()
打包并刷新排队的帧,如果失败,将返回nil和描述错误原因的Lua字符串。
session:submit_request
语法:local ok, err = session:submit_request(headers, no_body, priority?, pad?)
向当前HTTP/2会话提交HTTP请求,如果失败,将返回nil和描述错误原因的Lua字符串。
每个参数的含义:
-
headers,应为一个类哈希的Lua表,表示HTTP请求头,值得注意的是,该库不处理HTTP头的语义,因此调用者有责任提供这些,并且调用者应转换任何必要的伪头。例如,:authority应传递而不是Host; -
no_body,一个布尔值,指示此请求是否有主体。当为true时,生成的HEADERS帧将包含END_HEADERS标志; -
priority,一个类哈希的Lua表,用于定义自定义流依赖关系: priority.sid表示依赖的流标识符;priority.excl,新流是否成为由priority.sid指示的流的唯一依赖项;-
priority.weight定义新流的权重; -
pad,填充数据。
session:submit_window_update
语法:local ok, err = session:submit_window_update(incr)
为整个HTTP/2会话提交一个增量为incr的WINDOW_UPDATE帧,如果失败,将返回nil和描述错误原因的Lua字符串。
session:recv_frame
语法:local frame, err = session:recv_frame()
接收一个HTTP/2帧,如果失败,将返回nil和描述错误原因的Lua字符串。
相应的操作将自动执行,例如,如果对等方违反HTTP/2协议约定,将发送GOAWAY帧;如果对等方的发送窗口变得太小,将发送WINDOW_UPDATE帧。
session:close
语法:session:close(code?, debug_data?)
生成一个带有错误代码code和调试数据debug_data的GOAWAY帧,默认错误代码为NO_ERROR,调试数据为nil。
请注意,此函数仅将GOAWAY帧排入输出队列,调用者应调用session:flush_queue以真正发送帧。
session:detach
语法:session:detach()
与Cosocket对象分离当前HTTP/2会话。
session:attach
语法:local ok, err = session:attach(recv, send, ctx)
将当前HTTP/2会话与Cosocket对象关联,如果失败,将返回nil和描述错误原因的Lua字符串。
recv、send和ctx的含义与http.new中描述的相同。
resty.http2.stream
此模块实现了一些低级流相关的API。
要加载此模块,只需执行以下操作:
local h2_stream = require "resty.http2.stream"
h2_stream.new
语法:local stream = h2_stream.new(sid, weight, session)
创建一个新的流,标识符为sid,权重为weight,并属于HTTP/2会话。
h2_stream.new_root
语法:local root_stream = h2_stream.new_root(session)
创建根流及其会话。
根流的标识符为0x0,实际上是一个虚拟流,用于操作整个HTTP/2会话。
stream:submit_headers
语法:local ok, err = stream:submit_headers(headers, end_stream, priority?, pad?)
向流提交一些HTTP头。
第一个参数headers应为一个类哈希的Lua表,表示HTTP请求头,值得注意的是,该库不处理HTTP头的语义,因此调用者有责任提供这些,并且调用者应转换任何必要的伪头。例如,:authority应传递而不是Host;
end_stream参数应为布尔值,用于控制HEADERS帧是否应带有END_STREAM标志,基本上如果没有请求体需要发送,调用者可以将其设置为true。
priority应为一个类哈希的Lua表(如果有),用于定义自定义流依赖关系:
* priority.sid表示依赖的流标识符;
* priority.excl,新流是否成为由priority.sid指示的流的唯一依赖项;
* priority.weight定义新流的权重;
最后一个参数pad表示填充数据。
如果失败,将返回nil和描述相应错误的Lua字符串。
stream:submit_data
语法:local ok, err = stream:submit_data(data, pad, last)
向流提交一些请求体,data应为Lua字符串,带有可选的填充数据。
最后一个参数last指示这是否是最后一次提交,如果last为true,则当前DATA帧将附加END_STREAM标志。
如果失败,将返回nil和描述相应错误的Lua字符串。
stream:submit_window_update
语法:local ok, err = session:submit_window_update(incr)
为流提交一个增量为incr的WINDOW_UPDATE帧,如果失败,将返回nil和描述错误原因的Lua字符串。
stream:set_dependency
语法:stream:set_dependency(depend, excl)
将当前流的依赖关系设置为标识符为depend的流。
第二个参数excl指示当前流是否将成为depend的唯一子流。
当depend缺失时,目标流将是根流,excl将被视为false。
stream:rst
语法:stream:rst(code)
生成一个带有错误代码code的RST_STREAM帧。如果code缺失,将选择NO_ERROR代码。
请注意,此方法仅生成RST_STREAM帧,而不是发送它,调用者应通过调用session:flush_queue发送此帧。
resty.http2.frame
此模块实现了一些低级帧相关的API。
要加载此模块,只需执行以下操作:
local h2_frame = require "resty.http2.frame"
h2_frame.header.new
语法:local hd = h2_frame.header.new(length, typ, flags, id)
创建一个帧头,负载长度为length,帧类型为type,并将flags作为帧标志,属于流id。
h2_frame.header.pack
语法:h2_frame.header.pack(hd, dst)
将帧头hd序列化到目标dst。dst必须是一个类数组的Lua表。
h2_frame.header.unpack
语法:h2_frame.header.unpack(src)
从Lua字符串src反序列化帧头,src的长度必须至少为9个字节。
h2_frame.priority.pack
语法:h2_frame.priority.pack(pf, dst)
将PRIORITY帧序列化到目标dst。dst必须是一个类数组的Lua表。
pf必须是一个类哈希的Lua表,包含:
header,帧头;depend,依赖的流标识符;excl,指定当前流在此PRIORITY帧中是否成为由depend标识的流的唯一子流;weight,为当前流分配新的权重weight;
h2_frame.priority.unpack
语法:local ok, err = h2_frame.priority.unpack(pf, src, stream)
从Lua字符串src反序列化PRIORITY帧,src的长度必须至少为pf.header.length中指定的大小。
pf应为一个类哈希的Lua表,已经包含当前PRIORITY帧的头,即pf.header。
最后一个参数stream指定当前PRIORITY帧所属的流。
在此方法内部将自动执行相应的操作,例如构建新的依赖关系。
如果失败,将返回nil和错误代码。
h2_frame.rst_stream.pack
语法:h2_frame.rst_stream.pack(rf, dst)
将RST_STREAM帧序列化到目标dst。dst必须是一个类数组的Lua表。
rf必须是一个类哈希的Lua表,包含:
header,帧头;error_code,错误代码;
h2_frame.rst_stream.unpack
语法:local ok, err = h2_frame.rst_stream.unpack(rf, src, stream)
从Lua字符串src反序列化RST_STREAM帧。src的长度必须至少为rf.header.length中指定的大小。
rf应为一个类哈希的Lua表,已经包含当前RST_STREAM帧的头,即rf.header。
最后一个参数stream指定当前RST_STREAM帧所属的流。
在此方法内部将自动执行相应的操作,例如更改流的状态。
如果失败,将返回nil和错误代码。
h2_frame.rst_stream.new
语法:local rf = h2_frame.rst_stream.new(error_code, sid)
创建一个带有错误代码error_code的RST_STREAM帧,属于流sid。
h2_frame.settings.pack
语法:h2_frame.settings.pack(sf, dst)
将SETTINGS帧序列化到目标dst。dst必须是一个类数组的Lua表。
sf必须是一个类哈希的Lua表,包含:
header,帧头;item,特定设置,应为一个类数组的Lua表,每个元素应为一个类哈希的Lua表:id,设置标识符,可以是:- SETTINGS_ENABLE_PUSH (0x2)
- SETTINGS_MAX_CONCURRENT_STREAMS (0x3)
- SETTINGS_INITIAL_WINDOW_SIZE (0x4)
- SETTINGS_MAX_FRAME_SIZE (0x5)
value,相应的设置值;
h2_frame.settings.unpack
语法:local ok, err = h2_frame.settings.unpack(sf, src, stream)
从Lua字符串src反序列化SETTINGS帧。src的长度必须至少为sf.header.length中指定的大小。
sf应为一个类哈希的Lua表,已经包含当前SETTINGS帧的头,即sf.header。
最后一个参数stream指定当前SETTINGS帧所属的流(必须是根流)。
在此方法内部将自动执行相应的操作,例如更新HTTP/2会话设置值。
如果失败,将返回nil和错误代码。
h2_frame.settings.new
语法:local sf = h2_frame.settings.new(flags, payload)
创建一个带有标志flags和负载项payload的SETTINGS帧。
payload应为一个类数组的Lua表,每个元素应为一个类哈希的Lua表:
* id,设置标识符,可以是:
* SETTINGS_ENABLE_PUSH (0x2)
* SETTINGS_MAX_CONCURRENT_STREAMS (0x3)
* SETTINGS_INITIAL_WINDOW_SIZE (0x4)
* SETTINGS_MAX_FRAME_SIZE (0x5)
* value,相应的设置值;
h2_frame.ping.pack
语法:h2_frame.ping.pack(pf, dst)
将PING帧序列化到目标dst。dst必须是一个类数组的Lua表。
pf必须是一个类哈希的Lua表,包含:
header,帧头;opaque_data_hi,对应PING数据的最高32位值;opaque_data_lo,对应PING数据的最低32位值;
h2_frame.ping.unpack
语法:local ok, err = h2_frame.ping.unpack(pf, src, stream)
从Lua字符串src反序列化PING帧。src的长度必须至少为sf.header.length中指定的大小。
pf应为一个类哈希的Lua表,已经包含当前PING帧的头,即pf.header。
最后一个参数stream指定当前PING帧所属的流(必须是根流)。
如果失败,将返回nil和错误代码。
h2_frame.goaway.pack
语法:h2_frame.goaway.pack(gf, dst)
将GOAWAY帧序列化到目标dst。dst必须是一个类数组的Lua表。
gf必须是一个类哈希的Lua表,包含:
header,帧头;last_stream_id,最后一个对等方初始化的流标识符;error_code,错误代码;debug_data,调试数据;
h2_frame.goaway.unpack
语法:local ok, err = h2_frame.goaway.unpack(gf, src, stream)
从Lua字符串src反序列化GOAWAY帧。src的长度必须至少为gf.header.length中指定的大小。
gf应为一个类哈希的Lua表,已经包含当前GOAWAY帧的头,即gf.header。
最后一个参数stream指定当前GOAWAY帧所属的流(必须是根流)。
如果失败,将返回nil和描述错误原因的Lua字符串。
h2_frame.goaway.new
语法:local gf = h2_frame.goaway.new(last_sid, error_code, debug_data)
创建一个带有最后一个对等方初始化的流标识符last_sid和错误代码error_code的GOAWAY帧。可选地,带有调试数据debug_data。
h2_frame.window_update.pack
语法:h2_frame.window_update.pack(wf, dst)
将WINDOW_UPDATE帧序列化到目标dst。dst必须是一个类数组的Lua表。
wf必须是一个类哈希的Lua表,包含:
header,帧头;window_size_increment,窗口大小增量;
h2_frame.window_update.unpack
语法:local ok, err = h2_frame.window_update.unpack(wf, src, stream)
从Lua字符串src反序列化WINDOW_UPDATE帧。src的长度必须至少为wf.header.length中指定的大小。
wf应为一个类哈希的Lua表,已经包含当前WINDOW_UPDATE帧的头,即wf.header。
最后一个参数stream指定当前WINDOW_UPDATE帧所属的流。
如果失败,将返回nil和错误代码。
h2_frame.window_update.new
语法:local wf = h2_frame.window_update.new(sid, window)
创建一个带有流标识符sid的WINDOW_UPDATE帧,并增大由window指定的窗口大小。
h2_frame.headers.pack
语法:h2_frame.headers.pack(hf, dst)
将HEADERS帧序列化到目标dst。dst必须是一个类数组的Lua表。
hf必须是一个类哈希的Lua表,包含:
header,帧头;pad,填充数据;depend,依赖的流标识符;excl,指定当前HEADERS帧所属的流是否将成为依赖流depend的唯一子流;weight,指定当前HEADERS帧所属流的权重;block_frags,原始HTTP头(经过hpack压缩后);
h2_frame.headers.unpack
语法:local ok,err = h2_frame.headers.unpack(hf, src, stream)
从Lua字符串src反序列化HEADERS帧,src的长度必须至少为hf.header.length中指定的大小。
hf应为一个类哈希的Lua表,已经包含当前HEADERS帧的头,即hf.header。
最后一个参数stream指定当前HEADERS帧所属的流。
将采取相应的操作,例如流状态转换将发生。
如果失败,将返回nil和错误代码。
h2_frame.headers.new
语法:local hf = h2_frame.headers.new(frags, pri?, pad?, end_stream, end_headers, sid)
创建一个带有块片段frags的HEADERS帧。
参数pri可以用于指定流依赖关系,pri应为一个类哈希的Lua表,包含:
sid,依赖的流标识符;excl,流sid是否将成为依赖流的唯一子流;weight,定义当前流(由sid指定)的权重;
pad指定填充数据,这是可选的。
当end_stream为true时,当前HEADERS帧将带有END_STREAM标志,同样,当end_headers为true时,当前HEADERS帧将带有END_HEADERS标志。
需要注意的是,如果当前HEADERS帧不包含整个头,则根据HTTP/2协议必须跟随一个或多个CONTINUATION帧。
h2_frame.continuation.pack
语法:h2_frame.continuation.pack(cf, dst)
将CONTINUATION帧序列化到目标dst。dst必须是一个类数组的Lua表。
cf必须是一个类哈希的Lua表,包含:
header,帧头;block_frags,原始HTTP头(经过hpack压缩后);
h2_frame.continuation.unpack
语法:local ok, err = h2_frame.continuation.unpack(cf, src, stream)
从Lua字符串src反序列化CONTINUATION帧,src的长度必须至少为cf.header.length中指定的大小。
cf应为一个类哈希的Lua表,已经包含当前CONTINUATION帧的头,即cf.header。
最后一个参数stream指定当前CONTINUATION帧所属的流。
将采取相应的操作,例如流状态转换将发生。
如果失败,将返回nil和错误代码。
h2_frame.continuation.new
语法:local cf = h2_frame.continuation.new(frags, end_headers, sid)
创建一个带有块片段frags的CONTINUATION帧。
当end_headers为true时,当前CONTINUATION帧将带有END_HEADERS标志。
需要注意的是,如果当前CONTINUATION帧不包含整个头,则根据HTTP/2协议必须跟随一个或多个CONTINUATION帧。
sid指定当前CONTINUATION帧所属的流。
h2_frame.data.pack
语法:h2_frame.data.pack(df, dst)
将DATA帧序列化到目标dst。dst必须是一个类数组的Lua表。
df必须是一个类哈希的Lua表,包含:
header,帧头;payload,HTTP请求/响应体;
h2_frame.data.unpack
语法:local ok, err = h2_frame.data.unpack(df, src, stream)
从Lua字符串src反序列化DATA帧,src的长度必须至少为df.header.length中指定的大小。
df应为一个类哈希的Lua表,已经包含当前DATA帧的头,即df.header。
最后一个参数stream指定当前DATA帧所属的流。
将采取相应的操作,例如流状态转换将发生。
如果失败,将返回nil和错误代码。
h2_frame.data.new
语法:local df = h2_frame.data.new(payload, pad, last, sid)
创建一个带有负载payload的DATA帧。
pad指定填充数据,这是可选的。
当last为true时,当前DATA帧将带有END_STREAM标志。
sid指定当前DATA帧所属的流。
h2_frame.push_promise.unpack
语法:local df = h2_frame.data.new(payload, pad, last, sid)
当前任何传入的PUSH_PROMISE帧将被拒绝。
此方法始终返回nil和错误PROTOCOL_ERROR。
resty.http2.hpack
此模块实现了一些低级HPACK API。
要加载此模块,只需执行以下操作:
local hpack = require "resty.http2.hpack"
hpack.encode
语法:hpack.encode(src, dst, lower)
将Lua字符串src编码到目标dst,dst必须是一个类数组的Lua表。将首先尝试霍夫曼编码。
lower指定当前编码操作是否不区分大小写,默认值为false。
hpack.indexed
语法:local v = hpack.indexed(index)
返回使用索引头字段表示法后的索引。
hpack.incr_indexed
语法:local v = hpack.indexed(index)
返回使用增量索引的字面头字段后的索引。
hpack.new
语法:local h = hpack.new(size)
创建一个hpack实例,因为HPACK解码是有状态的。
size表示最大hpack表大小,默认值为4096字节。
返回值h表示HPACK实例。h的一个成员很重要,即h.cached,它保存整个头块片段,[h:decode](#hdecode)将分析h.cached中的数据。
目前,h2_frame.headers.unpack和h2_frame.continuation.unpack将把头块片段推送到h.cached,一旦块完成,将执行解码。
h:insert_entry
语法:local ok = h:insert_entry(header_name, header_value)
尝试将名称为header_name和值为header_value的头条目插入HPACK动态表。
如果此条目太大,则插入可能会失败。如果空间不足,将发生必要的条目驱逐。
此方法将返回true,如果插入成功,或者返回false,如果失败。
h:resize
语法:local ok = h:resize(new_size)
将动态表大小调整为new_size,当前new_size不能超过4096,否则调整操作将失败。
当动态表大小缩小时,将根据HPACK的规则驱逐一些条目。
此方法将返回true,如果调整操作成功,或者返回false,如果失败。
h:decode
语法:local ok, err = h:decode(dst)
解码h.cached中的头块片段,解码后的头将保存在dst中,一个类哈希的Lua表。
如果失败,将返回nil和错误代码。
h:get_indexed_header
语法:local entry = h:get_indexed_header(index)
根据索引index返回头条目。
如果索引无效,返回值将为nil,否则,entry将是一个类哈希的Lua表,包含两个项目:
entry.name,头名称;entry.value,头值;
resty.http2.error
此模块实现了一些低级错误相关的API。
定义了许多错误代码,基本上与HTTP/2协议一致:
h2_error.NO_ERRORh2_error.PROTOCOLh2_error.INTERNAL_ERRORh2_error.FLOW_CONTROL_ERRORh2_error.SETTINGS_TIMEOUTh2_error.STREAM_CLOSEDh2_error.FRAME_SIZE_ERRORh2_error.REFUSED_STREAMh2_error.CANCELh2_error.COMPRESSION_ERRORh2_error.CONNECT_ERRORh2_error.ENHANCE_YOUR_CALMh2_error.INADEQUATE_SECURITYh2_error.HTTP_1_1_REQUIRED
还有三个自定义错误代码:
h2_error.STREAM_PROTOCOL_ERROR,流级协议错误;h2_error.STREAM_FLOW_CONTROL_ERROR,流级流量控制错误;h2_error.STREAM_FRAME_SIZE_ERROR,流级帧大小错误;
流级错误不会影响整个连接,但会重置当前流。
要加载此模块,只需执行以下操作:
h2_error.strerror
语法:local msg = h2_error.strerror(code)
返回描述错误代码code的Lua字符串,如果错误代码未知,将返回"unknown error"。
h2_error.is_stream_error
语法:local ok = h2_error.is_stream_error(code)
判断错误代码code是否为流级错误。
另见
- upyun-resty: https://github.com/upyun/upyun-resty
- lua-resty-httpipe: https://github.com/timebug/lua-resty-httpipe
- lua-resty-requests: https://github.com/tokers/lua-resty-requests
GitHub
您可以在nginx-module-http2的GitHub仓库中找到此模块的其他配置提示和文档。