Zum Inhalt

http2: Die HTTP/2-Protokoll (Client-Seite) Implementierung für nginx-module-lua

Installation

Wenn Sie noch kein RPM-Repository-Abonnement eingerichtet haben, melden Sie sich an. Dann können Sie mit den folgenden Schritten fortfahren.

CentOS/RHEL 7 oder 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

Um diese Lua-Bibliothek mit NGINX zu verwenden, stellen Sie sicher, dass nginx-module-lua installiert ist.

Dieses Dokument beschreibt lua-resty-http2 v1.0, veröffentlicht am 20. November 2019.


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)
    -- Verarbeiten Sie die Antwort-Header
end

local on_data_reach = function(ctx, data)
    -- Verarbeiten Sie den Antwortkörper
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()

Als formelles Beispiel lesen Sie bitte die util/example.lua.

Beschreibung

Diese reine Lua-Bibliothek implementiert das Client-Seite HTTP/2-Protokoll, aber nicht alle Details sind abgedeckt, zum Beispiel werden die Stream-Abhängigkeiten beibehalten, aber nie verwendet.

Es gibt einige inhärente Einschränkungen, die jedoch nicht gelöst sind.

Kann nicht über SSL/TLS-handshakete Verbindungen verwendet werden. Der tcpsock:sslhandshake unterstützt die ALPN- oder NPN-Erweiterungen nicht, daher können derzeit nur die unverschlüsselten Verbindungen verwendet werden. Die Bibliothek startet die HTTP/2-Sitzung, indem sie das Verbindungspräfix sendet, d.h. die Zeichenfolge:

PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n

Diese Bibliothek bietet einen Patch für die Application-Layer Protocol Negotiation. Verwenden Sie einfach dieses, wenn Sie es benötigen.

Es kann nur eine HTTP-Anfrage eingereicht werden. Derzeit unterstützen die implementierten APIs nur die Einreichung einer HTTP-Anfrage. PRs sind willkommen, um dies zu lösen.

HTTP/2-Sitzungswiederverwendung. Das HTTP/2-Protokoll ist als persistent konzipiert, während das Cosocket-Objekt an eine spezifische HTTP-Anfrage gebunden ist. Man muss das Cosocket-Objekt schließen oder es aktiv halten, bevor die Anfrage beendet ist. Dieses Modell steht im Konflikt mit der Wiederverwendung von HTTP/2-Sitzungen. Nur eine Umgehungslösung kann dies lösen. Siehe client:keepalive für die Details.

Implementierte API

resty.http2

Um dieses Modul zu laden, tun Sie einfach Folgendes:

local http2 = require "resty.http2"

http2.new

Syntax: local client, err = http2.new(opts)

Erstellt einen HTTP/2-Client, indem die Optionen angegeben werden. Im Falle eines Fehlers werden nil und eine Fehlermeldungszeichenfolge zurückgegeben.

Der einzige Parameter opts, der eine Lua-Tabelle ist, enthält einige Felder:

  • recv, eine Lua-Funktion, die verwendet wird, um Bytes zu lesen;

  • send, eine Lua-Funktion, die verwendet wird, um Bytes zu senden;

  • ctx, ein undurchsichtiger Datentyp, der als Kontext des Aufrufers fungiert;

Die recv- und send-Funktionen werden wie folgt aufgerufen:

local data, err = recv(ctx, size)
local ok, err = send(ctx, data)
  • preread_size, eine Lua-Zahl, die die anfängliche Sendefenstergröße des Peers beeinflusst (über das SETTINGS-Frame beworben), Standard ist 65535;

  • max_concurrent_stream, eine Lua-Zahl, die die maximalen gleichzeitigen Streams in einer HTTP/2-Sitzung begrenzt, Standard ist 128;

  • max_frame_size, eine Lua-Zahl, die die maximale Rahmenhöhe begrenzt, die der Peer senden kann, Standard ist 16777215.

  • key, eine Lua-Zeichenfolge, die angibt, welche zwischengespeicherte HTTP/2-Sitzung die Aufrufer wiederverwenden möchten. Wenn nicht gefunden, wird eine neue HTTP/2-Sitzung erstellt. Siehe client:keepalive für weitere Details.

client:acknowledge_settings

Syntax: local ok, err = client:acknowledge_settings()

Bestätigt das SETTINGS-Frame des Peers, die Einstellungen werden automatisch angewendet.

Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

client:request

Syntax: local ok, err = client:request(headers, body?, on_headers_reach, on_data_reach)

Sendet eine HTTP-Anfrage an den Peer.

Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

Die headers sollten eine array-ähnliche Lua-Tabelle sein, die die HTTP-Anfrage-Header darstellt, jeder Eintrag ist wie { name = "header1", value = "value1" }.

Es ist erwähnenswert, dass diese Bibliothek sich nicht um die Semantik der HTTP-Header kümmert, daher liegt es in der Verantwortung der Aufrufer, dies bereitzustellen, und die Aufrufer sollten alle notwendigen Konvertierungen implementieren. Zum Beispiel sollte Host in :authority umgewandelt werden. Auch die folgenden Header werden ignoriert, da sie spezifisch für die Verbindung sind.

  • Connection
  • Keep-Alive
  • Proxy-Connection
  • Upgrade
  • Transfer-Encoding

