http2: The HTTP/2 Protocol (Client Side) Implementation for nginx-module-lua
Installation
If you haven't set up RPM repository subscription, sign up. Then you can proceed with the following steps.
CentOS/RHEL 7 or 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
To use this Lua library with NGINX, ensure that nginx-module-lua is installed.
This document describes lua-resty-http2 v1.0 released on Nov 20 2019.
lua-resty-http2 - The HTTP/2 Protocol (Client Side) Implementation for OpenResty. Still Pending.
Status
This Lua module is currently considered experimental.
Synopsis
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)
-- Process the response headers
end
local on_data_reach = function(ctx, data)
-- Process the response body
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()
As a more formal exemplify, please read the util/example.lua.
Description
This pure Lua library implements the client side HTTP/2 protocol, but not all details are covered, for example, the stream dependencies is maintained but never used.
There are some inherent limitations which are not solved, however.
Cannot be used over the SSL/TLS handshaked connections. The tcpsock:sslhandshake
doesn't support the ALPN or NPN extensions,
so currently only the plain connections can be used, the library will start
HTTP/2 session with sending the connection preface, i.e. the string:
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
This library provides a patch for the Application-Layer Protocol Negotiation. just uses this if you need.
Only a HTTP request can be submitted. Currently the implemented APIs support for submitting just one HTTP request. PRs are welcome to solve this.
HTTP/2 session reuse. The HTTP/2 protocol is designed as persistent, while the Cosocket object is binded to a specific HTTP request. One has to close the Cosocket object or set it alive before the request is over, this model is conflict with the reuse of HTTP/2 session, just a work-around way can solve this, see client:keepalive for the details.
API Implemented
resty.http2
To load this module, just do this:
local http2 = require "resty.http2"
http2.new
syntax: local client, err = http2.new(opts)
Creates a HTTP/2 client by specifying the options. In case of failure, nil
and a error message string will be returned.
The sole parameter opts
, which is a Lua table, contains some fields:
-
recv
, a Lua function which is used to read bytes; -
send
, a Lua function which is used to send bytes; -
ctx
, an opaque data, acts as the callers' context;
The recv
and send
function will be called like:
local data, err = recv(ctx, size)
local ok, err = send(ctx, data)
-
preread_size
, a Lua number which influences the peer's initial send window size (advertise through the SETTINGS frame), default is65535
; -
max_concurrent_stream
, a Lua number which limits the max concurrent streams in a HTTP/2 session, default is128
; -
max_frame_size
, a Lua number which limits the max frame size that peer can send, default is16777215
. -
key
, a Lua string which represents which cached HTTP/2 session the callers want to resue, if not found, new HTTP/2 session will be created. See client:keepalive for more details.
client:acknowledge_settings
syntax: local ok, err = client:acknowledge_settings()
Acknowledges peer's SETTINGS frame, settings will be applied automatically.
In case of failure, nil
and a Lua string will describes the error reason will be given.
client:request
syntax: local ok, err = client:request(headers, body?, on_headers_reach, on_data_reach)
Sends a HTTP request to peer,
In case of failure, nil
and a Lua string will describes the error reason will be given.
the headers
, should be a array-like Lua table represent the HTTP request headers, each entry is like { name = "header1", value = "value1" }
.
It is worth noting that this library doesn't take care of the HTTP headers' semantics, so it's callers' responsibility to supply this, and callers should implement any necessary conversions, for example, Host
should be converted to :authority
. Also, the following headers will be ignored as they are CONNECTION specific.
Connection
Keep-Alive
Proxy-Connection
Upgrade
Transfer-Encoding
The body
, can be a Lua string represents the HTTP request body. It also can be a Lua function to implement the stream-way uploading. When body
is a Lua function, it will be called like:
local part_data, last, err = body(size)
In case of failure, body
should provide the 3rd return value err
to tell this library that some fatal errors happen, then this method will be aborted immediately, and a GOAWAY frame will be sent to peer with error code INTERNAL_ERROR.
When all data has been generated, the 2nd return value last
should be provided, and it's value must be true
.
on_headers_reach
, should be a Lua function, as a callback which will be called when complete HTTP response headers are received, it will be called like:
local abort = on_headers_reach(ctx, headers)
The 2nd parameter headers
is a hash-like Lua table which represents the HTTP response headers received from peer.
on_headers_reach
can decide whether aborts the HTTP/2 session by returning a boolean value abort
to the library, the HTTP/2 session will be aborted if on_headers_reach
returns a true value.
The last parameter, on_data_reach
, is a Lua function, acts as the callback which will be called when response body are received every time, it will be called like:
local abort = on_data_reach(ctx, data)
The 2nd parameter data
is a Lua string represents the HTTP respose body received this time.
The meaning of return value is same as the on_headers_reach
.
After this method returns, the HTTP/2 session is still alive, one can decide to close this session by calling client:close or going ahead to do something.
client:send_request
syntax: local stream, err = client:send_request(headers, body?)
Sends the headers and body (if any) to peer.
meanings of headers
and body
are same as the one in client:request.
The corresponding created stream object will be given when this method returns.
In case of failure, nil
and a Lua string which describes the error reason will be given.
client:read_headers
syntax: local headers, err = client:read_headers(stream)
Reads the response headers from peer, the parameter stream
is the one created by client:send_request.
The return headers is a hash-like Lua table which contains the whole HTTP response headers, which may contains some pesudo-headers like ":status"
, callers should do some transforms if necessary.
In case of failure, nil
and a Lua string which describes the error reason will be given.
client:read_body
syntax: local body, err = client:read_body(stream)
Reads a DATA frame from peer, the parameter stream
is the one created by client:send_request.
The return data is a Lua string which represents a piece of response body. Empty string will be given if the whole body were read done.
In case of failure, nil
and a Lua string which describes the error reason will be given.
client:close
syntax: local ok, err = client:close(code)
Closes the current HTTP/2 session with the error code code
.
See resty.http2.error to learn the error codes.
In case of failure, nil
and a Lua string which describes the error reason will be given.
client:keepalive
syntax: client:keepalive(key)
Caches current HTTP/2 session for the reuse, note malformed HTTP/2 session will never be cached. The HTTP/2 session will detached from the connection, precisely, the current Cosocket object.
The detached HTTP/2 session will be saved in an internal hash-like Lua table, the unique parameter key
will be used to index this session when callers want to reuse it.
After set this session as alive, callers should also set the Cosocket object as keepalive.
There is an inherent limitation between the mapping of HTTP/2 session and the underlying connection. A HTTP/2 session can only be used in a TCP connection becasue it is stateful, if callers store the connection to a pool which caches multiple connections, the binding relations is lost, since which connection is picked to the Cosocket object is not sure, thereby which HTTP/2 session shall be matched is also unknown.
This is no elegant way to solve this, unless the Cosocket model can assign an identifier to the underlying connection. Now what callers can do is use the single size connection pool to bypass this limitation, for example:
...
sock:connect(host, port, { pool = "h2" })
...
sock:setkeepalive(75, 1)
client:keepalive("test")
resty.http2.protocol
This module implements some low-level protocool-relevant APIs.
To load this module, just do this:
local protocol = require "resty.http2.protocol"
protocol.session
syntax: local session, err = protocol.session(recv, send, ctx, preread_size?, max_concurrent_stream?, max_frame_size?)
Creates a new HTTP/2 session, in case of failure, nil
and a Lua string which
describes the error reason will be given.
The meaning of every parameter is same as these described in http2.new.
The initial SETTINGS frame and WINDOW_UPDATE frame will be sent before this function returns.
session:adjust_window
syntax: local ok = session:adjust_window(delta)
Adjusts each streams send window size, stream will be reset if the altered send window size exceeds MAX_WINDOW_SIZE, in this case, ok
will be nil
.
session:frame_queue
syntax: session:frame_queue(frame)
Appends frame
to current session's output queue.
session:flush_queue
syntax: local ok, err = session:flush_queue()
Packs and flushes the queueing frames, in case of failure, nil
and a Lua
string which described the error reason will be given.
session:submit_request
syntax: local ok, err = session:submit_request(headers, no_body, priority?, pad?)
Submits a HTTP request to the current HTTP/2 session, in case of failure, nil
and a Lua string which described the error reason wil be given.
Meaning of each parameter:
-
headers
, should be a hash-like Lua table represent the HTTP request headers, it is worth noting that this library doesn't take care of the HTTP headers' semantics, so it's callers' responsibility to supply this, and callers should transform any necessary pesudo headers. For example,:authority
should be passed ratherHost
; -
no_body
, a boolean value, indicates whether this request has body. When it is true, the generated HEADERS frame will contains the END_HEADERS flag; -
priority
, a hash-like Lua table, which is used to define a custom stream dependencies: priority.sid
represents the dependent stream identifier;priority.excl
, whether the new stream becomes the sole dependency of the stream indicated bypriority.sid
;-
priority.weight
defines weight of new stream; -
pad
, the padding data.
session:submit_window_update
syntax: local ok, err = session:submit_window_update(incr)
Submits a WINDOW_UPDATE frame for the whole HTTP/2 session with an increment incr
, in case of failure, nil
and a Lua string which describes the error reason will be given.
session:recv_frame
syntax: local frame, err = session:recv_frame()
Receives a HTTP/2 frame, in case of failure, nil
and a Lua string which describes the error reason will be given.
The corresponding action will be taken automatically, for example, GOAWAY frame will be sent if peer violates the HTTP/2 protocol conventions; WINDOW_UPDATE frame will be sent if peer's send window becomes too small.
session:close
syntax: session:close(code?, debug_data?)
Generates a GOAWAY frame with the error code code
and debug data debug_data
, the default error code is NO_ERROR and the debug_data is nil
.
Note this function just queues the GOAWAY frame to the output queue, callers
should call session:flush_queue
to really send the frames.
session:detach
syntax: session:detach()
Detachs the current HTTP/2 session with the Cosocket object.
session:attach
syntax: local ok, err = session:attach(recv, send, ctx)
Attachs the current HTTP/2 session with a Cosocket object, in case of failure, nil
and a Lua string which describes the error reason will be given.
The meanings of recv
, send
and ctx
are same as these described in http.new.
resty.http2.stream
This module implements some low-level stream-relevant APIs.
To load this module, just do this:
local h2_stream = require "resty.http2.stream"
h2_stream.new
syntax: local stream = h2_stream.new(sid, weight, session)
Creates a new stream with the identifier sid
, weight weight
and the HTTP/2 session which it belongs.
h2_stream.new_root
syntax: local root_stream = h2_stream.new_root(session)
Creates the root stream with it's session.
The root stream's identifier is 0x0
and is really a virtual stream which is used to manipulate the whole HTTP/2 session.
stream:submit_headers
syntax: local ok, err = stream:submit_headers(headers, end_stream, priority?, pad?)
Submits some HTTP headers to the stream.
The first parameter headers
, should be a hash-like Lua table represent the HTTP request headers, it is worth noting that this library doesn't take care of the HTTP headers' semantics, so it's callers' responsibility to supply this, and callers should transform any necessary pesudo headers. For example, :authority
should be passed rather Host
;
The end_stream
parameter should be a boolean value and is used to control whether the HEADERS frame should take the END_STREAM flag, basically callers can set it true if there is no request body need to send.
priority
should be a hash-like Lua table (if any), which is used to define a custom stream dependencies:
* priority.sid
represents the dependent stream identifier;
* priority.excl
, whether the new stream becomes the sole dependency of the
stream indicated by priority.sid
;
* priority.weight
defines weight of new stream;
The last parameter pad
, represents the padding data.
In case of failure, nil
and a Lua string which describes the corresponding error will be given.
stream:submit_data
syntax: local ok, err = stream:submit_data(data, pad, last)
Submits some request body to the stream, data
should be a Lua string, with optional padding data.
The last parameter last
is indicated whether this is the last submittion, the current DATA frame will attach the END_STREAM flag if last
is true.
In case of failure, nil
and a Lua string which describes the corresponding error will be given.
stream:submit_window_update
syntax: local ok, err = session:submit_window_update(incr)
Submits a WINDOW_UPDATE frame for the stream with an increment incr
, in case of failure, nil
and a Lua string which describes the error reason will be given.
stream:set_dependency
syntax: stream:set_dependency(depend, excl)
Sets current stream's dependencies to a stream with the identifier depend
.
The second parameter excl
, indicates whether current stream will be the sole child of depend
.
When depend
is absent, the target stream will be the root and excl
will be treat as false
.
stream:rst
syntax: stream:rst(code)
Generates a RST_STREAM frame with the error code code
. In the case of code
is absent, the NO_ERROR code will be selected.
Note this method just generates a RST_STREAM frame rather than send it, caller should send this frame by calling session:flush_queue.
resty.http2.frame
This module implements some low-level frame-relevant APIs.
To load this module, just do this:
local h2_frame = require "resty.http2.frame"
h2_frame.header.new
syntax: local hd = h2_frame.header.new(length, typ, flags, id)
Creates a frame header, with the payload length length
, frame type type
and
takes flags
as the frame flags, which belongs to the stream id
.
h2_frame.header.pack
syntax: h2_frame.header.pack(hd, dst)
Serializes the frame header hd
to the destination dst
. The dst
must be a
array-like Lua table.
h2_frame.header.unpack
syntax: h2_frame.header.unpack(src)
Deserializes a frame header from a Lua string src
, the length of src
must be at least 9 octets.
h2_frame.priority.pack
syntax: h2_frame.priority.pack(pf, dst)
Serializes a PRIORITY frame to the destination dst
. The dst
must be a array-like Lua table.
The pf
must be a hash-like Lua table which contians:
header
, the frame header;depend
, the dependent stream identifierexcl
, specifies whether the current stream where this PRIORITY frame stays becomes the sole child of the stream identified bydepend
;weight
, assigns a new weightweight
to current stream;
h2_frame.priority.unpack
syntax: local ok, err = h2_frame.priority.unpack(pf, src, stream)
Deserializes a PRIORITY frame from a Lua string src
, the length of src
must
be at least the size specified in the pf.header.length
.
The pf
should be a hash-like Lua table which already contains the current
PRIORITY frame's header, i.e. pf.header
.
The last parameter stream
specifies the stream that current PRIORITY frame
belongs.
Corresponding actions will be taken automatically inside this method like building the new dependencies.
In case of failure, nil
and an error code will be given.
h2_frame.rst_stream.pack
syntax: h2_frame.rst_stream.pack(rf, dst)
Serializes a RST_STREAM frame to the destination dst
. The dst
must be a array-like Lua table.
The rf
must be a hash-like Lua table which contains:
header
, the frame header;error_code
, the error code;
h2_frame.rst_stream.unpack
syntax: h2_frame.rst_stream.unpack(rf, src, stream)
Deserializes a RST_STREAM frame from a Lua string src
. The length of src
must be at least the size specified in the rf.header.length
.
The rf
should be a hash-like Lua table which already contains the current
RST_STREAM frame's header, i.e. rf.header
.
The last parameter stream
specifies the stream that current RST_STREAM frame
belongs.
Corresponding actions will be taken automatically inside this method like changing the stream's state.
In case of failure, nil
and an error code will be given.
h2_frame.rst_stream.new
syntax: local rf = h2_frame.rst_stream.new(error_code, sid)
Creates a RST_STREAM frame with the error code error_code
, which belongs to
the stream sid
.
h2_frame.settings.pack
syntax: h2_frame.settings.pack(sf, dst)
Serializes a SETTINGS frame to the destination dst
. The dst
must be a array-like Lua table.
The sf
must be a hash-like Lua table which contains:
header
, the frame header;item
, the specific settings, which should be a array-like Lua table, each element should be a hash-like Lua table:id
, the setting identifier, can be:- SETTINGS_ENABLE_PUSH (0x2)
- SETTINGS_MAX_CONCURRENT_STREAMS (0x3)
- SETTINGS_INITIAL_WINDOW_SIZE (0x4)
- SETTINGS_MAX_FRAME_SIZE (0x5)
value
, the corresponding setting value;
h2_frame.settings.unpack
syntax: local ok, err = h2_frame.settings.unpack(sf, src, stream)
Deserializes a SETTINGS frame from a Lua string src
. The length of src
must be at least the size specified in the sf.header.length
.
The sf
should be a hash-like Lua table which already contains the current SETTINGS frame's header, i.e. sf.header
.
The last parameter stream
specifies the stream that current SETTINGS frame belongs (must be the root stream).
Corresponding actions will be taken automatically inside this method like updating the HTTP/2 session settings value.
In case of failure, nil
and an error code will be given.
h2_frame.settings.new
syntax: local sf = h2_frame.settings.new(flags, payload)
Creates a SETTINGS frame with the flags flags
and payload item payload
.
The payload
should be a array-like Lua table, each element should be a hash-like Lua table:
* id
, the setting identifier, can be:
* SETTINGS_ENABLE_PUSH (0x2)
* SETTINGS_MAX_CONCURRENT_STREAMS (0x3)
* SETTINGS_INITIAL_WINDOW_SIZE (0x4)
* SETTINGS_MAX_FRAME_SIZE (0x5)
* value
, the corresponding setting value;
h2_frame.ping.pack
syntax: h2_frame.ping.pack(pf, dst)
Serializes a PING frame to the destination dst
. The dst
must be a array-like Lua table.
The pf
must be a hash-like Lua table which contains:
header
, the frame header;opaque_data_hi
, highest 32 bits value of the corresponding ping data;opaque_data_lo
, lowest 32 bits value of the corresponding ping data;
h2_frame.ping.unpack
syntax: local ok, err = h2_frame.ping.unpack(pf, src, stream)
Deserializes a PING frame from a Lua string src
. The length of src
must be at least the size specified in the sf.header.length
.
The pf
should be a hash-like Lua table which already contains the current PING frame's header, i.e. pf.header
.
The last parameter stream
specifies the stream that current PING frame belongs (must be the root stream).
In case of failure, nil
and an error code will be given.
h2_frame.goaway.pack
syntax: h2_frame.goaway.pack(gf, dst)
Serializes a GOAWAY frame to the destination dst
. The dst
must be a array-like Lua table.
The gf
must be hash-like Lua table which contains:
header
, the frame header;last_stream_id
, the last peer-initialized stream identifier;error_code
, the error code;debug_data
, the debug data;
h2_frame.goaway.unpack
syntax: local ok, err = h2_frame.goaway.unpack(gf, src, stream)
Deserializes a GOAWAY frame from a Lua string src
. The length of src
must be at least the size specified in the gf.header.length
.
The gf
should be a hash-like Lua table which already contains the current GOAWAY frame's heaer, i.e. gf.header
.
The last parameter stream
specifies the stream that current GOAWAY frame belongs (must be the root stream).
In case of failure, nil
and a Lua string which describes the error reason will be given.
h2_frame.goaway.new
syntax: local gf = h2_frame.goaway.new(last_sid, error_code, debug_data)
Creates a GOAWAY frame with the last peer-initialized stream identifier last_sid
, and error code error_code
. Optionally, with the debug data debug_data
.
h2_frame.window_update.pack
syntax: h2_frame.window_update.pack(wf, dst)
Serializes a WINDOW_UPDATE frame to the destination dst
. The dst
must be a array-like Lua table.
The wf
must be hash-like Lua table which contains:
header
, the frame header;window_size_increment
, the window size increment;
h2_frame.window_update.unpack
syntax: local ok, err = h2_frame.window_update.unpack(wf, src, stream)
Deserializes a WINDOW_UPDATE frame from a Lua string src
. The length of src
must be at least the size specified in the wf.header.length
.
The wf
should be a hash-like Lua table which already contains the current WINDOW_UPDATE frame's heaer, i.e. wf.header
.
The last parameter stream
specifies the stream that current WINDOW_UPDATE frame belongs.
In case of failure, nil
and an error code will be given.
h2_frame.window_update.new
syntax: local wf = h2_frame.window_update.new(sid, window)
Creates a WINDOW_UPDATE frame with the stream identifier sid
, and enlarges the window size specified by window
.
h2_frame.headers.pack
syntax: h2_frame.headers.pack(hf, dst)
Serializes a HEADERS frame to the destination dst
. The dst
must be a array-like Lua table.
The hf
must be hash-like Lua table which contains:
header
, the frame header;pad
, the padding data;depend
, the dependent stream identifier;excl
, specifies whether the stream that current HEADERS frame belongs will become the sole child of the streamdepend
;weight
, specifies the weight of the stream that current HEADERS frame belongs.block_frags
, the plain HTTP headers (after the hpack compressing);
h2_frame.headers.unpack
syntax: local ok,err = h2_frame.headers.unpack(hf, src, stream)
Deserializes a HEADERS frame from the Lua string src
, the length of src
must be at least the size specified in the hf.header.length
The hf
should be a hash-like Lua table which already contains the current HEADERS frame's heaer, i.e. hf.header
.
The last parameter stream
specifies the stream that current HEADER frame belongs.
The corresponding action will be taken, for example, stream state transition will happens.
In case of failure, nil
and an error code will be given.
h2_frame.headers.new
syntax: local hf = h2_frame.headers.new(frags, pri?, pad?, end_stream, end_headers, sid)
Creates a HEADERS frame which takes the block fragments frags
.
The parameter pri
can be taken to specify the stream dependencies, pri
should be a hash-like Lua table, which contains:
sid
, the dependent stream identifier;excl
, whether the streamsid
will be the sole child of dependent stream;weight
, defines the current stream's (specified bysid
) weight ;
The pad
specifies the padding data, which is optional.
When end_stream
is true, current HEADERS frame will takes the END_STREAM flag, likewise, when end_headers
is true, current HEADERS frame will takes the END_HEADERS flag.
One should take care that if current HEADERS frame doesn't contain the whole headers, then one or more CONTINUATION frames must be followed according to the HTTP/2 procotol.
h2_frame.continuation.pack
syntax: h2_frame.continuation.pack(cf, dst)
Serializes a CONTINUATION frame to the destination dst
. The dst
must be a array-like Lua table.
The cf
must be hash-like Lua table which contains:
header
, the frame header;block_frags
, the plain HTTP headers (after the hpack compressing);
h2_frame.continuation.unpack
syntax: local ok, err = h2_frame.continuation.unpack(cf, src, stream)
Deserializes a CONTINUATION frame from the Lua string src
, the length of src
must be at least the size specified in the cf.header.length
The cf
should be a hash-like Lua table which already contains the current CONTINUATION frame's heaer, i.e. cf.header
.
The last parameter stream
specifies the stream that current CONTINUATION frame belongs.
The corresponding action will be taken, for example, stream state transition will happens.
In case of failure, nil
and an error code will be given.
h2_frame.continuation.new
syntax: local cf = h2_frame.continuation.new(frags, end_headers, sid)
Creates a CONTINUATION frame which takes the block fragments frags
.
When end_headers
is true, current CONTINUATION frame will takes the END_HEADERS flag.
One should take care that if current CONTINUATION frame doesn't contain the whole headers, then one or more CONTINUATION frames must be followed according to the HTTP/2 procotol.
The sid
specifies the stream that current CONTINUATION frame belongs.
h2_frame.data.pack
syntax: h2_frame.data.pack(df, dst)
Serializes a DATA frame to the destination dst
. The dst
must be a array-like Lua table.
The df
must be hash-like Lua table which contains:
header
, the frame header;payload
, the HTTP request/response body;
h2_frame.data.unpack
syntax: local ok, err = h2_frame.data.unpack(df, src, stream)
Deserializes a DATA frame from the Lua string src
, the length of src
must be at least the size specified in the df.header.length
The df
should be a hash-like Lua table which already contains the current DATA frame's heaer, i.e. df.header
.
The last parameter stream
specifies the stream that current DATA frame belongs.
The corresponding action will be taken, for example, stream state transition will happens.
In case of failure, nil
and an error code will be given.
h2_frame.data.new
syntax: local df = h2_frame.data.new(payload, pad, last, sid)
Creates a DATA frame which takes the payload payload
.
The pad
specifies the padding data, which is optional.
When last
is true, current DATA frame will takes the END_STREAM flag.
The sid
specifies the stream that current DATA frame belongs.
h2_frame.push_promise.unpack
syntax: local df = h2_frame.data.new(payload, pad, last, sid)
Currently any incoming PUSH_PROMISE frame will be rejected.
This method always returns nil
and the error PROTOCOL_ERROR.
resty.http2.hpack
This module implements some low-level HPACK APIs.
To load this module, just do this:
local hpack = require "resty.http2.hpack"
hpack.encode
syntax: hpack.encode(src, dst, lower)
Encodes the Lua string src
to destination dst
, the dst
must be a array-like Lua table. Huffman codes will be tried firstly.
The lower
specifies whether current encoding operation is case-insensitive, default is false
.
hpack.indexed
syntax: local v = hpack.indexed(index)
Returns the index after using Indexed Header Field Representation.
hpack.incr_indexed
syntax: local v = hpack.indexed(index)
Returns the index after using Literal Header Field With Incremental Indexing.
hpack.new
syntax: local h = hpack.new(size)
Creates a hpack instance, since the HPACK decoding is stateful.
The size
represents the maximum hpack table size, default is 4096 bytes.
The return value h
, represents the HPACK instance. One of h's member is important, i.e. h.cached
, which saves the whole header block fragments, and h:decode will analysis the data inside the h.cached
.
Currently the h2_frame.headers.unpack and
h2_frame.continuation.unpack will push header block fragments to h.cached
, once the block is complete, the decoding will be executed.
h:insert_entry
syntax: local ok = h:insert_entry(header_name, header_value)
Tries to insert a header entry with name header_name
and value header_value
to the HPACK dynamic table.
The insertion maybe failed if this entry is too large. Necessary entry eviction will happen if the space is not enough.
This method will return true
if the insertion is successful or false
if not.
h:resize
syntax: local ok = h:resize(new_size)
Adjusts the dynamic table size to new_size
, currently the new_size
cannot exceed 4096
, other the resize operation will be failed.
When the dynamic table size is shrink, some entries will be evicted according the HPACK's rule.
This method will return true
if the resize operation is successful or false
if not.
h:decode
syntax: local ok, err = h:decode(dst)
Decodes the header block fragments inside h.cached
, decoded headers will be saved in dst
, a hash-like Lua table.
In case of failure, nil
and an error code will be given.
h:get_indexed_header
syntax: local entry = h:get_indexed_header(index)
Returns the header entry according the index index
.
The return value will be nil
if the index is invalid, otherwise, the entry
will be hash-like Lua table with two items:
entry.name
, the header name;entry.value
, the header value;
resty.http2.error
This module implements some low-level error-relevant APIs.
There many defined error codes, basically they are consistent with HTTP/2 protocol:
h2_error.NO_ERROR
h2_error.PROTOCOL
h2_error.INTERNAL_ERROR
h2_error.FLOW_CONTROL_ERROR
h2_error.SETTINGS_TIMEOUT
h2_error.STREAM_CLOSED
h2_error.FRAME_SIZE_ERROR
h2_error.REFUSED_STREAM
h2_error.CANCEL
h2_error.COMPRESSION_ERROR
h2_error.CONNECT_ERROR
h2_error.ENHANCE_YOUR_CALM
h2_error.INADEQUATE_SECURITY
h2_error.HTTP_1_1_REQUIRED
And three custom error codes:
h2_error.STREAM_PROTOCOL_ERROR
, stream level protocol error;h2_error.STREAM_FLOW_CONTROL_ERROR
, stream level flow control error;h2_error.STREAM_FRAME_SIZE_ERROR
, stream level frame size error;
Stream level errors will not influence the whole connection but reset the current stream.
To load this module, just do this:
h2_error.strerror
syntax: local msg = h2_error.strerror(code)
Returns a Lua string which describe the error code code
, "unknown error"
will be given if the error code is unknown.
h2_error.is_stream_error
syntax: local ok = h2_error.is_stream_error(code)
Judges whether the error code code
is a stream-level error.
See Also
- 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
You may find additional configuration tips and documentation for this module in the GitHub repository for nginx-module-http2.