Zum Inhalt

httpipe: Lua HTTP-Client-Cosocket-Treiber für nginx-module-lua, Schnittstellen sind flexibler

Installation

Wenn Sie das RPM-Repository-Abonnement noch nicht 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-httpipe

CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-httpipe

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

Dieses Dokument beschreibt lua-resty-httpipe v0.5, veröffentlicht am 25. November 2015.


Lua HTTP-Client-Cosocket-Treiber für OpenResty / ngx_lua.

Funktionen

  • HTTP 1.0/1.1 und HTTPS
  • Flexibles Schnittstellendesign
  • Streaming-Leser und Uploads
  • Chunked-encoding Anfrage-/Antwortkörper
  • Setzt das Timeout für Lese- und Sendeoperationen
  • Begrenzung der maximalen Antwortkörpergröße
  • Keepalive

Zusammenfassung

server {

  listen 9090;

  location /echo {
    content_by_lua '
      local raw_header = ngx.req.raw_header()

      if ngx.req.get_method() == "GET" then
          ngx.header["Content-Length"] = #raw_header
      end

      ngx.req.read_body()
      local body, err = ngx.req.get_body_data()

      ngx.print(raw_header)
      ngx.print(body)
    ';
  }

  location /simple {
    content_by_lua '
      local httpipe = require "resty.httpipe"

      local hp, err = httpipe:new()
      if not hp then
          ngx.log(ngx.ERR, "failed to new httpipe: ", err)
          return ngx.exit(503)
      end

      hp:set_timeout(5 * 1000) -- 5 sec

      local res, err = hp:request("127.0.0.1", 9090, {
                                     method = "GET", path = "/echo" })
      if not res then
          ngx.log(ngx.ERR, "failed to request: ", err)
          return ngx.exit(503)
      end

      ngx.status = res.status

      for k, v in pairs(res.headers) do
          ngx.header[k] = v
      end

      ngx.say(res.body)
    ';
  }

  location /generic {
    content_by_lua '
      local cjson = require "cjson"
      local httpipe = require "resty.httpipe"

      local hp, err = httpipe:new(10) -- chunk_size = 10
      if not hp then
          ngx.log(ngx.ERR, "failed to new httpipe: ", err)
          return ngx.exit(503)
      end

      hp:set_timeout(5 * 1000) -- 5 sec

      local ok, err = hp:connect("127.0.0.1", 9090)
      if not ok then
          ngx.log(ngx.ERR, "failed to connect: ", err)
          return ngx.exit(503)
      end

      local ok, err = hp:send_request{ method = "GET", path = "/echo" }
      if not ok then
          ngx.log(ngx.ERR, "failed to send request: ", err)
          return ngx.exit(503)
      end

      -- vollständiger Streaming-Parser

      while true do
          local typ, res, err = hp:read()
          if not typ then
              ngx.say("failed to read: ", err)
              return
          end

          ngx.say("read: ", cjson.encode({typ, res}))

          if typ == 'eof' then
              break
          end
      end
    ';
  }

  location /advanced {
    content_by_lua '
      local httpipe = require "resty.httpipe"

      local hp, err = httpipe:new()

      hp:set_timeout(5 * 1000) -- 5 sec

      local r0, err = hp:request("127.0.0.1", 9090, {
                                     method = "GET", path = "/echo",
                                     stream = true })

      -- von einem HTTP-Stream zu einem anderen, wie eine Unix-Pipe

      local pipe = r0.pipe

      pipe:set_timeout(5 * 1000) -- 5 sec

      --[[
          local headers = {["Content-Length"] = r0.headers["Content-Length"]}
          local r1, err = pipe:request("127.0.0.1", 9090, {
                                           method = "POST", path = "/echo",
                                           headers = headers,
                                           body = r0.body_reader })
      --]]
      local r1, err = pipe:request("127.0.0.1", 9090, {
                                       method = "POST", path = "/echo" })

      ngx.status = r1.status

      for k, v in pairs(r1.headers) do
          ngx.header[k] = v
      end

      ngx.say(r1.body)
    ';
  }

}