Der body kann eine Lua-Zeichenfolge sein, die den HTTP-Anfragekörper darstellt. Er kann auch eine Lua-Funktion sein, um das Streaming-Upload zu implementieren. Wenn body eine Lua-Funktion ist, wird sie wie folgt aufgerufen:

local part_data, last, err = body(size)

Im Falle eines Fehlers sollte body den 3. Rückgabewert err bereitstellen, um dieser Bibliothek mitzuteilen, dass ein schwerwiegender Fehler aufgetreten ist. Dann wird diese Methode sofort abgebrochen, und ein GOAWAY-Frame wird mit dem Fehlercode INTERNAL_ERROR an den Peer gesendet.

Wenn alle Daten generiert wurden, sollte der 2. Rückgabewert last bereitgestellt werden, und sein Wert muss true sein.

on_headers_reach sollte eine Lua-Funktion sein, die als Callback fungiert, die aufgerufen wird, wenn die vollständigen HTTP-Antwort-Header empfangen werden. Sie wird wie folgt aufgerufen:

local abort = on_headers_reach(ctx, headers)

Der 2. Parameter headers ist eine hash-ähnliche Lua-Tabelle, die die vom Peer empfangenen HTTP-Antwort-Header darstellt.

on_headers_reach kann entscheiden, ob die HTTP/2-Sitzung abgebrochen wird, indem es einen booleschen Wert abort an die Bibliothek zurückgibt. Die HTTP/2-Sitzung wird abgebrochen, wenn on_headers_reach einen wahren Wert zurückgibt.

Der letzte Parameter, on_data_reach, ist eine Lua-Funktion, die als Callback fungiert, die jedes Mal aufgerufen wird, wenn der Antwortkörper empfangen wird. Sie wird wie folgt aufgerufen:

local abort = on_data_reach(ctx, data)

Der 2. Parameter data ist eine Lua-Zeichenfolge, die den HTTP-Antwortkörper darstellt, der dieses Mal empfangen wurde.

Die Bedeutung des Rückgabewerts ist die gleiche wie bei on_headers_reach.

Nachdem diese Methode zurückgegeben hat, ist die HTTP/2-Sitzung weiterhin aktiv. Man kann entscheiden, diese Sitzung zu schließen, indem man client:close aufruft oder fortfährt, um etwas zu tun.

client:send_request

Syntax: local stream, err = client:send_request(headers, body?)

Sendet die Header und den Body (falls vorhanden) an den Peer.

Die Bedeutungen von headers und body sind die gleichen wie bei client:request.

Das entsprechende erstellte Stream-Objekt wird zurückgegeben, wenn diese Methode zurückgegeben wird.

Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

client:read_headers

Syntax: local headers, err = client:read_headers(stream)

Liest die Antwort-Header vom Peer. Der Parameter stream ist derjenige, der von client:send_request erstellt wurde.

Die zurückgegebenen Header sind eine hash-ähnliche Lua-Tabelle, die die gesamten HTTP-Antwort-Header enthält, die einige Pseudo-Header wie ":status" enthalten können. Die Aufrufer sollten bei Bedarf einige Transformationen durchführen.

Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

client:read_body

Syntax: local body, err = client:read_body(stream)

Liest ein DATA-Frame vom Peer. Der Parameter stream ist derjenige, der von client:send_request erstellt wurde.

Die zurückgegebenen Daten sind eine Lua-Zeichenfolge, die ein Stück des Antwortkörpers darstellt. Eine leere Zeichenfolge wird zurückgegeben, wenn der gesamte Körper gelesen wurde.

Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

client:close

Syntax: local ok, err = client:close(code)

Schließt die aktuelle HTTP/2-Sitzung mit dem Fehlercode code.

Siehe resty.http2.error, um die Fehlercodes zu lernen.

Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

client:keepalive

Syntax: client:keepalive(key)

Speichert die aktuelle HTTP/2-Sitzung zur Wiederverwendung im Cache. Beachten Sie, dass fehlerhafte HTTP/2-Sitzungen niemals im Cache gespeichert werden. Die HTTP/2-Sitzung wird von der Verbindung getrennt, genauer gesagt vom aktuellen Cosocket-Objekt.

Die getrennte HTTP/2-Sitzung wird in einer internen hash-ähnlichen Lua-Tabelle gespeichert. Der eindeutige Parameter key wird verwendet, um diese Sitzung zu indizieren, wenn die Aufrufer sie wiederverwenden möchten.

Nachdem diese Sitzung als aktiv festgelegt wurde, sollten die Aufrufer auch das Cosocket-Objekt als aktiv halten.

