Skip to content

The HTTP/2 Protocol (Client Side) Implementation for OpenResty.

License

Notifications You must be signed in to change notification settings

adobe-apiplatform/lua-resty-http2

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Name

lua-resty-http2 - The HTTP/2 Protocol (Client Side) Implementation for OpenResty. Still Pending.

Build Status

Table of Contents

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.

Back to TOC

API Implemented

Back to TOC

resty.http2

To load this module, just do this:

local http2 = require "resty.http2"

Back to TOC

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.

Back to TOC

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.

Back to TOC

client:request

syntax: local ok, err = client:request(headers, body?, on_headers_reach, on_data_reach, on_trailers_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 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.

The last parameter on_trailers_reach is not necessary but is required if trailer headers exist in in some scenarios (like gRPC).

local abort = on_trailers_reach(ctx, trailers)

The 2nd parameter is the a hash-like Lua table which holds all the trailer HTTP headers.

Likewise, on_trailers_reach can decide whether aborts the HTTP/2 session by passing the return value abort, session will be aborted if abort is true.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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")

Back to TOC

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"

Back to TOC

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.

Back to TOC

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.

Back to TOC

session:frame_queue

syntax: session:frame_queue(frame)

Appends frame to current session's output queue.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

session:detach

syntax: session:detach()

Detachs the current HTTP/2 session with the Cosocket object.

Back to TOC

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.

Back to TOC

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"

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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"

Back to TOC

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.

Back to TOC

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.

Back to TOC

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. Back to TOC

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;

Back to TOC

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.

Back to TOC

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;

Back to TOC

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.

Back to TOC

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.

Back to TOC

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;

Back to TOC

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.

Back to TOC

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;

Back to TOC

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;

Back to TOC

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.

Back to TOC

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;

Back to TOC

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.

Back to TOC

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.

Back to TOC

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;

Back to TOC

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.

Back to TOC

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.

Back to TOC

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);

Back to TOC

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.

Back to TOC

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.

Back to TOC

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);

Back to TOC

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.

Back to TOC

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.

Back to TOC

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;

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

resty.http2.hpack

This module implements some low-level HPACK APIs.

To load this module, just do this:

local hpack = require "resty.http2.hpack"

Back to TOC

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.

Back to TOC

hpack.indexed

syntax: local v = hpack.indexed(index)

Returns the index after using Indexed Header Field Representation.

Back to TOC

hpack.incr_indexed

syntax: local v = hpack.indexed(index)

Returns the index after using Literal Header Field With Incremental Indexing.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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.

Back to TOC

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;

Back to TOC

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:

Back to TOC

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.

Back to TOC

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.

Back to TOC

Author

Alex Zhang (张超) [email protected], UPYUN Inc.

Back to TOC

Copyright and License

Please see the LICENSE file.

Back to TOC

See Also

Back to TOC

About

The HTTP/2 Protocol (Client Side) Implementation for OpenResty.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Lua 98.2%
  • Other 1.8%