Eine typische Ausgabe der oben definierten /simple-Location ist:

GET /echo HTTP/1.1
Host: 127.0.0.1
User-Agent: Resty/HTTPipe-1.00
Accept: */*

Eine typische Ausgabe der oben definierten /generic-Location ist:

read: ["statusline","200"]
read: ["header",["Server","openresty\/1.5.12.1","Server: openresty\/1.5.12.1"]]
read: ["header",["Date","Tue, 10 Jun 2014 07:29:57 GMT","Date: Tue, 10 Jun 2014 07:29:57 GMT"]]
read: ["header",["Content-Type","text\/plain","Content-Type: text\/plain"]]
read: ["header",["Connection","keep-alive","Connection: keep-alive"]]
read: ["header",["Content-Length","84","Content-Length: 84"]]
read: ["header_end"]
read: ["body","GET \/echo "]
read: ["body","HTTP\/1.1\r\n"]
read: ["body","Host: 127."]
read: ["body","0.0.1\r\nUse"]
read: ["body","r-Agent: R"]
read: ["body","esty\/HTTPi"]
read: ["body","pe-1.00\r\nA"]
read: ["body","ccept: *\/*"]
read: ["body","\r\n\r\n"]
read: ["body_end"]
read: ["eof"]

Eine typische Ausgabe der oben definierten /advanced-Location ist:

POST /echo HTTP/1.1
Content-Length: 84
User-Agent: Resty/HTTPipe-1.00
Accept: */*
Host: 127.0.0.1