Es gibt eine inhärente Einschränkung zwischen der Zuordnung der HTTP/2-Sitzung und der zugrunde liegenden Verbindung. Eine HTTP/2-Sitzung kann nur in einer TCP-Verbindung verwendet werden, da sie zustandsbehaftet ist. Wenn die Aufrufer die Verbindung in einen Pool speichern, der mehrere Verbindungen zwischenspeichert, geht die Bindungsbeziehung verloren, da nicht sicher ist, welche Verbindung dem Cosocket-Objekt zugewiesen wird. Daher ist auch unbekannt, welche HTTP/2-Sitzung übereinstimmt.

Es gibt keinen eleganten Weg, dies zu lösen, es sei denn, das Cosocket-Modell kann eine Kennung der zugrunde liegenden Verbindung zuweisen. Was die Aufrufer jetzt tun können, ist, den Pool mit einer einzigen Größe zu verwenden, um diese Einschränkung zu umgehen, zum Beispiel:

...

sock:connect(host, port, { pool = "h2" })

...

sock:setkeepalive(75, 1)
client:keepalive("test")

resty.http2.protocol

Dieses Modul implementiert einige Low-Level-Protokoll-relevante APIs.

Um dieses Modul zu laden, tun Sie einfach Folgendes:

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

Erstellt eine neue HTTP/2-Sitzung. Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

Die Bedeutung jedes Parameters ist die gleiche wie die, die in http2.new beschrieben ist.

Das anfängliche SETTINGS-Frame und das WINDOW_UPDATE-Frame werden gesendet, bevor diese Funktion zurückgegeben wird.

session:adjust_window

Syntax: local ok = session:adjust_window(delta)

Passt die Sendefenstergröße jedes Streams an. Der Stream wird zurückgesetzt, wenn die geänderte Sendefenstergröße MAX_WINDOW_SIZE überschreitet. In diesem Fall ist ok nil.

session:frame_queue

Syntax: session:frame_queue(frame)

Fügt frame zur Ausgabewarteschlange der aktuellen Sitzung hinzu.

session:flush_queue

Syntax: local ok, err = session:flush_queue()

Packt und leert die wartenden Frames. Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

session:submit_request

Syntax: local ok, err = session:submit_request(headers, no_body, priority?, pad?)

Reicht eine HTTP-Anfrage an die aktuelle HTTP/2-Sitzung ein. Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

Bedeutung jedes Parameters:

  • headers sollte eine hash-ähnliche Lua-Tabelle sein, die die HTTP-Anfrage-Header darstellt. Es ist erwähnenswert, dass diese Bibliothek sich nicht um die Semantik der HTTP-Header kümmert, daher liegt es in der Verantwortung der Aufrufer, dies bereitzustellen, und die Aufrufer sollten alle notwendigen Pseudo-Header transformieren. Zum Beispiel sollte :authority anstelle von Host übergeben werden;

  • no_body, ein boolescher Wert, der angibt, ob diese Anfrage einen Body hat. Wenn er wahr ist, enthält das generierte HEADERS-Frame das END_HEADERS-Flag;

  • priority, eine hash-ähnliche Lua-Tabelle, die verwendet wird, um benutzerdefinierte Stream-Abhängigkeiten zu definieren:

  • priority.sid repräsentiert die abhängige Stream-ID;
  • priority.excl, ob der neue Stream die einzige Abhängigkeit des Streams wird, der durch priority.sid angezeigt wird;
  • priority.weight definiert das Gewicht des neuen Streams;

  • pad, die Polsterdaten.

session:submit_window_update

Syntax: local ok, err = session:submit_window_update(incr)

Reicht ein WINDOW_UPDATE-Frame für die gesamte HTTP/2-Sitzung mit einer Erhöhung incr ein. Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

session:recv_frame

Syntax: local frame, err = session:recv_frame()

Empfängt ein HTTP/2-Frame. Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

Die entsprechenden Aktionen werden automatisch ausgeführt, zum Beispiel wird ein GOAWAY-Frame gesendet, wenn der Peer die HTTP/2-Protokollkonventionen verletzt; ein WINDOW_UPDATE-Frame wird gesendet, wenn das Sendefenster des Peers zu klein wird.

session:close

Syntax: session:close(code?, debug_data?)

Generiert ein GOAWAY-Frame mit dem Fehlercode code und den Debugdaten debug_data. Der Standardfehlercode ist NO_ERROR und die debug_data ist nil.

Beachten Sie, dass diese Funktion das GOAWAY-Frame nur in die Ausgabewarteschlange einreiht. Die Aufrufer sollten session:flush_queue aufrufen, um die Frames tatsächlich zu senden.

session:detach

Syntax: session:detach()

Trennt die aktuelle HTTP/2-Sitzung vom Cosocket-Objekt.

session:attach

Syntax: local ok, err = session:attach(recv, send, ctx)

Bindet die aktuelle HTTP/2-Sitzung an ein Cosocket-Objekt. Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

Die Bedeutungen von recv, send und ctx sind die gleichen wie die, die in http.new beschrieben sind.

resty.http2.stream

Dieses Modul implementiert einige Low-Level-Stream-relevante APIs.

Um dieses Modul zu laden, tun Sie einfach Folgendes:

local h2_stream = require "resty.http2.stream"

h2_stream.new

Syntax: local stream = h2_stream.new(sid, weight, session)

