lua-resty-http

OpenResty/ngx_lua 的 Lua HTTP 客户端 cosocket 驱动

$ opm get agentzh/lua-resty-http

lua-resty-http

用于 OpenResty / ngx_lua 的 Lua HTTP 客户端 cosocket 驱动。

状态

生产就绪。

特性

  • HTTP 1.0 和 1.1

  • SSL

  • 响应体的流式接口,用于可预测的内存使用

  • 用于单次请求的替代简单接口,无需手动连接步骤

  • 分块和非分块传输编码

  • 保持连接

  • 流水线

  • 尾部字段

API

摘要

`` lua lua_package_path "/path/to/lua-resty-http/lib/?.lua;;";

server {

location /simpleinterface { resolver 8.8.8.8; # 例如使用谷歌的公开 DNS 服务器

    content_by_lua '

      -- For simple singleshot requests, use the URI interface.
      local http = require "resty.http"
      local httpc = http.new()
      local res, err = httpc:request_uri("http://example.com/helloworld", {
        method = "POST",
        body = "a=1&b=2",
        headers = {
          ["Content-Type"] = "application/x-www-form-urlencoded",
        }
      })

      if not res then
        ngx.say("failed to request: ", err)
        return
      end

      -- In this simple form, there is no manual connection step, so the body is read
      -- all in one go, including any trailers, and the connection closed or keptalive
      -- for you.

      ngx.status = res.status

      for k,v in pairs(res.headers) do
          --
      end

      ngx.say(res.body)
    ';
}

location /genericinterface { content_by_lua '

      local http = require "resty.http"
      local httpc = http.new()

      -- The generic form gives us more control. We must connect manually.
      httpc:set_timeout(500)
      httpc:connect("127.0.0.1", 80)

      -- And request using a path, rather than a full URI.
      local res, err = httpc:request{
          path = "/helloworld",
          headers = {
              ["Host"] = "example.com",
          },
      }

      if not res then
        ngx.say("failed to request: ", err)
        return
      end

      -- Now we can use the body_reader iterator, to stream the body according to our desired chunk size.
      local reader = res.body_reader

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

        if chunk then
          -- process
        end
      until not chunk

      local ok, err = httpc:set_keepalive()
      if not ok then
        ngx.say("failed to set keepalive: ", err)
        return
      end
    ';
}
}
C<>``

连接

新建

语法: httpc = http.new()

创建 http 对象。如果失败,则返回 nil 和描述错误的字符串。

连接

语法: ok, err = httpc:connect(host, port, options_table?)

语法: ok, err = httpc:connect("unix:/path/to/unix.sock", options_table?)

尝试连接到 Web 服务器。

在实际解析主机名并连接到远程后端之前,此方法将始终查找由先前对此方法的调用创建的匹配空闲连接的连接池。

可以将一个可选的 Lua 表格指定为此方法的最后一个参数,以指定各种连接选项

  • pool : 指定正在使用的连接池的自定义名称。如果省略,则连接池名称将从字符串模板 <host>:<port><unix-socket-path> 生成。

设置超时

语法: httpc:set_timeout(time)

设置后续操作(包括 connect 方法)的超时(以毫秒为单位)保护。

SSL 握手

语法: session, err = httpc:ssl_handshake(session, host, verify)

在 TCP 连接上执行 SSL 握手,仅在 ngx_lua > v0.9.11 中可用

有关详细信息,请参阅 ngx.socket.tcp 的文档。

设置保持连接

语法: ok, err = httpc:set_keepalive(max_idle_timeout, pool_size)

尝试将当前连接放入 ngx_lua cosocket 连接池中。

您可以指定连接在池中的最大空闲超时时间(以毫秒为单位)以及每个 nginx 工作进程池的最大大小。

仅在您本来会调用 close 方法的地方调用此方法。调用此方法会立即将当前 http 对象置于 closed 状态。除了 connect() 之外的任何后续操作都将返回 closed 错误。

请注意,代替 close 调用此方法是“安全”的,因为它将根据请求类型有条件地关闭。具体来说,没有 Connection: Keep-Alive1.0 请求将被关闭,具有 Connection: Close1.1 请求也将被关闭。

如果成功,则返回 1。如果出错,则返回 nil, err。在如上所述有条件地关闭连接的情况下,返回 2 和错误字符串 connection must be closed

获取重用次数

语法: times, err = httpc:get_reused_times()

此方法返回当前连接的(成功)重用次数。如果出错,则返回 nil 和描述错误的字符串。

如果当前连接不是来自内置连接池,则此方法始终返回 0,即连接从未被重用(尚未)。如果连接来自连接池,则返回值始终非零。因此,此方法也可用于确定当前连接是否来自池。

关闭

语法: ok, err = http:close()

关闭当前连接并返回状态。

如果成功,则返回 1。如果出错,则返回 nil 和描述错误的字符串。

请求

请求

语法: res, err = httpc:request(params)

返回一个 res 表格或 nil 和错误消息。

params 表格接受以下字段

  • version HTTP 版本号,目前支持 1.0 或 1.1。

  • method HTTP 方法字符串。

  • path 路径字符串。

  • headers 请求头的表格。

  • body 请求主体作为字符串,或迭代器函数(请参阅 get_client_body_reader)。

  • ssl_verify 验证 SSL 证书是否与主机名匹配

如果请求成功,则 res 将包含以下字段

  • status 状态代码。

  • reason 状态原因短语。

  • headers 头部的表格。具有相同字段名称的多个头部将显示为值的表格。

  • has_body 一个布尔标志,指示是否存在要读取的主体。

  • body_reader 用于以流式方式读取主体的迭代器函数。

  • read_body 用于将整个主体读取到字符串中的方法。

  • read_trailers 用于在读取主体后将任何尾部字段合并到头部下方的方法。