GET /echo HTTP/1.1
Host: 127.0.0.1
User-Agent: Resty/HTTPipe-1.00
Accept: */*

Verbindung

new

syntax: hp, err = httpipe:new(chunk_size?, sock?)

Erstellt das httpipe-Objekt. Im Falle von Fehlern gibt es nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

Das Argument chunk_size gibt die Puffergröße an, die von Cosocket-Leseoperationen verwendet wird. Standardmäßig 8192.

connect

syntax: ok, err = hp:connect(host, port, options_table?)

syntax: ok, err = hp:connect("unix:/path/to/unix.sock", options_table?)

Versucht, eine Verbindung zum Webserver herzustellen.

Bevor der Hostname tatsächlich aufgelöst und eine Verbindung zum Remote-Backend hergestellt wird, sucht diese Methode immer im Verbindungspool nach passenden inaktiven Verbindungen, die durch vorherige Aufrufe dieser Methode erstellt wurden.

Eine optionale Lua-Tabelle kann als letztes Argument dieser Methode angegeben werden, um verschiedene Verbindungsoptionen festzulegen:

  • pool : Gibt einen benutzerdefinierten Namen für den verwendeten Verbindungspool an. Wenn weggelassen, wird der Name des Verbindungspools aus der Zeichenfolgenvorlage <host>:<port> oder <unix-socket-path> generiert.

set_timeout

syntax: hp:set_timeout(time)

Setzt den Timeout (in ms) für nachfolgende Operationen, einschließlich der connect-Methode.

ssl_handshake

syntax: hp:ssl_handshake(reused_session?, server_name?, ssl_verify?)

Führt das SSL/TLS-Handshake über die derzeit etablierte Verbindung durch.

Siehe mehr: http://wiki.nginx.org/HttpLuaModule#tcpsock:sslhandshake

set_keepalive

syntax: ok, err = hp:set_keepalive(max_idle_timeout, pool_size)

Versucht, die aktuelle Verbindung in den ngx_lua-Cosocket-Verbindungspool zu legen.

Hinweis Normalerweise wird es automatisch nach der Verarbeitung der Anfrage aufgerufen. Mit anderen Worten, wir können die Verbindung nicht wieder in den Pool zurückgeben, es sei denn, Sie konsumieren alle Daten.

Sie können das maximale Leerlauf-Timeout (in ms) angeben, wenn die Verbindung im Pool ist, und die maximale Größe des Pools für jeden NGINX-Worker-Prozess.

Im Erfolgsfall gibt es 1 zurück. Im Fehlerfall gibt es nil mit einer Zeichenfolge zurück, die den Fehler beschreibt.

get_reused_times

syntax: times, err = hp:get_reused_times()

Diese Methode gibt die (erfolgreich) wiederverwendeten Zeiten für die aktuelle Verbindung zurück. Im Fehlerfall gibt es nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

Wenn die aktuelle Verbindung nicht aus dem integrierten Verbindungspool stammt, gibt diese Methode immer 0 zurück, d.h. die Verbindung wurde (noch) nie wiederverwendet. Wenn die Verbindung aus dem Verbindungspool stammt, ist der Rückgabewert immer ungleich null. Diese Methode kann also auch verwendet werden, um zu bestimmen, ob die aktuelle Verbindung aus dem Pool stammt.

close

syntax: ok, err = hp:close()

Schließt die aktuelle Verbindung und gibt den Status zurück.

Im Erfolgsfall gibt es 1 zurück. Im Fehlerfall gibt es nil mit einer Zeichenfolge zurück, die den Fehler beschreibt.

Anfragen

request

syntax: res, err = hp:request(opts?)

syntax: res, err = hp:request(host, port, opts?)

syntax: res, err = hp:request("unix:/path/to/unix-domain.socket", opts?)

Die opts-Tabelle akzeptiert die folgenden Felder:

  • version: Setzt die HTTP-Version. Verwenden Sie 10 für HTTP/1.0 und 11 für HTTP/1.1. Standardmäßig 11.
  • method: Die HTTP-Methodenzeichenfolge. Standardmäßig GET.
  • path: Die Pfadzeichenfolge. Standardmäßig /.
  • query: Gibt Abfrageparameter an. Akzeptiert entweder eine Zeichenfolge oder eine Lua-Tabelle.
  • headers: Eine Tabelle von Anfrage-Headern. Akzeptiert eine Lua-Tabelle.
  • body: Der Anfragekörper als Zeichenfolge oder eine Iteratorfunktion.
  • read_timeout: Setzt das Timeout in Millisekunden für Netzwerk-Leseoperationen speziell.
  • send_timeout: Setzt das Timeout in Millisekunden für Netzwerk-Sendeoperationen speziell.
  • stream: Wenn auf true gesetzt, gibt ein iterierbares res.body_reader-Objekt anstelle von res.body zurück.
  • maxsize: Setzt die maximale Größe in Bytes, die abgerufen werden soll. Ein Antwortkörper, der größer als dies ist, führt dazu, dass die Funktion einen exceeds maxsize-Fehler zurückgibt. Standardmäßig nil, was bedeutet, dass es keine Begrenzung gibt.
  • ssl_verify: Ein Lua-Boolescher Wert, um zu steuern, ob eine SSL-Überprüfung durchgeführt werden soll.

Wenn die Anfrage erfolgreich ist, enthält res die folgenden Felder:

  • res.status (Zahl): Der Antwortstatus, z.B. 200
  • res.headers (Tabelle): Eine Lua-Tabelle mit Antwort-Headern.
  • res.body (Zeichenfolge): Der einfache Antwortkörper.
  • res.body_reader (Funktion): Eine Iteratorfunktion zum Lesen des Körpers in einem Streaming-Verfahren.
  • res.pipe (httpipe): Eine neue HTTP-Pipe, die standardmäßig den aktuellen body_reader als Eingabekörper verwendet.

Hinweis Alle Header (Anfrage und Antwort) werden für die Großschreibung normalisiert - z.B. Accept-Encoding, ETag, Foo-Bar, Baz - im normalen HTTP-"Standard."

Im Fehlerfall gibt es nil mit einer Zeichenfolge zurück, die den Fehler beschreibt.

request_uri

syntax: res, err = hp:request_uri(uri, opts?)

Die einfache Schnittstelle. Optionen, die in der opts-Tabelle bereitgestellt werden, sind die gleichen wie in der generischen Schnittstelle und überschreiben Komponenten, die in der URI selbst gefunden werden.

Gibt ein res-Objekt zurück, das dasselbe ist wie die hp:request-Methode.

Im Fehlerfall gibt es nil mit einer Zeichenfolge zurück, die den Fehler beschreibt.

res.body_reader

Der body_reader-Iterator kann verwendet werden, um den Antwortkörper in von Ihnen gewählten Chunk-Größen zu streamen, wie folgt:

local reader = res.body_reader

repeat
  local chunk, err = reader(8192)
  if err then
    ngx.log(ngx.ERR, err)
    break
  end

  if chunk then
    -- verarbeiten
  end
until not chunk

send_request

syntax: ok, err = hp:send_request(opts?)

Im Fehlerfall gibt es nil mit einer Zeichenfolge zurück, die den Fehler beschreibt.

read_response

syntax: local res, err = hp:read_response(callback?)

Die callback-Tabelle akzeptiert die folgenden Felder:

  • header_filter: Eine Callback-Funktion für die Filterung von Antwort-Headern
local res, err = hp:read_response{
    header_filter = function (status, headers)
        if status == 200 then
            return 1
        end
end }
  • body_filter: Eine Callback-Funktion für die Filterung des Antwortkörpers
local res, err = hp:read_response{
    body_filter = function (chunk)
        ngx.print(chunk)
    end
}

Zusätzlich gibt es keine Möglichkeit, den Antwortkörper in dieser Methode zu streamen. Wenn die Antwort erfolgreich ist, enthält res die folgenden Felder: res.status, res.headers, res.body.

Hinweis Wenn in der Callback-Funktion true zurückgegeben wird, wird der Filterprozess unterbrochen.

Im Fehlerfall gibt es nil mit einer Zeichenfolge zurück, die den Fehler beschreibt.

read

syntax: local typ, res, err = hp:read()

Streaming-Parser für die vollständige Antwort.

Der Benutzer muss einfach die Lese-Methode wiederholt aufrufen, bis ein nil-Token-Typ zurückgegeben wird. Für jedes Token, das von der Lese-Methode zurückgegeben wird, überprüfen Sie einfach den ersten Rückgabewert für den aktuellen Token-Typ. Der Token-Typ kann statusline, header, header_end, body, body_end und eof sein. Zum Format des res-Wertes siehe das obige Beispiel. Zum Beispiel halten mehrere Körper-Token jeweils einen Körperdatenchunk, sodass der res-Wert dem Körperdatenchunk entspricht.

Im Fehlerfall gibt es nil mit einer Zeichenfolge zurück, die den Fehler beschreibt.

eof

syntax: local eof = hp:eof()

Wenn true zurückgegeben wird, bedeutet dies, dass bereits alle Daten konsumiert wurden; andernfalls gibt es noch kein Ende der Anfrage, und Sie müssen hp:close aufrufen, um die Verbindung gewaltsam zu schließen.

Dienstprogramm

parse_uri

syntax: local scheme, host, port, path, args = unpack(hp:parse_uri(uri))

Dies ist eine Hilfsfunktion, die es einfacher macht, die generische Schnittstelle zu verwenden, wenn die Eingabedaten eine URI sind.

get_client_body_reader

syntax: reader, err = hp:get_client_body_reader(chunk_size?)

Gibt eine Iteratorfunktion zurück, die verwendet werden kann, um den Anfragekörper des nachgelagerten Clients in einem Streaming-Verfahren zu lesen. Zum Beispiel:

local req_reader = hp:get_client_body_reader()

repeat
  local chunk, err = req_reader(8192)
  if err then
    ngx.log(ngx.ERR, err)
    break
  end

  if chunk then
    -- verarbeiten
  end
until not chunk

Dieser Iterator kann auch als Wert für das Feld body in den Anfrageparametern verwendet werden, sodass der Anfragekörper in eine proxied Upstream-Anfrage gestreamt werden kann.

local client_body_reader, err = hp:get_client_body_reader()

local res, err = hp:request{
   path = "/helloworld",
   body = client_body_reader,
}

GitHub

Sie finden möglicherweise zusätzliche Konfigurationstipps und Dokumentationen für dieses Modul im GitHub-Repository für nginx-module-httpipe.