Erstellt einen neuen Stream mit der ID sid, Gewicht weight und der HTTP/2-Sitzung, zu der er gehört.

h2_stream.new_root

Syntax: local root_stream = h2_stream.new_root(session)

Erstellt den Root-Stream mit seiner Sitzung.

Die ID des Root-Streams ist 0x0 und ist wirklich ein virtueller Stream, der verwendet wird, um die gesamte HTTP/2-Sitzung zu manipulieren.

stream:submit_headers

Syntax: local ok, err = stream:submit_headers(headers, end_stream, priority?, pad?)

Reicht einige HTTP-Header an den Stream ein.

Der erste Parameter headers sollte eine hash-ähnliche Lua-Tabelle sein, die die HTTP-Anfrage-Header darstellt. Es ist erwähnenswert, dass diese Bibliothek sich nicht um die Semantik der HTTP-Header kümmert, daher liegt es in der Verantwortung der Aufrufer, dies bereitzustellen, und die Aufrufer sollten alle notwendigen Pseudo-Header transformieren. Zum Beispiel sollte :authority anstelle von Host übergeben werden;

Der Parameter end_stream sollte ein boolescher Wert sein und wird verwendet, um zu steuern, ob das HEADERS-Frame das END_STREAM-Flag erhalten soll. Grundsätzlich können die Aufrufer ihn auf wahr setzen, wenn kein Anfragekörper gesendet werden muss.

priority sollte eine hash-ähnliche Lua-Tabelle (falls vorhanden) sein, die verwendet wird, um benutzerdefinierte Stream-Abhängigkeiten zu definieren: * priority.sid repräsentiert die abhängige Stream-ID; * priority.excl, ob der neue Stream die einzige Abhängigkeit des Streams wird, der durch priority.sid angezeigt wird; * priority.weight definiert das Gewicht des neuen Streams;

Der letzte Parameter pad repräsentiert die Polsterdaten.

Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den entsprechenden Fehler beschreibt, zurückgegeben.

stream:submit_data

Syntax: local ok, err = stream:submit_data(data, pad, last)

Reicht einen Teil des Anfragekörpers an den Stream ein. data sollte eine Lua-Zeichenfolge sein, mit optionalen Polsterdaten.

Der letzte Parameter last gibt an, ob dies die letzte Einreichung ist. Das aktuelle DATA-Frame wird das END_STREAM-Flag anhängen, wenn last wahr ist.

Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den entsprechenden Fehler beschreibt, zurückgegeben.

stream:submit_window_update

Syntax: local ok, err = session:submit_window_update(incr)

Reicht ein WINDOW_UPDATE-Frame für den Stream mit einer Erhöhung incr ein. Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

stream:set_dependency

Syntax: stream:set_dependency(depend, excl)

Setzt die Abhängigkeiten des aktuellen Streams zu einem Stream mit der ID depend.

Der zweite Parameter excl gibt an, ob der aktuelle Stream das einzige Kind von depend sein wird.

Wenn depend fehlt, wird der Zielstream der Root-Stream und excl wird als false behandelt.

stream:rst

Syntax: stream:rst(code)

Generiert ein RST_STREAM-Frame mit dem Fehlercode code. Im Falle von code, der fehlt, wird der NO_ERROR-Code ausgewählt.

Beachten Sie, dass diese Methode nur ein RST_STREAM-Frame generiert und nicht sendet. Der Aufrufer sollte dieses Frame senden, indem er session:flush_queue aufruft.

resty.http2.frame

Dieses Modul implementiert einige Low-Level-Frame-relevante APIs.

Um dieses Modul zu laden, tun Sie einfach Folgendes:

local h2_frame = require "resty.http2.frame"

h2_frame.header.new

Syntax: local hd = h2_frame.header.new(length, typ, flags, id)

Erstellt einen Frame-Header mit der Payload-Länge length, dem Frame-Typ type und verwendet flags als die Frame-Flags, die zum Stream id gehören.

h2_frame.header.pack

Syntax: h2_frame.header.pack(hd, dst)

Serialisiert den Frame-Header hd in das Ziel dst. Das dst muss eine array-ähnliche Lua-Tabelle sein.

h2_frame.header.unpack

Syntax: h2_frame.header.unpack(src)

Deserialisiert einen Frame-Header aus einer Lua-Zeichenfolge src. Die Länge von src muss mindestens 9 Oktette betragen.

h2_frame.priority.pack

Syntax: h2_frame.priority.pack(pf, dst)

Serialisiert ein PRIORITY-Frame in das Ziel dst. Das dst muss eine array-ähnliche Lua-Tabelle sein.

Das pf muss eine hash-ähnliche Lua-Tabelle sein, die enthält:

  • header, der Frame-Header;
  • depend, die abhängige Stream-ID;
  • excl, gibt an, ob der aktuelle Stream, in dem dieses PRIORITY-Frame bleibt, das einzige Kind des Streams wird, der durch depend identifiziert wird;
  • weight, weist dem aktuellen Stream ein neues Gewicht weight zu;

h2_frame.priority.unpack

