Skip to content

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.

Build Status

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 is 65535;

  • max_concurrent_stream, a Lua number which limits the max concurrent streams in a HTTP/2 session, default is 128;

  • max_frame_size, a Lua number which limits the max frame size that peer can send, default is 16777215.

  • 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 rather Host;

  • 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 by priority.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 identifier
  • excl, specifies whether the current stream where this PRIORITY frame stays becomes the sole child of the stream identified by depend;
  • weight, assigns a new weight weight 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 stream depend;
  • 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 stream sid will be the sole child of dependent stream;
  • weight, defines the current stream's (specified by sid) 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

GitHub

You may find additional configuration tips and documentation for this module in the GitHub repository for nginx-module-http2.