请求 URI

语法: res, err = httpc:request_uri(uri, params)

简单的接口。params 表格中提供的选项与通用接口中的选项相同,并将覆盖在 uri 本身中找到的组件。

在此模式下,无需先手动连接。连接将代表您建立,适用于您只需要获取 URI 而无需太多麻烦的情况。

此外,在此模式下无法流式传输响应主体。如果请求成功,则 res 将包含以下字段

  • status 状态代码。

  • headers 头部的表格。

  • body 响应主体作为字符串。

请求流水线

语法: responses, err = httpc:request_pipeline(params)

此方法的工作方式与上面的 request 方法相同,但 params 却是一个 param 表格的表格。每个请求按顺序发送,并且 responses 作为响应句柄的表格返回。例如

    local responses = httpc:request_pipeline{
      {
        path = "/b",
      },
      {
        path = "/c",
      },
      {
        path = "/d",
      }
    }
    
    for i,r in ipairs(responses) do
      if r.status then
        ngx.say(r.status)
        ngx.say(r:read_body())
      end
    end

由于流水线的性质,在您尝试使用响应字段(状态/头部等)之前,实际上不会读取任何响应。并且由于响应按顺序读取,因此您必须读取整个主体(以及您拥有的任何尾部字段),然后才能尝试读取下一个响应。

请注意,这并不排除使用流式响应主体读取器。只要在尝试访问下一个响应之前流式传输了整个主体,响应仍然可以流式传输。

在尝试使用其他字段之前,请务必至少测试一个字段(例如状态),以防发生套接字读取错误。

响应

res.body_reader

body_reader 迭代器可用于以您选择的块大小流式传输响应主体,如下所示

``lua local reader = res.body_reader

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

if chunk then -- 处理 end until not chunk ``

如果迭代器在不带参数的情况下被调用,则行为取决于连接类型。如果响应被编码为分块,则迭代器将在它们到达时返回块。否则,它将简单地返回整个主体。

请注意,提供的 size 实际上是 **最大** size。因此,在分块传输的情况下,您可能会获得小于您要求的 size 的块,作为实际 HTTP 块的剩余部分。

res:read_body

语法: body, err = res:read_body()

将整个主体读取到本地字符串中。

res:read_trailers

语法: res:read_trailers()

这会将任何尾部字段合并到 res.headers 表格本身的下方。必须在读取主体后调用。

代理

当一个人只想将当前请求代理到已连接的上游,并安全地将其发送到下游到客户端作为反向代理时,有两个便捷方法。完整的示例

    local http = require "resty.http"
    local httpc = http.new()
    
    httpc:set_timeout(500)
    local ok, err = httpc:connect(HOST, PORT)
    
    if not ok then
      ngx.log(ngx.ERR, err)
      return
    end
    
    httpc:set_timeout(2000)
    httpc:proxy_response(httpc:proxy_request())
    httpc:set_keepalive()

代理请求

语法: local res, err = httpc:proxy_request(request_body_chunk_size?)

使用当前客户端请求参数执行请求,有效地代理到已连接的上游。请求主体将以流式方式读取,根据 request_body_chunk_size(请参阅下面的 “客户端主体读取器的文档”)。

代理响应

语法: httpc:proxy_response(res, chunksize?)

根据给定的 res 设置当前响应。确保不会将逐跳头部发送到下游,并将根据 chunksize 读取响应(请参阅上面的 “主体读取器的文档”)。

实用程序

解析 URI

语法: local scheme, host, port, path = unpack(httpc:parse_uri(uri))

这是一个便捷函数,允许人们在输入数据为 URI 时更轻松地使用通用接口。

获取客户端主体读取器

语法: reader, err = httpc:get_client_body_reader(chunksize?, sock?)

返回一个迭代器函数,该函数可用于以流式方式读取下游客户端请求主体。您还可以指定一个可选的默认块大小(默认为 65536),或在客户端请求中指定一个已建立的套接字。

示例

    local req_reader = httpc:get_client_body_reader()
    
    repeat
      local chunk, err = req_reader(8192)
      if err then
        ngx.log(ngx.ERR, err)
        break
      end
    
      if chunk then
        -- process
      end
    until not chunk

此迭代器也可作为请求参数中主体字段的值使用,允许将请求主体流式传输到代理的上游请求中。

    local client_body_reader, err = httpc:get_client_body_reader()
    
    local res, err = httpc:request{
       path = "/helloworld",
       body = client_body_reader,
    }

如果指定了 sock

作者

James Hurst <[email protected]>

最初基于 https://github.com/bakins/lua-resty-http-simple 启动。从其他 lua-resty-* cosocket 模块借用了 cosocket 文档和实现。

许可证

此模块根据 2 条款 BSD 许可证许可。

版权所有 (c) 2013-2016,James Hurst <[email protected]>

保留所有权利。

在满足以下条件的情况下,允许以源代码和二进制形式重新分发和使用,无论是否修改:

  • 源代码的再分发必须保留上述版权声明、此条件列表和以下免责声明。

  • 二进制形式的再分发必须在随分发提供的文档和/或其他材料中复制上述版权声明、此条件列表和以下免责声明。

本软件由版权持有人和贡献者“按原样”提供,并且不提供任何明示或暗示的担保,包括但不限于适销性和特定用途适用性的暗示担保。在任何情况下,版权持有人或贡献者均不对任何直接、间接、偶然、特殊、惩戒性或后果性损害(包括但不限于替代商品或服务的采购;使用、数据或利润损失;或业务中断)负责,无论此类损害是如何造成的以及在何种责任理论下(无论是合同、严格责任或侵权行为(包括疏忽或其他)),即使已被告知此类损害的可能性。

作者

James Hurst

许可证

2bsd

版本