Syntax: local ok, err = h2_frame.priority.unpack(pf, src, stream)

Deserialisiert ein PRIORITY-Frame aus einer Lua-Zeichenfolge src. Die Länge von src muss mindestens die Größe betragen, die im pf.header.length angegeben ist.

Das pf sollte eine hash-ähnliche Lua-Tabelle sein, die bereits den Header des aktuellen PRIORITY-Frames enthält, d.h. pf.header.

Der letzte Parameter stream gibt den Stream an, zu dem das aktuelle PRIORITY-Frame gehört.

Entsprechende Aktionen werden innerhalb dieser Methode automatisch ausgeführt, z.B. das Erstellen der neuen Abhängigkeiten.

Im Falle eines Fehlers werden nil und ein Fehlercode zurückgegeben.

h2_frame.rst_stream.pack

Syntax: h2_frame.rst_stream.pack(rf, dst)

Serialisiert ein RST_STREAM-Frame in das Ziel dst. Das dst muss eine array-ähnliche Lua-Tabelle sein.

Das rf muss eine hash-ähnliche Lua-Tabelle sein, die enthält:

  • header, der Frame-Header;
  • error_code, der Fehlercode;

h2_frame.rst_stream.unpack

Syntax: h2_frame.rst_stream.unpack(rf, src, stream)

Deserialisiert ein RST_STREAM-Frame aus einer Lua-Zeichenfolge src. Die Länge von src muss mindestens die Größe betragen, die im rf.header.length angegeben ist.

Das rf sollte eine hash-ähnliche Lua-Tabelle sein, die bereits den Header des aktuellen RST_STREAM-Frames enthält, d.h. rf.header.

Der letzte Parameter stream gibt den Stream an, zu dem das aktuelle RST_STREAM-Frame gehört.

Entsprechende Aktionen werden innerhalb dieser Methode automatisch ausgeführt, z.B. das Ändern des Stream-Zustands.

Im Falle eines Fehlers werden nil und ein Fehlercode zurückgegeben.

h2_frame.rst_stream.new

Syntax: local rf = h2_frame.rst_stream.new(error_code, sid)

Erstellt ein RST_STREAM-Frame mit dem Fehlercode error_code, das zum Stream sid gehört.

h2_frame.settings.pack

Syntax: h2_frame.settings.pack(sf, dst)

Serialisiert ein SETTINGS-Frame in das Ziel dst. Das dst muss eine array-ähnliche Lua-Tabelle sein.

Das sf muss eine hash-ähnliche Lua-Tabelle sein, die enthält:

  • header, der Frame-Header;
  • item, die spezifischen Einstellungen, die eine array-ähnliche Lua-Tabelle sein sollten, wobei jedes Element eine hash-ähnliche Lua-Tabelle sein sollte:
  • id, der Einstellungsbezeichner, kann sein:
    • SETTINGS_ENABLE_PUSH (0x2)
    • SETTINGS_MAX_CONCURRENT_STREAMS (0x3)
    • SETTINGS_INITIAL_WINDOW_SIZE (0x4)
    • SETTINGS_MAX_FRAME_SIZE (0x5)
  • value, der entsprechende Einstellungswert;

h2_frame.settings.unpack

Syntax: local ok, err = h2_frame.settings.unpack(sf, src, stream)

Deserialisiert ein SETTINGS-Frame aus einer Lua-Zeichenfolge src. Die Länge von src muss mindestens die Größe betragen, die im sf.header.length angegeben ist.

Das sf sollte eine hash-ähnliche Lua-Tabelle sein, die bereits den Header des aktuellen SETTINGS-Frames enthält, d.h. sf.header.

Der letzte Parameter stream gibt den Stream an, zu dem das aktuelle SETTINGS-Frame gehört (muss der Root-Stream sein).

Entsprechende Aktionen werden innerhalb dieser Methode automatisch ausgeführt, z.B. das Aktualisieren des Wertes der HTTP/2-Sitzungseinstellungen.

Im Falle eines Fehlers werden nil und ein Fehlercode zurückgegeben.

h2_frame.settings.new

Syntax: local sf = h2_frame.settings.new(flags, payload)

Erstellt ein SETTINGS-Frame mit den Flags flags und dem Payload-Element payload.

Das payload sollte eine array-ähnliche Lua-Tabelle sein, wobei jedes Element eine hash-ähnliche Lua-Tabelle sein sollte: * id, der Einstellungsbezeichner, kann sein: * SETTINGS_ENABLE_PUSH (0x2) * SETTINGS_MAX_CONCURRENT_STREAMS (0x3) * SETTINGS_INITIAL_WINDOW_SIZE (0x4) * SETTINGS_MAX_FRAME_SIZE (0x5) * value, der entsprechende Einstellungswert;

h2_frame.ping.pack

Syntax: h2_frame.ping.pack(pf, dst)

Serialisiert ein PING-Frame in das Ziel dst. Das dst muss eine array-ähnliche Lua-Tabelle sein.

Das pf muss eine hash-ähnliche Lua-Tabelle sein, die enthält:

  • header, der Frame-Header;
  • opaque_data_hi, der höchste 32-Bit-Wert der entsprechenden Ping-Daten;
  • opaque_data_lo, der niedrigste 32-Bit-Wert der entsprechenden Ping-Daten;

h2_frame.ping.unpack

Syntax: local ok, err = h2_frame.ping.unpack(pf, src, stream)

Deserialisiert ein PING-Frame aus einer Lua-Zeichenfolge src. Die Länge von src muss mindestens die Größe betragen, die im sf.header.length angegeben ist.

Das pf sollte eine hash-ähnliche Lua-Tabelle sein, die bereits den Header des aktuellen PING-Frames enthält, d.h. pf.header.

Der letzte Parameter stream gibt den Stream an, zu dem das aktuelle PING-Frame gehört (muss der Root-Stream sein).

Im Falle eines Fehlers werden nil und ein Fehlercode zurückgegeben.

h2_frame.goaway.pack

Syntax: h2_frame.goaway.pack(gf, dst)

Serialisiert ein GOAWAY-Frame in das Ziel dst. Das dst muss eine array-ähnliche Lua-Tabelle sein.

Das gf muss eine hash-ähnliche Lua-Tabelle sein, die enthält:

  • header, der Frame-Header;
  • last_stream_id, die letzte vom Peer initialisierte Stream-ID;
  • error_code, der Fehlercode;
  • debug_data, die Debugdaten;

h2_frame.goaway.unpack

Syntax: local ok, err = h2_frame.goaway.unpack(gf, src, stream)

Deserialisiert ein GOAWAY-Frame aus einer Lua-Zeichenfolge src. Die Länge von src muss mindestens die Größe betragen, die im gf.header.length angegeben ist.

Das gf sollte eine hash-ähnliche Lua-Tabelle sein, die bereits den Header des aktuellen GOAWAY-Frames enthält, d.h. gf.header.

Der letzte Parameter stream gibt den Stream an, zu dem das aktuelle GOAWAY-Frame gehört (muss der Root-Stream sein).

Im Falle eines Fehlers werden nil und eine Lua-Zeichenfolge, die den Fehlergrund beschreibt, zurückgegeben.

h2_frame.goaway.new

Syntax: local gf = h2_frame.goaway.new(last_sid, error_code, debug_data)

Erstellt ein GOAWAY-Frame mit der letzten vom Peer initialisierten Stream-ID last_sid und dem Fehlercode error_code. Optional mit den Debugdaten debug_data.

h2_frame.window_update.pack

Syntax: h2_frame.window_update.pack(wf, dst)

Serialisiert ein WINDOW_UPDATE-Frame in das Ziel dst. Das dst muss eine array-ähnliche Lua-Tabelle sein.

Das wf muss eine hash-ähnliche Lua-Tabelle sein, die enthält:

  • header, der Frame-Header;
  • window_size_increment, die Erhöhung der Fenstergröße;

h2_frame.window_update.unpack

Syntax: local ok, err = h2_frame.window_update.unpack(wf, src, stream)

Deserialisiert ein WINDOW_UPDATE-Frame aus einer Lua-Zeichenfolge src. Die Länge von src muss mindestens die Größe betragen, die im wf.header.length angegeben ist.

Das wf sollte eine hash-ähnliche Lua-Tabelle sein, die bereits den Header des aktuellen WINDOW_UPDATE-Frames enthält, d.h. wf.header.

Der letzte Parameter stream gibt den Stream an, zu dem das aktuelle WINDOW_UPDATE-Frame gehört.

Im Falle eines Fehlers werden nil und ein Fehlercode zurückgegeben.

h2_frame.window_update.new

Syntax: local wf = h2_frame.window_update.new(sid, window)

Erstellt ein WINDOW_UPDATE-Frame mit der Stream-ID sid und vergrößert die Fenstergröße, die durch window angegeben ist.

h2_frame.headers.pack

Syntax: h2_frame.headers.pack(hf, dst)

Serialisiert ein HEADERS-Frame in das Ziel dst. Das dst muss eine array-ähnliche Lua-Tabelle sein.

Das hf muss eine hash-ähnliche Lua-Tabelle sein, die enthält:

  • header, der Frame-Header;
  • pad, die Polsterdaten;
  • depend, die abhängige Stream-ID;
  • excl, gibt an, ob der Stream, zu dem das aktuelle HEADERS-Frame gehört, das einzige Kind des Streams depend wird;
  • weight, gibt das Gewicht des Streams an, zu dem das aktuelle HEADERS-Frame gehört;
  • block_frags, die einfachen HTTP-Header (nach dem HPACK-Komprimieren);

h2_frame.headers.unpack

Syntax: local ok,err = h2_frame.headers.unpack(hf, src, stream)

Deserialisiert ein HEADERS-Frame aus der Lua-Zeichenfolge src. Die Länge von src muss mindestens die Größe betragen, die im hf.header.length angegeben ist.

Das hf sollte eine hash-ähnliche Lua-Tabelle sein, die bereits den Header des aktuellen HEADERS-Frames enthält, d.h. hf.header.

Der letzte Parameter stream gibt den Stream an, zu dem das aktuelle HEADER-Frame gehört.

Die entsprechende Aktion wird ausgeführt, zum Beispiel wird der Stream-Zustandsübergang stattfinden.

Im Falle eines Fehlers werden nil und ein Fehlercode zurückgegeben.

h2_frame.headers.new

Syntax: local hf = h2_frame.headers.new(frags, pri?, pad?, end_stream, end_headers, sid)

Erstellt ein HEADERS-Frame, das die Blockfragmente frags enthält.

Der Parameter pri kann verwendet werden, um die Stream-Abhängigkeiten anzugeben. pri sollte eine hash-ähnliche Lua-Tabelle sein, die enthält:

  • sid, die abhängige Stream-ID;
  • excl, ob der Stream sid das einzige Kind des abhängigen Streams wird;
  • weight, definiert das Gewicht des aktuellen Streams (angegeben durch sid);

Das pad gibt die Polsterdaten an, die optional sind.

Wenn end_stream wahr ist, erhält das aktuelle HEADERS-Frame das END_STREAM-Flag. Ebenso erhält das aktuelle HEADERS-Frame das END_HEADERS-Flag, wenn end_headers wahr ist.

Man sollte darauf achten, dass, wenn das aktuelle HEADERS-Frame nicht alle Header enthält, dann müssen gemäß dem HTTP/2-Protokoll ein oder mehrere CONTINUATION-Frames folgen.

h2_frame.continuation.pack

Syntax: h2_frame.continuation.pack(cf, dst)

Serialisiert ein CONTINUATION-Frame in das Ziel dst. Das dst muss eine array-ähnliche Lua-Tabelle sein.

Das cf muss eine hash-ähnliche Lua-Tabelle sein, die enthält:

  • header, der Frame-Header;
  • block_frags, die einfachen HTTP-Header (nach dem HPACK-Komprimieren);

h2_frame.continuation.unpack

Syntax: local ok, err = h2_frame.continuation.unpack(cf, src, stream)

Deserialisiert ein CONTINUATION-Frame aus der Lua-Zeichenfolge src. Die Länge von src muss mindestens die Größe betragen, die im cf.header.length angegeben ist.

Das cf sollte eine hash-ähnliche Lua-Tabelle sein, die bereits den Header des aktuellen CONTINUATION-Frames enthält, d.h. cf.header.

Der letzte Parameter stream gibt den Stream an, zu dem das aktuelle CONTINUATION-Frame gehört.

Die entsprechende Aktion wird ausgeführt, zum Beispiel wird der Stream-Zustandsübergang stattfinden.

Im Falle eines Fehlers werden nil und ein Fehlercode zurückgegeben.

h2_frame.continuation.new

Syntax: local cf = h2_frame.continuation.new(frags, end_headers, sid)

Erstellt ein CONTINUATION-Frame, das die Blockfragmente frags enthält.

Wenn end_headers wahr ist, erhält das aktuelle CONTINUATION-Frame das END_HEADERS-Flag.

Man sollte darauf achten, dass, wenn das aktuelle CONTINUATION-Frame nicht alle Header enthält, dann müssen gemäß dem HTTP/2-Protokoll ein oder mehrere CONTINUATION-Frames folgen.

Der sid gibt den Stream an, zu dem das aktuelle CONTINUATION-Frame gehört.

h2_frame.data.pack

Syntax: h2_frame.data.pack(df, dst)

Serialisiert ein DATA-Frame in das Ziel dst. Das dst muss eine array-ähnliche Lua-Tabelle sein.

Das df muss eine hash-ähnliche Lua-Tabelle sein, die enthält:

  • header, der Frame-Header;
  • payload, der HTTP-Anfrage-/Antwortkörper;

h2_frame.data.unpack

Syntax: local ok, err = h2_frame.data.unpack(df, src, stream)

Deserialisiert ein DATA-Frame aus der Lua-Zeichenfolge src. Die Länge von src muss mindestens die Größe betragen, die im df.header.length angegeben ist.

Das df sollte eine hash-ähnliche Lua-Tabelle sein, die bereits den Header des aktuellen DATA-Frames enthält, d.h. df.header.

Der letzte Parameter stream gibt den Stream an, zu dem das aktuelle DATA-Frame gehört.

Die entsprechende Aktion wird ausgeführt, zum Beispiel wird der Stream-Zustandsübergang stattfinden.

Im Falle eines Fehlers werden nil und ein Fehlercode zurückgegeben.

h2_frame.data.new

Syntax: local df = h2_frame.data.new(payload, pad, last, sid)

Erstellt ein DATA-Frame, das die Payload payload enthält.

Das pad gibt die Polsterdaten an, die optional sind.

Wenn last wahr ist, erhält das aktuelle DATA-Frame das END_STREAM-Flag.

Der sid gibt den Stream an, zu dem das aktuelle DATA-Frame gehört.

h2_frame.push_promise.unpack

Syntax: local df = h2_frame.data.new(payload, pad, last, sid)

Derzeit wird jedes eingehende PUSH_PROMISE-Frame abgelehnt.

Diese Methode gibt immer nil und den Fehler PROTOCOL_ERROR zurück.

resty.http2.hpack

Dieses Modul implementiert einige Low-Level HPACK-APIs.

Um dieses Modul zu laden, tun Sie einfach Folgendes:

local hpack = require "resty.http2.hpack"

hpack.encode

Syntax: hpack.encode(src, dst, lower)

Kodiert die Lua-Zeichenfolge src in das Ziel dst. Das dst muss eine array-ähnliche Lua-Tabelle sein. Huffman-Codes werden zuerst versucht.

Der lower gibt an, ob die aktuelle Kodierungsoperation nicht zwischen Groß- und Kleinschreibung unterscheidet. Standard ist false.

hpack.indexed

Syntax: local v = hpack.indexed(index)

Gibt den Index zurück, nachdem die Indexed Header Field Representation verwendet wurde.

hpack.incr_indexed

Syntax: local v = hpack.indexed(index)

Gibt den Index zurück, nachdem die Literal Header Field With Incremental Indexing verwendet wurde.

hpack.new

Syntax: local h = hpack.new(size)

Erstellt eine HPACK-Instanz, da das HPACK-Dekodieren zustandsbehaftet ist.

Die size repräsentiert die maximale HPACK-Tabellengröße, Standard ist 4096 Bytes.

Der Rückgabewert h repräsentiert die HPACK-Instanz. Eines von h's Mitgliedern ist wichtig, d.h. h.cached, das die gesamten Headerblockfragmente speichert, und h:decode wird die Daten innerhalb des h.cached analysieren.

Derzeit werden die h2_frame.headers.unpack und h2_frame.continuation.unpack Headerblockfragmente in h.cached schieben. Sobald der Block vollständig ist, wird das Dekodieren ausgeführt.

h:insert_entry

Syntax: local ok = h:insert_entry(header_name, header_value)

Versucht, einen Header-Eintrag mit dem Namen header_name und dem Wert header_value in die HPACK-Dynamiktabelle einzufügen.

Die Einfügung kann fehlschlagen, wenn dieser Eintrag zu groß ist. Notwendige Einträge werden entfernt, wenn der Platz nicht ausreicht.

Diese Methode gibt true zurück, wenn die Einfügung erfolgreich ist, oder false, wenn nicht.

h:resize

Syntax: local ok = h:resize(new_size)

Passt die Größe der Dynamiktabelle auf new_size an. Derzeit darf new_size 4096 nicht überschreiten, andernfalls schlägt die Größenänderung fehl.

Wenn die Größe der Dynamiktabelle verringert wird, werden einige Einträge gemäß den HPACK-Regeln entfernt.

Diese Methode gibt true zurück, wenn die Größenänderung erfolgreich ist, oder false, wenn nicht.

h:decode

Syntax: local ok, err = h:decode(dst)

Dekodiert die Headerblockfragmente innerhalb von h.cached. Die dekodierten Header werden in dst gespeichert, einer hash-ähnlichen Lua-Tabelle.

Im Falle eines Fehlers werden nil und ein Fehlercode zurückgegeben.

h:get_indexed_header

Syntax: local entry = h:get_indexed_header(index)

Gibt den Header-Eintrag gemäß dem Index index zurück.

Der Rückgabewert ist nil, wenn der Index ungültig ist, andernfalls wird der entry eine hash-ähnliche Lua-Tabelle mit zwei Elementen sein:

  • entry.name, der Header-Name;
  • entry.value, der Header-Wert;

resty.http2.error

Dieses Modul implementiert einige Low-Level-Fehler-relevante APIs.

Es gibt viele definierte Fehlercodes, die im Wesentlichen mit dem HTTP/2-Protokoll übereinstimmen:

  • 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

Und drei benutzerdefinierte Fehlercodes:

  • h2_error.STREAM_PROTOCOL_ERROR, Fehler auf Stream-Ebene;
  • h2_error.STREAM_FLOW_CONTROL_ERROR, Fehler auf Stream-Ebene bei der Flusskontrolle;
  • h2_error.STREAM_FRAME_SIZE_ERROR, Fehler auf Stream-Ebene bei der Rahmenhöhe;

Fehler auf Stream-Ebene beeinflussen nicht die gesamte Verbindung, sondern setzen nur den aktuellen Stream zurück.

Um dieses Modul zu laden, tun Sie einfach Folgendes:

h2_error.strerror

Syntax: local msg = h2_error.strerror(code)

Gibt eine Lua-Zeichenfolge zurück, die den Fehlercode code beschreibt. "unknown error" wird zurückgegeben, wenn der Fehlercode unbekannt ist.

h2_error.is_stream_error

Syntax: local ok = h2_error.is_stream_error(code)

Beurteilt, ob der Fehlercode code ein Fehler auf Stream-Ebene ist.

Siehe auch

GitHub

Sie finden zusätzliche Konfigurationstipps und Dokumentation für dieses Modul im GitHub-Repository für nginx-module-http2.