Merge commit '7160fd94e3dc22299ee3c9f8b0e71a5e2c1bb501' as 'src/deps/src/lua-resty-http'

This commit is contained in:
Théophile Diot 2023-06-30 15:38:34 -04:00
commit e566b98afc
41 changed files with 7478 additions and 0 deletions

View File

@ -0,0 +1 @@
*.t linguist-language=lua

View File

@ -0,0 +1 @@
github: pintsized

View File

@ -0,0 +1,62 @@
name: Test
on: [push, pull_request]
jobs:
luacheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: leafo/gh-actions-lua@v8
with:
luaVersion: "luajit-openresty"
- uses: leafo/gh-actions-luarocks@v4
- run: luarocks install luacheck
- run: luacheck lib
run_tests:
strategy:
matrix:
openresty_version:
- 1.17.8.2
- 1.19.9.1
runs-on: ubuntu-latest
container:
image: openresty/openresty:${{ matrix.openresty_version }}-alpine-fat
# --init runs tinit as PID 1 and prevents the 'WARNING: killing the child process' spam from the test suite
options: --init
steps:
- uses: actions/checkout@v2
- name: Install deps
run: |
apk add --no-cache curl perl bash wget git perl-dev libarchive-tools nodejs
ln -s /usr/bin/bsdtar /usr/bin/tar
- name: Install CPAN
run: curl -s -L http://xrl.us/cpanm > /bin/cpanm && chmod +x /bin/cpanm
- name: Cache
uses: actions/cache@v2
with:
path: |
~/.cpan
~/.cache
key: ${{ runner.os }}-${{ matrix.openresty_version }}-cache
- name: Install Test::Nginx
run: cpanm -q -n Test::Nginx
- name: Install Luacov
run: /usr/local/openresty/luajit/bin/luarocks install luacov
- uses: actions/checkout@v2
- name: Run tests
run: make coverage
- name: Coverage
run: |
luacov
tail -n 8 luacov.report.out

View File

@ -0,0 +1,6 @@
t/servroot/
t/error.log
luacov*
lua-resty-http-*/
lua-resty-http-*.src.rock
lua-resty-http-*.tar.gz

View File

@ -0,0 +1,2 @@
std = "ngx_lua"
redefined = false

View File

@ -0,0 +1,5 @@
modules = {
["http"] = "lib/resty/http.lua",
["http_headers"] = "lib/resty/http_headers.lua",
["http_connect"] = "lib/resty/http_connect.lua",
}

View File

@ -0,0 +1,23 @@
Copyright (c) 2013, James Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,33 @@
OPENRESTY_PREFIX=/usr/local/openresty
PREFIX ?= /usr/local
LUA_INCLUDE_DIR ?= $(PREFIX)/include
LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION)
INSTALL ?= install
TEST_FILE ?= t
export PATH := $(OPENRESTY_PREFIX)/nginx/sbin:$(PATH)
PROVE=TEST_NGINX_NO_SHUFFLE=1 prove -I../test-nginx/lib -r $(TEST_FILE)
# Keep in sync with .luacov, so that we show the right amount of output
LUACOV_NUM_MODULES ?= 3
.PHONY: all test install
all: ;
install: all
$(INSTALL) -d $(DESTDIR)/$(LUA_LIB_DIR)/resty
$(INSTALL) lib/resty/*.lua $(DESTDIR)/$(LUA_LIB_DIR)/resty/
test: all
$(PROVE)
coverage: all
@rm -f luacov.stats.out
TEST_COVERAGE=1 $(PROVE)
@luacov
@tail -$$(( $(LUACOV_NUM_MODULES) + 5)) luacov.report.out
check:
luacheck lib

View File

@ -0,0 +1,475 @@
# lua-resty-http
Lua HTTP client cosocket driver for [OpenResty](http://openresty.org/) / [ngx\_lua](https://github.com/openresty/lua-nginx-module).
## Status
Production ready.
[![Test](https://github.com/ledgetech/lua-resty-http/actions/workflows/test.yml/badge.svg)](https://github.com/ledgetech/lua-resty-http/actions)
## Features
* HTTP 1.0 and 1.1
* SSL
* Streaming interface to the response body, for predictable memory usage
* Alternative simple interface for single-shot requests without a manual connection step
* Chunked and non-chunked transfer encodings
* Connection keepalives
* Request pipelining
* Trailers
* HTTP proxy connections
* mTLS (requires `ngx_lua_http_module` >= v0.10.23)
## API
* [new](#new)
* [connect](#connect)
* [set\_proxy\_options](#set_proxy_options)
* [set\_timeout](#set_timeout)
* [set\_timeouts](#set_timeouts)
* [set\_keepalive](#set_keepalive)
* [get\_reused\_times](#get_reused_times)
* [close](#close)
* [request](#request)
* [request\_uri](#request_uri)
* [request\_pipeline](#request_pipeline)
* [parse\_uri](#parse_uri)
* [get\_client\_body\_reader](#get_client_body_reader)
* [Response](#response)
* [body\_reader](#resbody_reader)
* [read\_body](#resread_body)
* [read\_trailers](#resread_trailers)
### Deprecated
These methods may be removed in future versions.
* [connect\_proxy](#connect_proxy)
* [ssl\_handshake](#ssl_handshake)
* [proxy\_request](#proxy_request)
* [proxy\_response](#proxy_response)
## Usage
There are two basic modes of operation:
1. **Simple single-shot requests** which require no manual connection management but which buffer the entire response and leave the connection either closed or back in the connection pool.
2. **Streamed requests** where the connection is established separately, then the request is sent, the body stream read in chunks, and finally the connection is manually closed or kept alive. This technique requires a little more code but provides the ability to discard potentially large response bodies on the Lua side, as well as pipelining multiple requests over a single connection.
### Single-shot request
```lua
local httpc = require("resty.http").new()
-- Single-shot requests use the `request_uri` interface.
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.log(ngx.ERR, "request failed: ", err)
return
end
-- At this point, the entire request / response is complete and the connection
-- will be closed or back on the connection pool.
-- The `res` table contains the expeected `status`, `headers` and `body` fields.
local status = res.status
local length = res.headers["Content-Length"]
local body = res.body
```
### Streamed request
```lua
local httpc = require("resty.http").new()
-- First establish a connection
local ok, err, ssl_session = httpc:connect({
scheme = "https",
host = "127.0.0.1",
port = 8080,
})
if not ok then
ngx.log(ngx.ERR, "connection failed: ", err)
return
end
-- Then send using `request`, supplying a path and `Host` header instead of a
-- full URI.
local res, err = httpc:request({
path = "/helloworld",
headers = {
["Host"] = "example.com",
},
})
if not res then
ngx.log(ngx.ERR, "request failed: ", err)
return
end
-- At this point, the status and headers will be available to use in the `res`
-- table, but the body and any trailers will still be on the wire.
-- We can use the `body_reader` iterator, to stream the body according to our
-- desired buffer size.
local reader = res.body_reader
local buffer_size = 8192
repeat
local buffer, err = reader(buffer_size)
if err then
ngx.log(ngx.ERR, err)
break
end
if buffer then
-- process
end
until not buffer
local ok, err = httpc:set_keepalive()
if not ok then
ngx.say("failed to set keepalive: ", err)
return
end
-- At this point, the connection will either be safely back in the pool, or closed.
````
# Connection
## new
`syntax: httpc, err = http.new()`
Creates the HTTP connection object. In case of failures, returns `nil` and a string describing the error.
## connect
`syntax: ok, err, ssl_session = httpc:connect(options)`
Attempts to connect to the web server while incorporating the following activities:
- TCP connection
- SSL handshake
- HTTP proxy configuration
In doing so it will create a distinct connection pool name that is safe to use with SSL and / or proxy based connections, and as such this syntax is strongly recommended over the original (now deprecated) [TCP only connection syntax](#TCP-only-connect).
The options table has the following fields:
* `scheme`: scheme to use, or nil for unix domain socket
* `host`: target host, or path to a unix domain socket
* `port`: port on target host, will default to `80` or `443` based on the scheme
* `pool`: custom connection pool name. Option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsockconnect), except that the default will become a pool name constructed using the SSL / proxy properties, which is important for safe connection reuse. When in doubt, leave it blank!
* `pool_size`: option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsockconnect)
* `backlog`: option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsockconnect)
* `proxy_opts`: sub-table, defaults to the global proxy options set, see [set\_proxy\_options](#set_proxy_options).
* `ssl_reused_session`: option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
* `ssl_verify`: option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake), except that it defaults to `true`.
* `ssl_server_name`: option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
* `ssl_send_status_req`: option as per [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake)
* `ssl_client_cert`: will be passed to `tcpsock:setclientcert`. Requires `ngx_lua_http_module` >= v0.10.23.
* `ssl_client_priv_key`: as above.
## set\_timeout
`syntax: httpc:set_timeout(time)`
Sets the socket timeout (in ms) for subsequent operations. See [set\_timeouts](#set_timeouts) below for a more declarative approach.
## set\_timeouts
`syntax: httpc:set_timeouts(connect_timeout, send_timeout, read_timeout)`
Sets the connect timeout threshold, send timeout threshold, and read timeout threshold, respectively, in milliseconds, for subsequent socket operations (connect, send, receive, and iterators returned from receiveuntil).
## set\_keepalive
`syntax: ok, err = httpc:set_keepalive(max_idle_timeout, pool_size)`
Either places the current connection into the pool for future reuse, or closes the connection. Calling this instead of [close](#close) is "safe" in that it will conditionally close depending on the type of request. Specifically, a `1.0` request without `Connection: Keep-Alive` will be closed, as will a `1.1` request with `Connection: Close`.
In case of success, returns `1`. In case of errors, returns `nil, err`. In the case where the connection is conditionally closed as described above, returns `2` and the error string `connection must be closed`, so as to distinguish from unexpected errors.
See [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsocksetkeepalive) for parameter documentation.
## set\_proxy\_options
`syntax: httpc:set_proxy_options(opts)`
Configure an HTTP proxy to be used with this client instance. The `opts` table expects the following fields:
* `http_proxy`: an URI to a proxy server to be used with HTTP requests
* `http_proxy_authorization`: a default `Proxy-Authorization` header value to be used with `http_proxy`, e.g. `Basic ZGVtbzp0ZXN0`, which will be overriden if the `Proxy-Authorization` request header is present.
* `https_proxy`: an URI to a proxy server to be used with HTTPS requests
* `https_proxy_authorization`: as `http_proxy_authorization` but for use with `https_proxy` (since with HTTPS the authorisation is done when connecting, this one cannot be overridden by passing the `Proxy-Authorization` request header).
* `no_proxy`: a comma separated list of hosts that should not be proxied.
Note that this method has no effect when using the deprecated [TCP only connect](#TCP-only-connect) connection syntax.
## get\_reused\_times
`syntax: times, err = httpc:get_reused_times()`
See [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsockgetreusedtimes).
## close
`syntax: ok, err = httpc:close()`
See [OpenResty docs](https://github.com/openresty/lua-nginx-module#tcpsockclose).
# Requesting
## request
`syntax: res, err = httpc:request(params)`
Sends an HTTP request over an already established connection. Returns a `res` table or `nil` and an error message.
The `params` table expects the following fields:
* `version`: The HTTP version number. Defaults to `1.1`.
* `method`: The HTTP method string. Defaults to `GET`.
* `path`: The path string. Defaults to `/`.
* `query`: The query string, presented as either a literal string or Lua table..
* `headers`: A table of request headers.
* `body`: The request body as a string, a table of strings, or an iterator function yielding strings until nil when exhausted. Note that you must specify a `Content-Length` for the request body, or specify `Transfer-Encoding: chunked` and have your function implement the encoding. See also: [get\_client\_body\_reader](#get_client_body_reader)).
When the request is successful, `res` will contain the following fields:
* `status`: The status code.
* `reason`: The status reason phrase.
* `headers`: A table of headers. Multiple headers with the same field name will be presented as a table of values.
* `has_body`: A boolean flag indicating if there is a body to be read.
* `body_reader`: An iterator function for reading the body in a streaming fashion.
* `read_body`: A method to read the entire body into a string.
* `read_trailers`: A method to merge any trailers underneath the headers, after reading the body.
If the response has a body, then before the same connection can be used for another request, you must read the body using `read_body` or `body_reader`.
## request\_uri
`syntax: res, err = httpc:request_uri(uri, params)`
The single-shot interface (see [usage](#Usage)). Since this method performs an entire end-to-end request, options specified in the `params` can include anything found in both [connect](#connect) and [request](#request) documented above. Note also that fields `path`, and `query`, in `params` will override relevant components of the `uri` if specified (`scheme`, `host`, and `port` will always be taken from the `uri`).
There are 3 additional parameters for controlling keepalives:
* `keepalive`: Set to `false` to disable keepalives and immediately close the connection. Defaults to `true`.
* `keepalive_timeout`: The maximal idle timeout (ms). Defaults to `lua_socket_keepalive_timeout`.
* `keepalive_pool`: The maximum number of connections in the pool. Defaults to `lua_socket_pool_size`.
If the request is successful, `res` will contain the following fields:
* `status`: The status code.
* `headers`: A table of headers.
* `body`: The entire response body as a string.
## request\_pipeline
`syntax: responses, err = httpc:request_pipeline(params)`
This method works as per the [request](#request) method above, but `params` is instead a nested table of parameter tables. Each request is sent in order, and `responses` is returned as a table of response handles. For example:
```lua
local responses = httpc:request_pipeline({
{ path = "/b" },
{ path = "/c" },
{ path = "/d" },
})
for _, r in ipairs(responses) do
if not r.status then
ngx.log(ngx.ERR, "socket read error")
break
end
ngx.say(r.status)
ngx.say(r:read_body())
end
```
Due to the nature of pipelining, no responses are actually read until you attempt to use the response fields (status / headers etc). And since the responses are read off in order, you must read the entire body (and any trailers if you have them), before attempting to read the next response.
Note this doesn't preclude the use of the streaming response body reader. Responses can still be streamed, so long as the entire body is streamed before attempting to access the next response.
Be sure to test at least one field (such as status) before trying to use the others, in case a socket read error has occurred.
# Response
## res.body\_reader
The `body_reader` iterator can be used to stream the response body in chunk sizes of your choosing, as follows:
````lua
local reader = res.body_reader
local buffer_size = 8192
repeat
local buffer, err = reader(buffer_size)
if err then
ngx.log(ngx.ERR, err)
break
end
if buffer then
-- process
end
until not buffer
````
If the reader is called with no arguments, the behaviour depends on the type of connection. If the response is encoded as chunked, then the iterator will return the chunks as they arrive. If not, it will simply return the entire body.
Note that the size provided is actually a **maximum** size. So in the chunked transfer case, you may get buffers smaller than the size you ask, as a remainder of the actual encoded chunks.
## res:read\_body
`syntax: body, err = res:read_body()`
Reads the entire body into a local string.
## res:read\_trailers
`syntax: res:read_trailers()`
This merges any trailers underneath the `res.headers` table itself. Must be called after reading the body.
# Utility
## parse\_uri
`syntax: local scheme, host, port, path, query? = unpack(httpc:parse_uri(uri, query_in_path?))`
This is a convenience function allowing one to more easily use the generic interface, when the input data is a URI.
As of version `0.10`, the optional `query_in_path` parameter was added, which specifies whether the querystring is to be included in the `path` return value, or separately as its own return value. This defaults to `true` in order to maintain backwards compatibility. When set to `false`, `path` will only include the path, and `query` will contain the URI args, not including the `?` delimiter.
## get\_client\_body\_reader
`syntax: reader, err = httpc:get_client_body_reader(chunksize?, sock?)`
Returns an iterator function which can be used to read the downstream client request body in a streaming fashion. You may also specify an optional default chunksize (default is `65536`), or an already established socket in
place of the client request.
Example:
```lua
local req_reader = httpc:get_client_body_reader()
local buffer_size = 8192
repeat
local buffer, err = req_reader(buffer_size)
if err then
ngx.log(ngx.ERR, err)
break
end
if buffer then
-- process
end
until not buffer
```
This iterator can also be used as the value for the body field in request params, allowing one to stream the request body into a proxied upstream request.
```lua
local client_body_reader, err = httpc:get_client_body_reader()
local res, err = httpc:request({
path = "/helloworld",
body = client_body_reader,
})
```
# Deprecated
These features remain for backwards compatability, but may be removed in future releases.
### TCP only connect
*The following versions of the `connect` method signature are deprecated in favour of the single `table` argument [documented above](#connect).*
`syntax: ok, err = httpc:connect(host, port, options_table?)`
`syntax: ok, err = httpc:connect("unix:/path/to/unix.sock", options_table?)`
NOTE: the default pool name will only incorporate IP and port information so is unsafe to use in case of SSL and/or Proxy connections. Specify your own pool or, better still, do not use these signatures.
## connect\_proxy
`syntax: ok, err = httpc:connect_proxy(proxy_uri, scheme, host, port, proxy_authorization)`
*Calling this method manually is no longer necessary since it is incorporated within [connect](#connect). It is retained for now for compatibility with users of the older [TCP only connect](#TCP-only-connect) syntax.*
Attempts to connect to the web server through the given proxy server. The method accepts the following arguments:
* `proxy_uri` - Full URI of the proxy server to use (e.g. `http://proxy.example.com:3128/`). Note: Only `http` protocol is supported.
* `scheme` - The protocol to use between the proxy server and the remote host (`http` or `https`). If `https` is specified as the scheme, `connect_proxy()` makes a `CONNECT` request to establish a TCP tunnel to the remote host through the proxy server.
* `host` - The hostname of the remote host to connect to.
* `port` - The port of the remote host to connect to.
* `proxy_authorization` - The `Proxy-Authorization` header value sent to the proxy server via `CONNECT` when the `scheme` is `https`.
If an error occurs during the connection attempt, this method returns `nil` with a string describing the error. If the connection was successfully established, the method returns `1`.
There's a few key points to keep in mind when using this api:
* If the scheme is `https`, you need to perform the TLS handshake with the remote server manually using the `ssl_handshake()` method before sending any requests through the proxy tunnel.
* If the scheme is `http`, you need to ensure that the requests you send through the connections conforms to [RFC 7230](https://tools.ietf.org/html/rfc7230) and especially [Section 5.3.2.](https://tools.ietf.org/html/rfc7230#section-5.3.2) which states that the request target must be in absolute form. In practice, this means that when you use `send_request()`, the `path` must be an absolute URI to the resource (e.g. `http://example.com/index.html` instead of just `/index.html`).
## ssl\_handshake
`syntax: session, err = httpc:ssl_handshake(session, host, verify)`
*Calling this method manually is no longer necessary since it is incorporated within [connect](#connect). It is retained for now for compatibility with users of the older [TCP only connect](#TCP-only-connect) syntax.*
See [OpenResty docs](https://github.com/openresty/lua-nginx-module#ngxsockettcp).
## proxy\_request / proxy\_response
*These two convenience methods were intended simply to demonstrate a common use case of implementing reverse proxying, and the author regrets their inclusion in the module. Users are encouraged to roll their own rather than depend on these functions, which may be removed in a subsequent release.*
### proxy\_request
`syntax: local res, err = httpc:proxy_request(request_body_chunk_size?)`
Performs a request using the current client request arguments, effectively proxying to the connected upstream. The request body will be read in a streaming fashion, according to `request_body_chunk_size` (see [documentation on the client body reader](#get_client_body_reader) below).
### proxy\_response
`syntax: httpc:proxy_response(res, chunksize?)`
Sets the current response based on the given `res`. Ensures that hop-by-hop headers are not sent downstream, and will read the response according to `chunksize` (see [documentation on the body reader](#resbody_reader) above).
# Author
James Hurst <james@pintsized.co.uk>, with contributions from @hamishforbes, @Tieske, @bungle et al.
# Licence
This module is licensed under the 2-clause BSD license.
Copyright (c) James Hurst <james@pintsized.co.uk>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,9 @@
name=lua-resty-http
abstract=Lua HTTP client cosocket driver for OpenResty/ngx_lua
author=James Hurst
is_original=yes
license=2bsd
lib_dir=lib
doc_dir=lib
repo_link=https://github.com/ledgetech/lua-resty-http
main_module=lib/resty/http.lua

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,265 @@
local ngx_re_gmatch = ngx.re.gmatch
local ngx_re_sub = ngx.re.sub
local ngx_re_find = ngx.re.find
local ngx_log = ngx.log
local ngx_WARN = ngx.WARN
--[[
A connection function that incorporates:
- tcp connect
- ssl handshake
- http proxy
Due to this it will be better at setting up a socket pool where connections can
be kept alive.
Call it with a single options table as follows:
client:connect {
scheme = "https" -- scheme to use, or nil for unix domain socket
host = "myhost.com", -- target machine, or a unix domain socket
port = nil, -- port on target machine, will default to 80/443 based on scheme
pool = nil, -- connection pool name, leave blank! this function knows best!
pool_size = nil, -- options as per: https://github.com/openresty/lua-nginx-module#tcpsockconnect
backlog = nil,
-- ssl options as per: https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake
ssl_reused_session = nil
ssl_server_name = nil,
ssl_send_status_req = nil,
ssl_verify = true, -- NOTE: defaults to true
ctx = nil, -- NOTE: not supported
-- mTLS options: These require support for mTLS in cosockets, which first
-- appeared in `ngx_http_lua_module` v0.10.23.
ssl_client_cert = nil,
ssl_client_priv_key = nil,
proxy_opts, -- proxy opts, defaults to global proxy options
}
]]
local function connect(self, options)
local sock = self.sock
if not sock then
return nil, "not initialized"
end
local ok, err
local request_scheme = options.scheme
local request_host = options.host
local request_port = options.port
local poolname = options.pool
local pool_size = options.pool_size
local backlog = options.backlog
if request_scheme and not request_port then
request_port = (request_scheme == "https" and 443 or 80)
elseif request_port and not request_scheme then
return nil, "'scheme' is required when providing a port"
end
-- ssl settings
local ssl, ssl_reused_session, ssl_server_name
local ssl_verify, ssl_send_status_req, ssl_client_cert, ssl_client_priv_key
if request_scheme == "https" then
ssl = true
ssl_reused_session = options.ssl_reused_session
ssl_server_name = options.ssl_server_name
ssl_send_status_req = options.ssl_send_status_req
ssl_verify = true -- default
if options.ssl_verify == false then
ssl_verify = false
end
ssl_client_cert = options.ssl_client_cert
ssl_client_priv_key = options.ssl_client_priv_key
end
-- proxy related settings
local proxy, proxy_uri, proxy_authorization, proxy_host, proxy_port, path_prefix
proxy = options.proxy_opts or self.proxy_opts
if proxy then
if request_scheme == "https" then
proxy_uri = proxy.https_proxy
proxy_authorization = proxy.https_proxy_authorization
else
proxy_uri = proxy.http_proxy
proxy_authorization = proxy.http_proxy_authorization
-- When a proxy is used, the target URI must be in absolute-form
-- (RFC 7230, Section 5.3.2.). That is, it must be an absolute URI
-- to the remote resource with the scheme, host and an optional port
-- in place.
--
-- Since _format_request() constructs the request line by concatenating
-- params.path and params.query together, we need to modify the path
-- to also include the scheme, host and port so that the final form
-- in conformant to RFC 7230.
path_prefix = "http://" .. request_host .. (request_port == 80 and "" or (":" .. request_port))
end
if not proxy_uri then
proxy = nil
proxy_authorization = nil
path_prefix = nil
end
end
if proxy and proxy.no_proxy then
-- Check if the no_proxy option matches this host. Implementation adapted
-- from lua-http library (https://github.com/daurnimator/lua-http)
if proxy.no_proxy == "*" then
-- all hosts are excluded
proxy = nil
else
local host = request_host
local no_proxy_set = {}
-- wget allows domains in no_proxy list to be prefixed by "."
-- e.g. no_proxy=.mit.edu
for host_suffix in ngx_re_gmatch(proxy.no_proxy, "\\.?([^,]+)") do
no_proxy_set[host_suffix[1]] = true
end
-- From curl docs:
-- matched as either a domain which contains the hostname, or the
-- hostname itself. For example local.com would match local.com,
-- local.com:80, and www.local.com, but not www.notlocal.com.
--
-- Therefore, we keep stripping subdomains from the host, compare
-- them to the ones in the no_proxy list and continue until we find
-- a match or until there's only the TLD left
repeat
if no_proxy_set[host] then
proxy = nil
proxy_uri = nil
proxy_authorization = nil
break
end
-- Strip the next level from the domain and check if that one
-- is on the list
host = ngx_re_sub(host, "^[^.]+\\.", "")
until not ngx_re_find(host, "\\.")
end
end
if proxy then
local proxy_uri_t
proxy_uri_t, err = self:parse_uri(proxy_uri)
if not proxy_uri_t then
return nil, "uri parse error: ", err
end
local proxy_scheme = proxy_uri_t[1]
if proxy_scheme ~= "http" then
return nil, "protocol " .. tostring(proxy_scheme) ..
" not supported for proxy connections"
end
proxy_host = proxy_uri_t[2]
proxy_port = proxy_uri_t[3]
end
-- construct a poolname unique within proxy and ssl info
if not poolname then
poolname = (request_scheme or "")
.. ":" .. request_host
.. ":" .. tostring(request_port)
.. ":" .. tostring(ssl)
.. ":" .. (ssl_server_name or "")
.. ":" .. tostring(ssl_verify)
.. ":" .. (proxy_uri or "")
.. ":" .. (request_scheme == "https" and proxy_authorization or "")
-- in the above we only add the 'proxy_authorization' as part of the poolname
-- when the request is https. Because in that case the CONNECT request (which
-- carries the authorization header) is part of the connect procedure, whereas
-- with a plain http request the authorization is part of the actual request.
end
-- do TCP level connection
local tcp_opts = { pool = poolname, pool_size = pool_size, backlog = backlog }
if proxy then
-- proxy based connection
ok, err = sock:connect(proxy_host, proxy_port, tcp_opts)
if not ok then
return nil, "failed to connect to: " .. (proxy_host or "") ..
":" .. (proxy_port or "") ..
": ", err
end
if ssl and sock:getreusedtimes() == 0 then
-- Make a CONNECT request to create a tunnel to the destination through
-- the proxy. The request-target and the Host header must be in the
-- authority-form of RFC 7230 Section 5.3.3. See also RFC 7231 Section
-- 4.3.6 for more details about the CONNECT request
local destination = request_host .. ":" .. request_port
local res
res, err = self:request({
method = "CONNECT",
path = destination,
headers = {
["Host"] = destination,
["Proxy-Authorization"] = proxy_authorization,
}
})
if not res then
return nil, "failed to issue CONNECT to proxy:", err
end
if res.status < 200 or res.status > 299 then
return nil, "failed to establish a tunnel through a proxy: " .. res.status
end
end
elseif not request_port then
-- non-proxy, without port -> unix domain socket
ok, err = sock:connect(request_host, tcp_opts)
if not ok then
return nil, err
end
else
-- non-proxy, regular network tcp
ok, err = sock:connect(request_host, request_port, tcp_opts)
if not ok then
return nil, err
end
end
local ssl_session
-- Now do the ssl handshake
if ssl and sock:getreusedtimes() == 0 then
-- Experimental mTLS support
if ssl_client_cert and ssl_client_priv_key then
if type(sock.setclientcert) ~= "function" then
ngx_log(ngx_WARN, "cannot use SSL client cert and key without mTLS support")
else
ok, err = sock:setclientcert(ssl_client_cert, ssl_client_priv_key)
if not ok then
ngx_log(ngx_WARN, "could not set client certificate: ", err)
end
end
end
ssl_session, err = sock:sslhandshake(ssl_reused_session, ssl_server_name, ssl_verify, ssl_send_status_req)
if not ssl_session then
self:close()
return nil, err
end
end
self.host = request_host
self.port = request_port
self.keepalive = true
self.ssl = ssl
-- set only for http, https has already been handled
self.http_proxy_auth = request_scheme ~= "https" and proxy_authorization or nil
self.path_prefix = path_prefix
return true, nil, ssl_session
end
return connect

View File

@ -0,0 +1,44 @@
local rawget, rawset, setmetatable =
rawget, rawset, setmetatable
local str_lower = string.lower
local _M = {
_VERSION = '0.17.1',
}
-- Returns an empty headers table with internalised case normalisation.
function _M.new()
local mt = {
normalised = {},
}
mt.__index = function(t, k)
return rawget(t, mt.normalised[str_lower(k)])
end
mt.__newindex = function(t, k, v)
local k_normalised = str_lower(k)
-- First time seeing this header field?
if not mt.normalised[k_normalised] then
-- Create a lowercased entry in the metatable proxy, with the value
-- of the given field case
mt.normalised[k_normalised] = k
-- Set the header using the given field case
rawset(t, k, v)
else
-- We're being updated just with a different field case. Use the
-- normalised metatable proxy to give us the original key case, and
-- perorm a rawset() to update the value.
rawset(t, mt.normalised[k_normalised], v)
end
end
return setmetatable({}, mt)
end
return _M

View File

@ -0,0 +1,23 @@
package = "lua-resty-http"
version = "0.17.1-0"
source = {
url = "git://github.com/ledgetech/lua-resty-http",
tag = "v0.17.1"
}
description = {
summary = "Lua HTTP client cosocket driver for OpenResty / ngx_lua.",
homepage = "https://github.com/ledgetech/lua-resty-http",
license = "2-clause BSD",
maintainer = "James Hurst <james@pintsized.co.uk>"
}
dependencies = {
"lua >= 5.1"
}
build = {
type = "builtin",
modules = {
["resty.http"] = "lib/resty/http.lua",
["resty.http_headers"] = "lib/resty/http_headers.lua",
["resty.http_connect"] = "lib/resty/http_connect.lua"
}
}

View File

@ -0,0 +1,433 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: Simple default get.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect{
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
}
local res, err = httpc:request{
path = "/b"
}
ngx.status = res.status
ngx.print(res:read_body())
httpc:close()
';
}
location = /b {
echo "OK";
}
--- request
GET /a
--- response_body
OK
--- no_error_log
[error]
[warn]
=== TEST 2: HTTP 1.0
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect{
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
}
local res, err = httpc:request{
version = 1.0,
path = "/b"
}
ngx.status = res.status
ngx.print(res:read_body())
httpc:close()
';
}
location = /b {
echo "OK";
}
--- request
GET /a
--- response_body
OK
--- no_error_log
[error]
[warn]
=== TEST 3: Status code and reason phrase
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local ok, err = httpc:connect{
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
}
local res, err = httpc:request{
path = "/b"
}
ngx.status = res.status
ngx.say(res.reason)
ngx.print(res:read_body())
httpc:close()
';
}
location = /b {
content_by_lua '
ngx.status = 404
ngx.say("OK")
';
}
--- request
GET /a
--- response_body
Not Found
OK
--- error_code: 404
--- no_error_log
[error]
[warn]
=== TEST 4: Response headers
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect{
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
}
local res, err = httpc:request{
path = "/b"
}
ngx.status = res.status
ngx.say(res.headers["X-Test"])
httpc:close()
';
}
location = /b {
content_by_lua '
ngx.header["X-Test"] = "x-value"
ngx.say("OK")
';
}
--- request
GET /a
--- response_body
x-value
--- no_error_log
[error]
[warn]
=== TEST 5: Query
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect{
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
}
local res, err = httpc:request{
query = {
a = 1,
b = 2,
},
path = "/b"
}
ngx.status = res.status
for k,v in pairs(res.headers) do
ngx.header[k] = v
end
ngx.print(res:read_body())
httpc:close()
';
}
location = /b {
content_by_lua '
for k,v in pairs(ngx.req.get_uri_args()) do
ngx.header["X-Header-" .. string.upper(k)] = v
end
';
}
--- request
GET /a
--- response_headers
X-Header-A: 1
X-Header-B: 2
--- no_error_log
[error]
[warn]
=== TEST 7: HEAD has no body.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect{
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
}
local res, err = httpc:request{
method = "HEAD",
path = "/b"
}
local body = res:read_body()
if body then
ngx.print(body)
end
httpc:close()
';
}
location = /b {
echo "OK";
}
--- request
GET /a
--- response_body
--- no_error_log
[error]
[warn]
=== TEST 8: Errors when not initialized
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local res, err = http:connect{
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
}
if not res then ngx.say(err) end
local res, err = http:set_timeout(500)
if not res then ngx.say(err) end
local res, err = http:set_keepalive()
if not res then ngx.say(err) end
local res, err = http:get_reused_times()
if not res then ngx.say(err) end
local res, err = http:close()
if not res then ngx.say(err) end
';
}
--- request
GET /a
--- response_body
not initialized
not initialized
not initialized
not initialized
not initialized
--- no_error_log
[error]
[warn]
=== TEST 12: Allow empty HTTP header values (RFC7230)
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local httpc = require("resty.http").new()
-- Create a TCP connection and return an raw HTTP-response because
-- there is no way to set an empty header value in nginx.
assert(httpc:connect{
scheme = "http",
host = "127.0.0.1",
port = 12345,
}, "connect should return positively")
local res = httpc:request({ path = "/b" })
if res.headers["X-Header-Empty"] == "" then
ngx.say("Empty")
end
ngx.say(res.headers["X-Header-Test"])
ngx.print(res:read_body())
}
}
--- tcp_listen: 12345
--- tcp_reply
HTTP/1.0 200 OK
Date: Mon, 23 Jul 2018 13:00:00 GMT
X-Header-Empty:
X-Header-Test: Test
Server: OpenResty
OK
--- request
GET /a
--- response_body
Empty
Test
OK
--- no_error_log
[error]
[warn]
=== TEST 13: Should return error on invalid HTTP version in response status line
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("http://127.0.0.1:12345")
assert(err == "couldn't parse HTTP version from response status line: TEAPOT/1.1 OMG")
}
}
--- tcp_listen: 12345
--- tcp_reply
TEAPOT/1.1 OMG
Server: Teapot
OK
--- request
GET /a
--- no_error_log
[error]
[warn]
=== TEST 14: Should return error on invalid status code in response status line
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("http://127.0.0.1:12345")
assert(err == "couldn't parse status code from response status line: HTTP/1.1 OMG")
}
}
--- tcp_listen: 12345
--- tcp_reply
HTTP/1.1 OMG
Server: Teapot
OK
--- request
GET /a
--- no_error_log
[error]
[warn]
=== TEST 14: Empty query
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect{
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
}
local res, err = httpc:request{
query = {},
path = "/b"
}
ngx.status = res.status
ngx.print(ngx.header.test)
httpc:close()
';
}
location = /b {
content_by_lua '
ngx.header.test = ngx.var.request_uri
';
}
--- request
GET /a
--- response_headers
/b
--- no_error_log
[error]
[warn]

View File

@ -0,0 +1,324 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: Non chunked.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b"
}
local body = res:read_body()
ngx.say(#body)
httpc:close()
';
}
location = /b {
chunked_transfer_encoding off;
content_by_lua '
local len = 32768
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
';
}
--- request
GET /a
--- response_body
32768
--- no_error_log
[error]
[warn]
=== TEST 2: Chunked. The number of chunks received when no max size is given proves the response was in fact chunked.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b"
}
local chunks = {}
local c = 1
repeat
local chunk, err = res.body_reader()
if chunk then
chunks[c] = chunk
c = c + 1
end
until not chunk
local body = table.concat(chunks)
ngx.say(#body)
ngx.say(#chunks)
httpc:close()
';
}
location = /b {
content_by_lua '
local len = 32768
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
local len = 32768
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
';
}
--- request
GET /a
--- response_body
65536
2
--- no_error_log
[error]
[warn]
=== TEST 3: Chunked using read_body method.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b"
}
local body = res:read_body()
ngx.say(#body)
httpc:close()
';
}
location = /b {
content_by_lua '
local len = 32768
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
local len = 32768
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
';
}
--- request
GET /a
--- response_body
65536
--- no_error_log
[error]
[warn]
=== TEST 4: Chunked. multiple-headers, mixed case
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b"
}
local chunks = {}
local c = 1
repeat
local chunk, err = res.body_reader()
if chunk then
chunks[c] = chunk
c = c + 1
end
until not chunk
local body = table.concat(chunks)
ngx.say(#body)
ngx.say(#chunks)
ngx.say(type(res.headers["Transfer-Encoding"]))
httpc:close()
}
}
location = /b {
header_filter_by_lua_block {
ngx.header["Transfer-Encoding"] = {"chUnked", "CHunked"}
}
content_by_lua_block {
local len = 32768
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
local len = 32768
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
}
}
--- request
GET /a
--- response_body
65536
2
table
--- no_error_log
[error]
[warn]
=== TEST 5: transfer_encoding_is_chunked utility.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local http_headers = require("resty.http_headers")
local http = require("resty.http")
local headers = http_headers:new()
assert(http.transfer_encoding_is_chunked(headers) == false,
"empty headers should return false")
headers["Transfer-Encoding"] = "chunked"
assert(http.transfer_encoding_is_chunked(headers) == true,
"te set to `chunked` should return true`")
headers["Transfer-Encoding"] = " ChuNkEd "
assert(http.transfer_encoding_is_chunked(headers) == true,
"te set to ` ChuNkEd ` should return true`")
headers["Transfer-Encoding"] = { "chunked", " ChuNkEd " }
assert(http.transfer_encoding_is_chunked(headers) == true,
"te set to table values containing `chunked` should return true`")
headers["Transfer-Encoding"] = "chunked"
headers["Content-Length"] = 10
assert(http.transfer_encoding_is_chunked(headers) == true,
"transfer encoding should override content-length`")
}
}
--- request
GET /a
--- no_error_log
[error]
[warn]
=== TEST 6: Don't send Content-Length if Transfer-Encoding is specified
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local httpc = require("resty.http").new()
local yield = coroutine.yield
local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/b"
local res, err = assert(httpc:request_uri(uri, {
body = coroutine.wrap(function()
yield("3\r\n")
yield("foo\r\n")
yield("3\r\n")
yield("bar\r\n")
yield("0\r\n")
yield("\r\n")
end),
headers = {
["Transfer-Encoding"] = "chunked",
["Content-Length"] = 42,
},
}))
ngx.say(res.body)
}
}
location = /b {
content_by_lua_block {
ngx.req.read_body()
ngx.say(ngx.req.get_headers()["Content-Length"])
ngx.print(ngx.req.get_body_data())
}
}
--- request
GET /a
--- response_body
nil
foobar
--- no_error_log
[error]
[warn]

View File

@ -0,0 +1,509 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: POST form-urlencoded
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
body = "a=1&b=2&c=3",
path = "/b",
headers = {
["Content-Type"] = "application/x-www-form-urlencoded",
}
}
ngx.say(res:read_body())
httpc:close()
';
}
location = /b {
content_by_lua '
ngx.req.read_body()
local args = ngx.req.get_post_args()
ngx.say("a: ", args.a)
ngx.say("b: ", args.b)
ngx.print("c: ", args.c)
';
}
--- request
GET /a
--- response_body
a: 1
b: 2
c: 3
--- no_error_log
[error]
[warn]
=== TEST 2: POST form-urlencoded 1.0
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
method = "POST",
body = "a=1&b=2&c=3",
path = "/b",
headers = {
["Content-Type"] = "application/x-www-form-urlencoded",
},
version = 1.0,
}
ngx.say(res:read_body())
httpc:close()
';
}
location = /b {
content_by_lua '
ngx.req.read_body()
local args = ngx.req.get_post_args()
ngx.say(ngx.req.get_method())
ngx.say("a: ", args.a)
ngx.say("b: ", args.b)
ngx.print("c: ", args.c)
';
}
--- request
GET /a
--- response_body
POST
a: 1
b: 2
c: 3
--- no_error_log
[error]
[warn]
=== TEST 3: 100 Continue does not end requset
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
body = "a=1&b=2&c=3",
path = "/b",
headers = {
["Expect"] = "100-continue",
["Content-Type"] = "application/x-www-form-urlencoded",
}
}
ngx.say(res.status)
ngx.say(res:read_body())
httpc:close()
';
}
location = /b {
content_by_lua '
ngx.req.read_body()
local args = ngx.req.get_post_args()
ngx.say("a: ", args.a)
ngx.say("b: ", args.b)
ngx.print("c: ", args.c)
';
}
--- request
GET /a
--- response_body
200
a: 1
b: 2
c: 3
--- no_error_log
[error]
[warn]
=== TEST 4: Return non-100 status to user
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b",
headers = {
["Expect"] = "100-continue",
["Content-Type"] = "application/x-www-form-urlencoded",
}
}
if not res then
ngx.say(err)
end
ngx.say(res.status)
ngx.say(res:read_body())
httpc:close()
';
}
location = /b {
return 417 "Expectation Failed";
}
--- request
GET /a
--- response_body
417
Expectation Failed
--- no_error_log
[error]
[warn]
=== TEST 5: Return 100 Continue with headers
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
body = "a=1&b=2&c=3",
path = "/b",
headers = {
["Expect"] = "100-continue",
["Content-Type"] = "application/x-www-form-urlencoded",
}
}
if not res then
ngx.log(ngx.ERR, "httpc:request failed: ", err)
end
ngx.say(res.status)
ngx.say(res:read_body())
httpc:close()
}
}
location = /b {
content_by_lua_block {
local len = ngx.req.get_headers()["Content-Length"]
local sock, err = ngx.req.socket(true)
if not sock then
ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err)
return
end
-- with additional header
local ok, err = sock:send("HTTP/1.1 100 Continue\r\nConnection: keep-alive\r\n\r\n")
if not ok then
ngx.log(ngx.ERR, "failed to send 100 response: ", err)
end
local data, err = sock:receive(len)
if not data then
ngx.log(ngx.ERR, "failed to receive: ", err)
return
end
local ok, err = sock:send("HTTP/1.1 200 OK\r\n" ..
"Content-Length: " .. len .. "\r\n" ..
"Content-Type: application/x-www-form-urlencoded\r\n\r\n" ..
data)
if not ok then
ngx.log(ngx.ERR, "failed to send 200 response: ", err)
return
end
}
}
--- request
GET /a
--- response_body
200
a=1&b=2&c=3
--- no_error_log
[error]
[warn]
=== TEST 6: Return 100 Continue without headers
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
body = "a=1&b=2&c=3",
path = "/b",
headers = {
["Expect"] = "100-continue",
["Content-Type"] = "application/x-www-form-urlencoded",
}
}
if not res then
ngx.log(ngx.ERR, "httpc:request failed: ", err)
end
ngx.say(res.status)
ngx.say(res:read_body())
httpc:close()
}
}
location = /b {
content_by_lua_block {
local len = ngx.req.get_headers()["Content-Length"]
local sock, err = ngx.req.socket(true)
if not sock then
ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err)
return
end
-- without additional headers
local ok, err = sock:send("HTTP/1.1 100 Continue\r\n\r\n")
if not ok then
ngx.log(ngx.ERR, "failed to send 100 response: ", err)
end
local data, err = sock:receive(len)
if not data then
ngx.log(ngx.ERR, "failed to receive: ", err)
return
end
local ok, err = sock:send("HTTP/1.1 200 OK\r\n" ..
"Content-Length: " .. len .. "\r\n" ..
"Content-Type: application/x-www-form-urlencoded\r\n\r\n" ..
data)
if not ok then
ngx.log(ngx.ERR, "failed to send 200 response: ", err)
return
end
}
}
--- request
GET /a
--- response_body
200
a=1&b=2&c=3
--- no_error_log
[error]
[warn]
=== TEST 7: Non string request bodies are converted with correct length
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local httpc = require("resty.http").new()
local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/b"
for _, body in ipairs({ 12345,
true,
"string",
{ "tab", "le" },
{ "mix", 123, "ed", "tab", "le" } }) do
local res, err = assert(httpc:request_uri(uri, {
body = body,
}))
ngx.say(res.body)
end
';
}
location = /b {
content_by_lua '
ngx.req.read_body()
ngx.print(ngx.req.get_body_data())
';
}
--- request
GET /a
--- response_body
12345
true
string
table
mix123edtable
--- no_error_log
[error]
[warn]
=== TEST 8: Request body as iterator
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local httpc = require("resty.http").new()
local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/b"
local res, err = assert(httpc:request_uri(uri, {
body = coroutine.wrap(function()
coroutine.yield("foo")
coroutine.yield("bar")
end),
headers = {
["Content-Length"] = 6
}
}))
ngx.say(res.body)
';
}
location = /b {
content_by_lua '
ngx.req.read_body()
ngx.print(ngx.req.get_body_data())
';
}
--- request
GET /a
--- response_body
foobar
--- no_error_log
[error]
[warn]
=== TEST 9: Request body as iterator, errors with missing length
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local httpc = require("resty.http").new()
local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/b"
local res, err = httpc:request_uri(uri, {
body = coroutine.wrap(function()
coroutine.yield("foo")
coroutine.yield("bar")
end),
})
assert(not res)
ngx.say(err)
';
}
location = /b {
content_by_lua '
ngx.req.read_body()
ngx.print(ngx.req.get_body_data())
';
}
--- request
GET /a
--- response_body
Request body is a function but a length or chunked encoding is not specified
--- no_error_log
[error]
[warn]
=== TEST 10: Request body as iterator with chunked encoding
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local httpc = require("resty.http").new()
local yield = coroutine.yield
local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/b"
local res, err = assert(httpc:request_uri(uri, {
body = coroutine.wrap(function()
yield("3\r\n")
yield("foo\r\n")
yield("3\r\n")
yield("bar\r\n")
yield("0\r\n")
yield("\r\n")
end),
headers = {
["Transfer-Encoding"] = "chunked"
}
}))
ngx.say(res.body)
}
}
location = /b {
content_by_lua '
ngx.req.read_body()
ngx.print(ngx.req.get_body_data())
';
}
--- request
GET /a
--- response_body
foobar
--- no_error_log
[error]
[warn]

View File

@ -0,0 +1,162 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: Trailers. Check Content-MD5 generated after the body is sent matches up.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b",
headers = {
["TE"] = "trailers",
}
}
local body = res:read_body()
local hash = ngx.md5(body)
res:read_trailers()
if res.headers["Content-MD5"] == hash then
ngx.say("OK")
else
ngx.say(res.headers["Content-MD5"])
end
';
}
location = /b {
content_by_lua '
-- We use the raw socket to compose a response, since OpenResty
-- doesnt support trailers natively.
ngx.req.read_body()
local sock, err = ngx.req.socket(true)
if not sock then
ngx.say(err)
end
local res = {}
table.insert(res, "HTTP/1.1 200 OK")
table.insert(res, "Date: " .. ngx.http_time(ngx.time()))
table.insert(res, "Transfer-Encoding: chunked")
table.insert(res, "Trailer: Content-MD5")
table.insert(res, "")
local body = "Hello, World"
table.insert(res, string.format("%x", #body))
table.insert(res, body)
table.insert(res, "0")
table.insert(res, "")
table.insert(res, "Content-MD5: " .. ngx.md5(body))
table.insert(res, "")
table.insert(res, "")
sock:send(table.concat(res, "\\r\\n"))
';
}
--- request
GET /a
--- response_body
OK
--- no_error_log
[error]
[warn]
=== TEST 2: Advertised trailer does not exist, handled gracefully.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b",
headers = {
["TE"] = "trailers",
}
}
local body = res:read_body()
local hash = ngx.md5(body)
res:read_trailers()
ngx.say("OK")
httpc:close()
';
}
location = /b {
content_by_lua '
-- We use the raw socket to compose a response, since OpenResty
-- doesnt support trailers natively.
ngx.req.read_body()
local sock, err = ngx.req.socket(true)
if not sock then
ngx.say(err)
end
local res = {}
table.insert(res, "HTTP/1.1 200 OK")
table.insert(res, "Date: " .. ngx.http_time(ngx.time()))
table.insert(res, "Transfer-Encoding: chunked")
table.insert(res, "Trailer: Content-MD5")
table.insert(res, "")
local body = "Hello, World"
table.insert(res, string.format("%x", #body))
table.insert(res, body)
table.insert(res, "0")
table.insert(res, "")
table.insert(res, "")
sock:send(table.concat(res, "\\r\\n"))
';
}
--- request
GET /a
--- response_body
OK
--- no_error_log
[error]
[warn]

View File

@ -0,0 +1,607 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: Chunked streaming body reader returns the right content length.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b",
}
local chunks = {}
repeat
local chunk = res.body_reader()
if chunk then
table.insert(chunks, chunk)
end
until not chunk
local body = table.concat(chunks)
ngx.say(#body)
ngx.say(res.headers["Transfer-Encoding"])
httpc:close()
';
}
location = /b {
content_by_lua '
local len = 32768
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
';
}
--- request
GET /a
--- response_body
32768
chunked
--- no_error_log
[error]
[warn]
=== TEST 2: Non-Chunked streaming body reader returns the right content length.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b",
}
local chunks = {}
repeat
local chunk = res.body_reader()
if chunk then
table.insert(chunks, chunk)
end
until not chunk
local body = table.concat(chunks)
ngx.say(#body)
ngx.say(res.headers["Transfer-Encoding"])
ngx.say(#chunks)
httpc:close()
';
}
location = /b {
chunked_transfer_encoding off;
content_by_lua '
local len = 32768
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
';
}
--- request
GET /a
--- response_body
32768
nil
1
--- no_error_log
[error]
[warn]
=== TEST 2b: Non-Chunked streaming body reader, buffer size becomes nil
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b",
}
local chunks = {}
local buffer_size = 16384
repeat
local chunk = res.body_reader(buffer_size)
if chunk then
table.insert(chunks, chunk)
end
buffer_size = nil
until not chunk
local body = table.concat(chunks)
ngx.say(res.headers["Transfer-Encoding"])
httpc:close()
';
}
location = /b {
chunked_transfer_encoding off;
content_by_lua '
local len = 32768
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
';
}
--- request
GET /a
--- response_body
nil
--- error_log
Buffer size not specified, bailing
=== TEST 3: HTTP 1.0 body reader with no max size returns the right content length.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b",
version = 1.0,
}
local chunks = {}
repeat
local chunk = res.body_reader()
if chunk then
table.insert(chunks, chunk)
end
until not chunk
local body = table.concat(chunks)
ngx.say(#body)
ngx.say(res.headers["Transfer-Encoding"])
ngx.say(#chunks)
httpc:close()
';
}
location = /b {
chunked_transfer_encoding off;
content_by_lua '
local len = 32768
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
';
}
--- request
GET /a
--- response_body
32768
nil
1
--- no_error_log
[error]
[warn]
=== TEST 4: HTTP 1.0 body reader with max chunk size returns the right content length.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b",
version = 1.0,
}
local chunks = {}
local size = 8192
repeat
local chunk = res.body_reader(size)
if chunk then
table.insert(chunks, chunk)
end
size = size + size
until not chunk
local body = table.concat(chunks)
ngx.say(#body)
ngx.say(res.headers["Transfer-Encoding"])
ngx.say(#chunks)
httpc:close()
';
}
location = /b {
chunked_transfer_encoding off;
content_by_lua '
local len = 32769
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
';
}
--- request
GET /a
--- response_body
32769
nil
3
--- no_error_log
[error]
[warn]
=== TEST 4b: HTTP 1.0 body reader with no content length, stream works as expected.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b",
version = 1.0,
}
local chunks = {}
local size = 8192
repeat
local chunk = res.body_reader(size)
if chunk then
table.insert(chunks, chunk)
end
size = size + size
until not chunk
local body = table.concat(chunks)
ngx.say(#body)
ngx.say(#chunks)
httpc:close()
';
}
location = /b {
content_by_lua '
ngx.req.read_body()
local sock, err = ngx.req.socket(true)
if not sock then
ngx.say(err)
end
local res = {}
table.insert(res, "HTTP/1.0 200 OK")
table.insert(res, "Date: " .. ngx.http_time(ngx.time()))
table.insert(res, "")
local len = 32769
local t = {}
for i=1,len do
t[i] = 0
end
table.insert(res, table.concat(t))
sock:send(table.concat(res, "\\r\\n"))
';
}
--- request
GET /a
--- response_body
32769
3
--- no_error_log
[error]
[warn]
=== TEST 5: Chunked streaming body reader with max chunk size returns the right content length.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b",
}
local chunks = {}
local size = 8192
repeat
local chunk = res.body_reader(size)
if chunk then
table.insert(chunks, chunk)
end
size = size + size
until not chunk
local body = table.concat(chunks)
ngx.say(#body)
ngx.say(res.headers["Transfer-Encoding"])
ngx.say(#chunks)
httpc:close()
';
}
location = /b {
content_by_lua '
local len = 32768
local t = {}
for i=1,len do
t[i] = 0
end
ngx.print(table.concat(t))
';
}
--- request
GET /a
--- response_body
32768
chunked
3
--- no_error_log
[error]
[warn]
=== TEST 6: Request reader correctly reads body
--- http_config eval: $::HttpConfig
--- config
location = /a {
lua_need_request_body off;
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local reader, err = httpc:get_client_body_reader(8192)
repeat
local chunk, err = reader()
if chunk then
ngx.print(chunk)
end
until chunk == nil
';
}
--- request
POST /a
foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz
--- response_body: foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz
--- no_error_log
[error]
[warn]
=== TEST 7: Request reader correctly reads body in chunks
--- http_config eval: $::HttpConfig
--- config
location = /a {
lua_need_request_body off;
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local reader, err = httpc:get_client_body_reader(64)
local chunks = 0
repeat
chunks = chunks +1
local chunk, err = reader()
if chunk then
ngx.print(chunk)
end
until chunk == nil
ngx.say("\\n"..chunks)
';
}
--- request
POST /a
foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz
--- response_body
foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz
3
--- no_error_log
[error]
[warn]
=== TEST 8: Request reader passes into client
--- http_config eval: $::HttpConfig
--- config
location = /a {
lua_need_request_body off;
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local reader, err = httpc:get_client_body_reader(64)
local res, err = httpc:request{
method = POST,
path = "/b",
body = reader,
headers = ngx.req.get_headers(100, true),
}
local body = res:read_body()
ngx.say(body)
httpc:close()
';
}
location = /b {
content_by_lua '
ngx.req.read_body()
local body, err = ngx.req.get_body_data()
ngx.print(body)
';
}
--- request
POST /a
foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz
--- response_body
foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz
--- no_error_log
[error]
[warn]
=== TEST 9: Body reader is a function returning nil when no body is present.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b",
method = "HEAD",
}
repeat
local chunk = res.body_reader()
until not chunk
httpc:close()
';
}
location = /b {
content_by_lua '
ngx.exit(200)
';
}
--- request
GET /a
--- no_error_log
[error]
[warn]
=== TEST 10: Issue a notice (but do not error) if trying to read the request body in a subrequest
--- http_config eval: $::HttpConfig
--- config
location = /a {
echo_location /b;
}
location = /b {
lua_need_request_body off;
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local reader, err = httpc:get_client_body_reader(8192)
if not reader then
ngx.log(ngx.NOTICE, err)
return
end
repeat
local chunk, err = reader()
if chunk then
ngx.print(chunk)
end
until chunk == nil
';
}
--- request
POST /a
foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz
--- response_body:
--- no_error_log
[error]
[warn]
--- error_log
attempt to read the request body in a subrequest

View File

@ -0,0 +1,338 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: Simple URI interface
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("http://127.0.0.1:"..ngx.var.server_port.."/b?a=1&b=2")
if not res then
ngx.log(ngx.ERR, err)
end
ngx.status = res.status
ngx.header["X-Header-A"] = res.headers["X-Header-A"]
ngx.header["X-Header-B"] = res.headers["X-Header-B"]
ngx.print(res.body)
';
}
location = /b {
content_by_lua '
for k,v in pairs(ngx.req.get_uri_args()) do
ngx.header["X-Header-" .. string.upper(k)] = v
end
ngx.say("OK")
';
}
--- request
GET /a
--- response_headers
X-Header-A: 1
X-Header-B: 2
--- response_body
OK
--- no_error_log
[error]
[warn]
=== TEST 2: Simple URI interface HTTP 1.0
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri(
"http://127.0.0.1:"..ngx.var.server_port.."/b?a=1&b=2", {
}
)
ngx.status = res.status
ngx.header["X-Header-A"] = res.headers["X-Header-A"]
ngx.header["X-Header-B"] = res.headers["X-Header-B"]
ngx.print(res.body)
';
}
location = /b {
content_by_lua '
for k,v in pairs(ngx.req.get_uri_args()) do
ngx.header["X-Header-" .. string.upper(k)] = v
end
ngx.say("OK")
';
}
--- request
GET /a
--- response_headers
X-Header-A: 1
X-Header-B: 2
--- response_body
OK
--- no_error_log
[error]
[warn]
=== TEST 3 Simple URI interface, params override
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri(
"http://127.0.0.1:"..ngx.var.server_port.."/b?a=1&b=2", {
path = "/c",
query = {
a = 2,
b = 3,
},
}
)
ngx.status = res.status
ngx.header["X-Header-A"] = res.headers["X-Header-A"]
ngx.header["X-Header-B"] = res.headers["X-Header-B"]
ngx.print(res.body)
';
}
location = /c {
content_by_lua '
for k,v in pairs(ngx.req.get_uri_args()) do
ngx.header["X-Header-" .. string.upper(k)] = v
end
ngx.say("OK")
';
}
--- request
GET /a
--- response_headers
X-Header-A: 2
X-Header-B: 3
--- response_body
OK
--- no_error_log
[error]
[warn]
=== TEST 4 Simple URI interface, params override, query as string
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri(
"http://127.0.0.1:"..ngx.var.server_port.."/b?a=1&b=2", {
path = "/c",
query = "a=2&b=3",
}
)
ngx.status = res.status
ngx.header["X-Header-A"] = res.headers["X-Header-A"]
ngx.header["X-Header-B"] = res.headers["X-Header-B"]
ngx.print(res.body)
';
}
location = /c {
content_by_lua '
for k,v in pairs(ngx.req.get_uri_args()) do
ngx.header["X-Header-" .. string.upper(k)] = v
end
ngx.say("OK")
';
}
--- request
GET /a
--- response_headers
X-Header-A: 2
X-Header-B: 3
--- response_body
OK
--- no_error_log
[error]
[warn]
=== TEST 5 Simple URI interface, params override, query as string, as leading ?
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri(
"http://127.0.0.1:"..ngx.var.server_port.."/b?a=1&b=2", {
query = "?a=2&b=3",
}
)
ngx.status = res.status
ngx.header["X-Header-A"] = res.headers["X-Header-A"]
ngx.header["X-Header-B"] = res.headers["X-Header-B"]
ngx.print(res.body)
';
}
location = /b {
content_by_lua '
for k,v in pairs(ngx.req.get_uri_args()) do
ngx.header["X-Header-" .. string.upper(k)] = v
end
ngx.say("OK")
';
}
--- request
GET /a
--- response_headers
X-Header-A: 2
X-Header-B: 3
--- response_body
OK
--- no_error_log
[error]
[warn]
=== TEST 6: Connection is closed on error
--- http_config eval: $::HttpConfig
--- config
lua_socket_read_timeout 100ms;
location = /a {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("http://127.0.0.1:"..ngx.var.server_port.."/b?a=1&b=2")
if not res then
ngx.log(ngx.ERR, err)
else
return ngx.say("BAD")
end
local ok, err = httpc.sock:close()
ngx.say(ok, " ", err)
}
}
location = /b {
content_by_lua_block {
ngx.say("1")
ngx.flush(true)
ngx.sleep(0.5)
ngx.say("2")
}
}
--- request
GET /a
--- response_body
nil closed
--- error_log
lua tcp socket read timed out
=== TEST 7: Content-Length is set on POST/PUT/PATCH requests when body is absent
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
for i, method in ipairs({ "POST", "PUT", "PATCH" }) do
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("http://127.0.0.1:"..ngx.var.server_port.."/b", { method = method })
if not res then
ngx.log(ngx.ERR, err)
end
if i == 1 then
ngx.status = res.status
end
ngx.print(res.body)
end
';
}
location = /b {
content_by_lua '
ngx.say(ngx.req.get_method(), " Content-Length: ", ngx.req.get_headers()["Content-Length"])
';
}
--- request
GET /a
--- response_body
POST Content-Length: 0
PUT Content-Length: 0
PATCH Content-Length: 0
--- no_error_log
[error]
[warn]
=== TEST 8: Content-Length is not set on GET requests when body is absent
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("http://127.0.0.1:"..ngx.var.server_port.."/b")
if not res then
ngx.log(ngx.ERR, err)
end
ngx.status = res.status
ngx.print(res.body)
';
}
location = /b {
content_by_lua '
ngx.say("Content-Length: ", type(ngx.req.get_headers()["Content-Length"]))
';
}
--- request
GET /a
--- response_body
Content-Length: nil
--- no_error_log
[error]
[warn]

View File

@ -0,0 +1,434 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1 Simple interface, Connection: Keep-alive. Test the connection is reused.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri(
"http://127.0.0.1:" .. ngx.var.server_port.."/b", {}
)
ngx.say(res.headers["Connection"])
httpc:connect {
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
}
ngx.say(httpc:get_reused_times())
';
}
location = /b {
content_by_lua '
ngx.say("OK")
';
}
--- request
GET /a
--- response_body
keep-alive
1
--- no_error_log
[error]
[warn]
=== TEST 2 Simple interface, Connection: close, test we don't try to keepalive, but also that subsequent connections can keepalive.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri(
"http://127.0.0.1:"..ngx.var.server_port.."/b", {
version = 1.0,
headers = {
["Connection"] = "close",
},
}
)
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
ngx.say(httpc:get_reused_times())
httpc:set_keepalive()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
ngx.say(httpc:get_reused_times())
';
}
location = /b {
content_by_lua '
ngx.say("OK")
';
}
--- request
GET /a
--- response_body
0
1
--- no_error_log
[error]
[warn]
=== TEST 3 Generic interface, Connection: Keep-alive. Test the connection is reused.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
path = "/b"
}
local body = res:read_body()
ngx.say(res.headers["Connection"])
ngx.say(httpc:set_keepalive())
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
ngx.say(httpc:get_reused_times())
';
}
location = /b {
content_by_lua '
ngx.say("OK")
';
}
--- request
GET /a
--- response_body
keep-alive
1
1
--- no_error_log
[error]
[warn]
=== TEST 4 Generic interface, Connection: Close. Test we don't try to keepalive, but also that subsequent connections can keepalive.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
local res, err = httpc:request{
version = 1.0,
headers = {
["Connection"] = "Close",
},
path = "/b"
}
local body = res:read_body()
ngx.say(res.headers["Connection"])
local r, e = httpc:set_keepalive()
ngx.say(r)
ngx.say(e)
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
ngx.say(httpc:get_reused_times())
httpc:set_keepalive()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
ngx.say(httpc:get_reused_times())
';
}
location = /b {
content_by_lua '
ngx.say("OK")
';
}
--- request
GET /a
--- response_body
close
2
connection must be closed
0
1
--- no_error_log
[error]
[warn]
=== TEST 5: Generic interface, HTTP 1.0, no connection header. Test we don't try to keepalive, but also that subsequent connections can keepalive.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = 12345,
})
local res, err = httpc:request{
version = 1.0,
path = "/b"
}
local body = res:read_body()
ngx.print(body)
ngx.say(res.headers["Connection"])
local r, e = httpc:set_keepalive()
ngx.say(r)
ngx.say(e)
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
ngx.say(httpc:get_reused_times())
httpc:set_keepalive()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
ngx.say(httpc:get_reused_times())
';
}
location = /b {
content_by_lua '
ngx.say("OK")
';
}
--- request
GET /a
--- tcp_listen: 12345
--- tcp_reply
HTTP/1.0 200 OK
Date: Fri, 08 Aug 2016 08:12:31 GMT
Server: OpenResty
OK
--- response_body
OK
nil
2
connection must be closed
0
1
--- no_error_log
[error]
[warn]
=== TEST 6: Simple interface, override settings
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri(
"http://127.0.0.1:"..ngx.var.server_port.."/b",
{
keepalive = false
}
)
ngx.say(res.headers["Connection"])
httpc:connect {
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
}
ngx.say(httpc:get_reused_times())
httpc:close()
local res, err = httpc:request_uri(
"http://127.0.0.1:"..ngx.var.server_port.."/b",
{
keepalive_timeout = 10
}
)
ngx.say(res.headers["Connection"])
httpc:connect {
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
}
ngx.say(httpc:get_reused_times())
httpc:close()
local res, err = httpc:request_uri(
"http://127.0.0.1:"..ngx.var.server_port.."/b",
{
keepalive_timeout = 1
}
)
ngx.say(res.headers["Connection"])
ngx.sleep(1.1)
httpc:connect {
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
}
ngx.say(httpc:get_reused_times())
httpc:close()
}
}
location = /b {
content_by_lua_block {
ngx.say("OK")
}
}
--- request
GET /a
--- response_body
keep-alive
0
keep-alive
1
keep-alive
0
--- no_error_log
[error]
[warn]
=== TEST 7: Generic interface, HTTP 1.1, Connection: Upgrade, close. Test we don't try to keepalive, but also that subsequent connections can keepalive.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
-- Create a TCP connection and return an raw HTTP-response because
-- there is no way to set an "Connection: Upgrade, close" header in nginx.
assert(httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = 12345,
}), "connect should return positively")
local res = httpc:request({
version = 1.1,
path = "/b",
})
local body = res:read_body()
ngx.print(body)
ngx.say(res.headers["Connection"])
local r, e = httpc:set_keepalive()
ngx.say(r)
ngx.say(e)
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
ngx.say(httpc:get_reused_times())
httpc:set_keepalive()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
ngx.say(httpc:get_reused_times())
}
}
--- tcp_listen: 12345
--- tcp_reply
HTTP/1.1 200 OK
Date: Wed, 08 Aug 2018 17:00:00 GMT
Server: Apache/2
Upgrade: h2,h2c
Connection: Upgrade, close
OK
--- request
GET /a
--- response_body
OK
Upgrade, close
2
connection must be closed
0
1
--- no_error_log
[error]
[warn]

View File

@ -0,0 +1,155 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1 Test that pipelined requests can be read correctly.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
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.headers["X-Res"])
ngx.say(r:read_body())
end
end
';
}
location = /b {
content_by_lua '
ngx.status = 200
ngx.header["X-Res"] = "B"
ngx.print("B")
';
}
location = /c {
content_by_lua '
ngx.status = 404
ngx.header["X-Res"] = "C"
ngx.print("C")
';
}
location = /d {
content_by_lua '
ngx.status = 200
ngx.header["X-Res"] = "D"
ngx.print("D")
';
}
--- request
GET /a
--- response_body
200
B
B
404
C
C
200
D
D
--- no_error_log
[error]
[warn]
=== TEST 2: Test we can handle timeouts on reading the pipelined requests.
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port
})
httpc:set_timeout(1)
local responses = httpc:request_pipeline{
{
path = "/b",
},
{
path = "/c",
},
}
for i,r in ipairs(responses) do
if r.status then
ngx.say(r.status)
ngx.say(r.headers["X-Res"])
ngx.say(r:read_body())
end
end
';
}
location = /b {
content_by_lua '
ngx.status = 200
ngx.header["X-Res"] = "B"
ngx.print("B")
';
}
location = /c {
content_by_lua '
ngx.status = 404
ngx.header["X-Res"] = "C"
ngx.sleep(1)
ngx.print("C")
';
}
--- request
GET /a
--- response_body
200
B
B
--- no_error_log
[warn]
--- error_log eval
[qr/timeout/]

View File

@ -0,0 +1,64 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: parse_uri returns port 443 for https URIs
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local parsed = httpc:parse_uri("https://www.google.com/foobar")
ngx.say(parsed[3])
';
}
--- request
GET /a
--- response_body
443
--- no_error_log
[error]
[warn]
=== TEST 2: parse_uri returns port 80 for http URIs
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local parsed = httpc:parse_uri("http://www.google.com/foobar")
ngx.say(parsed[3])
';
}
--- request
GET /a
--- response_body
80
--- no_error_log
[error]
[warn]

View File

@ -0,0 +1,122 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: Issue a notice (but do not error) if trying to read the request body in a subrequest
--- http_config eval: $::HttpConfig
--- config
location = /a {
echo_location /b;
}
location = /b {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port,
})
local res, err = httpc:request{
path = "/c",
headers = {
["Content-Type"] = "application/x-www-form-urlencoded",
}
}
if not res then
ngx.say(err)
end
ngx.print(res:read_body())
httpc:close()
';
}
location /c {
echo "OK";
}
--- request
GET /a
--- response_body
OK
--- no_error_log
[error]
[warn]
=== TEST 2: Read request body
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local httpc = require("resty.http").new()
local reader, err = assert(httpc:get_client_body_reader())
repeat
local buffer, err = reader()
if err then
ngx.log(ngx.ERR, err)
end
if buffer then
ngx.print(buffer)
end
until not buffer
}
}
--- request
POST /a
foobar
--- response_body: foobar
--- no_error_log
[error]
[warn]
=== TEST 2: Read chunked request body, errors as not yet supported
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local httpc = require("resty.http").new()
local _, err = httpc:get_client_body_reader()
ngx.log(ngx.ERR, err)
}
}
--- more_headers
Transfer-Encoding: chunked
--- request eval
"POST /a
3\r
foo\r
3\r
bar\r
0\r
\r
"
--- error_log
chunked request bodies not supported yet
--- no_error_log
[warn]

View File

@ -0,0 +1,182 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: Proxy GET request and response
--- http_config eval: $::HttpConfig
--- config
location = /a_prx {
rewrite ^(.*)_prx$ $1 break;
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port,
})
httpc:proxy_response(httpc:proxy_request())
httpc:set_keepalive()
';
}
location = /a {
content_by_lua '
ngx.status = 200
ngx.header["X-Test"] = "foo"
ngx.say("OK")
';
}
--- request
GET /a_prx
--- response_body
OK
--- response_headers
X-Test: foo
--- error_code: 200
--- no_error_log
[error]
[warn]
--- error_log
[debug]
=== TEST 2: Proxy POST request and response
--- http_config eval: $::HttpConfig
--- config
location = /a_prx {
rewrite ^(.*)_prx$ $1 break;
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port,
})
httpc:proxy_response(httpc:proxy_request())
httpc:set_keepalive()
';
}
location = /a {
lua_need_request_body on;
content_by_lua '
ngx.status = 404
ngx.header["X-Test"] = "foo"
local args, err = ngx.req.get_post_args()
ngx.say(args["foo"])
ngx.say(args["hello"])
';
}
--- request
POST /a_prx
foo=bar&hello=world
--- response_body
bar
world
--- response_headers
X-Test: foo
--- error_code: 404
--- no_error_log
[error]
--- error_log
[warn]
--- error_log
[debug]
=== TEST 3: Proxy multiple headers
--- http_config eval: $::HttpConfig
--- config
location = /a_prx {
rewrite ^(.*)_prx$ $1 break;
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port,
})
httpc:proxy_response(httpc:proxy_request())
httpc:set_keepalive()
';
}
location = /a {
content_by_lua '
ngx.status = 200
ngx.header["Set-Cookie"] = { "cookie1", "cookie2" }
ngx.say("OK")
';
}
--- request
GET /a_prx
--- response_body
OK
--- raw_response_headers_like: .*Set-Cookie: cookie1\r\nSet-Cookie: cookie2\r\n
--- error_code: 200
--- no_error_log
[error]
[warn]
--- error_log
[debug]
=== TEST 4: Proxy still works with spaces in URI
--- http_config eval: $::HttpConfig
--- config
location = "/a_ b_prx" {
rewrite ^(.*)_prx$ $1 break;
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:connect({
scheme = "http",
host = "127.0.0.1",
port = ngx.var.server_port,
})
httpc:proxy_response(httpc:proxy_request())
httpc:set_keepalive()
';
}
location = "/a_ b" {
content_by_lua '
ngx.status = 200
ngx.header["X-Test"] = "foo"
ngx.say("OK")
';
}
--- request
GET /a_%20b_prx
--- response_body
OK
--- response_headers
X-Test: foo
--- error_code: 200
--- no_error_log
[error]
[warn]
--- error_log
[debug]

View File

@ -0,0 +1,182 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
underscores_in_headers On;
};
no_long_string();
no_diff();
run_tests();
__DATA__
=== TEST 1: Unit test header normalisation
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local headers = require("resty.http_headers").new()
headers["content-length"] = "a"
headers["TRANSFER-ENCODING"] = "b"
headers["SSL_CLIENT_CERTIFICATE"] = "foo"
assert(headers["coNtENt-LENgth"] == headers["content-length"],
"header values should match")
assert(headers["transfer-encoding"] == headers["TRANSFER-ENCODING"],
"header values should match")
assert(headers["ssl_client_certificate"] == headers["SSL_CLIENT_CERTIFICATE"],
"header values should match")
assert(headers["SSL-CLIENT-CERTIFICATE"] ~= headers["SSL_CLIENT_CERTIFICATE"],
"underscores are separate to hyphens")
}
}
--- request
GET /a
--- response_body
--- no_error_log
[error]
=== TEST 2: Integration test headers normalisation
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local httpc = require("resty.http").new()
assert(httpc:connect("127.0.0.1", ngx.var.server_port),
"connect should return positively")
local res, err = httpc:request{
path = "/b"
}
ngx.status = res.status
ngx.say(res.headers["X-Foo-Header"])
ngx.say(res.headers["x-fOo-heaDeR"])
httpc:close()
}
}
location = /b {
content_by_lua_block {
ngx.header["X-Foo-Header"] = "bar"
ngx.say("OK")
}
}
--- request
GET /a
--- response_body
bar
bar
--- no_error_log
[error]
=== TEST 3: Integration test request headers normalisation
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local httpc = require("resty.http").new()
assert(httpc:connect("127.0.0.1", ngx.var.server_port),
"connect should return positively")
local res, err = httpc:request{
path = "/b",
headers = {
["uSeR-AgENT"] = "test_user_agent",
["X_Foo"] = "bar",
},
}
ngx.status = res.status
ngx.print(res:read_body())
httpc:close()
}
}
location = /b {
content_by_lua_block {
ngx.say(ngx.req.get_headers()["User-Agent"])
ngx.say(ngx.req.get_headers(nil, true)["X_Foo"])
}
}
--- request
GET /a
--- response_body
test_user_agent
bar
--- no_error_log
[error]
=== TEST 4: Test that headers remain unique
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local headers = require("resty.http_headers").new()
headers["x-a-header"] = "a"
headers["X-A-HEAder"] = "b"
for k,v in pairs(headers) do
ngx.header[k] = v
end
}
}
--- request
GET /a
--- response_headers
x-a-header: b
--- no_error_log
[error]
=== TEST 5: Prove header tables are always unique
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua_block {
local headers = require("resty.http_headers").new()
headers["content-length"] = "a"
headers["TRANSFER-ENCODING"] = "b"
headers["SSL_CLIENT_CERTIFICATE"] = "foo"
local headers2 = require("resty.http_headers").new()
assert(headers2 ~= headers,
"headers should be unique")
assert(not next(headers2),
"headers2 should be empty")
assert(not next(getmetatable(headers2).normalised),
"headers normalised data should be empty")
}
}
--- request
GET /a
--- response_body
--- no_error_log
[error]

View File

@ -0,0 +1,55 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: request_uri (check the default path)
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("http://127.0.0.1:"..ngx.var.server_port)
if res and 200 == res.status then
ngx.say("OK")
else
ngx.say("FAIL")
end
';
}
location =/ {
content_by_lua '
ngx.print("OK")
';
}
--- request
GET /lua
--- response_body
OK
--- no_error_log
[error]

View File

@ -0,0 +1,198 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_NGINX_PWD} ||= $pwd;
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
resolver 8.8.8.8 ipv6=off;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
require("resty.http").debug(true)
}
};
sub read_file {
my $infile = shift;
open my $in, $infile
or die "cannot open $infile for reading: $!";
my $cert = do { local $/; <$in> };
close $in;
$cert;
}
our $TestCertificate = read_file("t/cert/test.crt");
our $TestCertificateKey = read_file("t/cert/test.key");
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: Default HTTP port is not added to Host header
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("http://www.google.com")
';
}
--- request
GET /lua
--- no_error_log
[error]
--- error_log
Host: www.google.com
=== TEST 2: Default HTTPS port is not added to Host header
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua '
local http = require "resty.http"
local httpc = http.new()
httpc:set_timeouts(300, 1000, 1000)
local res, err = httpc:request_uri("https://www.google.com:443", { ssl_verify = false })
';
}
--- request
GET /lua
--- no_error_log
[error]
--- error_log
Host: www.google.com
=== TEST 3: Non-default HTTP port is added to Host header
--- http_config
lua_package_path "$TEST_NGINX_PWD/lib/?.lua;;";
error_log logs/error.log debug;
resolver 8.8.8.8;
server {
listen *:8080;
}
--- config
location /lua {
content_by_lua '
require("resty.http").debug(true)
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("http://127.0.0.1:8080")
';
}
--- request
GET /lua
--- no_error_log
[error]
--- error_log
Host: 127.0.0.1:8080
=== TEST 4: Non-default HTTPS port is added to Host header
--- http_config
lua_package_path "$TEST_NGINX_PWD/lib/?.lua;;";
error_log logs/error.log debug;
resolver 8.8.8.8;
server {
listen *:8080;
listen *:8081 ssl;
ssl_certificate ../html/test.crt;
ssl_certificate_key ../html/test.key;
}
--- config
location /lua {
content_by_lua '
require("resty.http").debug(true)
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("https://127.0.0.1:8081", { ssl_verify = false })
';
}
--- user_files eval
">>> test.key
$::TestCertificateKey
>>> test.crt
$::TestCertificate"
--- request
GET /lua
--- no_error_log
[error]
--- error_log
Host: 127.0.0.1:8081
=== TEST 5: No host header on a unix domain socket returns a useful error.
--- http_config eval: $::HttpConfig
--- config
location /a {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:connect("unix:.test.sock")
if not res then
ngx.log(ngx.ERR, err)
end
local res, err = httpc:request({ path = "/" })
if not res then
ngx.say(err)
else
ngx.say(res:read_body())
end
}
}
--- tcp_listen: .test.sock
--- tcp_reply: OK
--- request
GET /a
--- no_error_log
[error]
--- response_body
Unable to generate a useful Host header for a unix domain socket. Please provide one.
=== TEST 6: Host header is correct when http_proxy is used
--- http_config
lua_package_path "$TEST_NGINX_PWD/lib/?.lua;;";
error_log logs/error.log debug;
resolver 8.8.8.8;
server {
listen *:8080;
}
--- config
location /lua {
content_by_lua '
require("resty.http").debug(true)
local http = require "resty.http"
local httpc = http.new()
httpc:set_proxy_options({
http_proxy = "http://127.0.0.1:8080"
})
local res, err = httpc:request_uri("http://127.0.0.1:8081")
';
}
--- request
GET /lua
--- no_error_log
[error]
--- error_log
Host: 127.0.0.1:8081

View File

@ -0,0 +1,138 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_NGINX_PWD} ||= $pwd;
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;;";
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
no_diff();
run_tests();
__DATA__
=== TEST 1: Reuse an instance connecting on different ports / paths
--- http_config eval: $::HttpConfig
--- config
location /a {
content_by_lua_block {
local httpc = require("resty.http").new()
assert(httpc:connect("127.0.0.1", ngx.var.server_port),
"connect should return positively")
local res1 = httpc:request({ path = "/b" })
ngx.print(res1:read_body())
local res2 = httpc:request({ path = "/c" })
ngx.print(res2:read_body())
assert(res1 ~= res2, "responses should be unique tables")
assert(res1.headers ~= res2.headers, "headers should be unique tables")
assert(httpc:connect("127.0.0.1", 12345),
"connect should return positively")
local res3 = httpc:request({ path = "/b" })
ngx.print(res3:read_body())
assert(res3 ~= res2, "responses should be unique tables")
assert(res3.headers ~= res2.headers, "headers should be unique tables")
assert(httpc.keepalive == false, "keepalive flag should be false")
assert(httpc:connect("127.0.0.1", ngx.var.server_port),
"connect should return positively")
assert(httpc.keepalive == true, "keepalive flag should be true")
}
}
location /b {
echo "b";
}
location /c {
echo "c";
}
--- tcp_listen: 12345
--- tcp_reply
HTTP/1.0 200 OK
Date: Fri, 08 Aug 2016 08:12:31 GMT
Server: OpenResty
d
--- request
GET /a
--- response_body
b
c
d
--- no_error_log
[error]
=== TEST 2: Reuse input params table
--- http_config eval: $::HttpConfig
--- config
location /a {
content_by_lua_block {
local httpc = require("resty.http").new()
assert(httpc:connect("127.0.0.1", ngx.var.server_port),
"connect should return positively")
local params = {
path = "/b",
method = "HEAD",
}
local res, err = httpc:request(params)
assert(res, "request should return positvely")
assert(not params.headers, "params table should not be modified")
local res, err =
httpc:request_uri("http://127.0.0.1:"..ngx.var.server_port, params)
assert(res, "request_uri should return positvely")
assert(not params.headers, "params table should not be modified")
assert(httpc:connect("127.0.0.1", ngx.var.server_port),
"connect should return positively")
local pipeline_params = {
{ path = "/b", method = "POST" },
{ path = "/b", method = "HEAD" },
}
local res, err = httpc:request_pipeline(pipeline_params)
assert(res, "request_pipeline should return positively")
assert(not pipeline_params[1].headers and not pipeline_params[2].headers,
"params tables should not be modified")
}
}
location /b {
echo "b";
}
--- request
GET /a
--- response_body
--- no_error_log
[error]

View File

@ -0,0 +1,520 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_NGINX_PWD} ||= $pwd;
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
resolver 8.8.8.8;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
run_tests();
__DATA__
=== TEST 1: get_proxy_uri returns nil if proxy is not configured
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
ngx.say(httpc:get_proxy_uri("http", "example.com"))
}
}
--- request
GET /lua
--- response_body
nil
--- no_error_log
[error]
[warn]
=== TEST 2: get_proxy_uri matches no_proxy hosts correctly
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
-- helper that verifies get_proxy_uri works correctly with the given
-- scheme, host and no_proxy list
local function test_no_proxy(scheme, host, no_proxy)
httpc:set_proxy_options({
http_proxy = "http://http_proxy.example.com",
https_proxy = "http://https_proxy.example.com",
no_proxy = no_proxy
})
local proxy_uri = httpc:get_proxy_uri(scheme, host)
ngx.say("scheme: ", scheme, ", host: ", host, ", no_proxy: ", no_proxy, ", proxy_uri: ", proxy_uri)
end
-- All these match the no_proxy list
test_no_proxy("http", "example.com", nil)
test_no_proxy("http", "example.com", "*")
test_no_proxy("http", "example.com", "example.com")
test_no_proxy("http", "sub.example.com", "example.com")
test_no_proxy("http", "example.com", "example.com,example.org")
test_no_proxy("http", "example.com", "example.org,example.com")
-- Same for https for good measure
test_no_proxy("https", "example.com", nil)
test_no_proxy("https", "example.com", "*")
test_no_proxy("https", "example.com", "example.com")
test_no_proxy("https", "sub.example.com", "example.com")
test_no_proxy("https", "example.com", "example.com,example.org")
test_no_proxy("https", "example.com", "example.org,example.com")
-- Edge cases
-- example.com should match .example.com in the no_proxy list (legacy behavior of wget)
test_no_proxy("http", "example.com", ".example.com")
-- notexample.com should not match example.com in the no_proxy list (not a subdomain)
test_no_proxy("http", "notexample.com", "example.com")
}
}
--- request
GET /lua
--- response_body
scheme: http, host: example.com, no_proxy: nil, proxy_uri: http://http_proxy.example.com
scheme: http, host: example.com, no_proxy: *, proxy_uri: nil
scheme: http, host: example.com, no_proxy: example.com, proxy_uri: nil
scheme: http, host: sub.example.com, no_proxy: example.com, proxy_uri: nil
scheme: http, host: example.com, no_proxy: example.com,example.org, proxy_uri: nil
scheme: http, host: example.com, no_proxy: example.org,example.com, proxy_uri: nil
scheme: https, host: example.com, no_proxy: nil, proxy_uri: http://https_proxy.example.com
scheme: https, host: example.com, no_proxy: *, proxy_uri: nil
scheme: https, host: example.com, no_proxy: example.com, proxy_uri: nil
scheme: https, host: sub.example.com, no_proxy: example.com, proxy_uri: nil
scheme: https, host: example.com, no_proxy: example.com,example.org, proxy_uri: nil
scheme: https, host: example.com, no_proxy: example.org,example.com, proxy_uri: nil
scheme: http, host: example.com, no_proxy: .example.com, proxy_uri: nil
scheme: http, host: notexample.com, no_proxy: example.com, proxy_uri: http://http_proxy.example.com
--- no_error_log
[error]
[warn]
=== TEST 3: get_proxy_uri returns correct proxy URIs for http and https URIs
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
-- helper that configures the proxy opts as proived and checks what
-- get_proxy_uri says for the given scheme / host pair
local function test_get_proxy_uri(scheme, host, http_proxy, https_proxy)
httpc:set_proxy_options({
http_proxy = http_proxy,
https_proxy = https_proxy
})
local proxy_uri = httpc:get_proxy_uri(scheme, host)
ngx.say(
"scheme: ", scheme,
", host: ", host,
", http_proxy: ", http_proxy,
", https_proxy: ", https_proxy,
", proxy_uri: ", proxy_uri
)
end
-- http
test_get_proxy_uri("http", "example.com", "http_proxy", "https_proxy")
test_get_proxy_uri("http", "example.com", nil, "https_proxy")
-- https
test_get_proxy_uri("https", "example.com", "http_proxy", "https_proxy")
test_get_proxy_uri("https", "example.com", "http_proxy", nil)
}
}
--- request
GET /lua
--- response_body
scheme: http, host: example.com, http_proxy: http_proxy, https_proxy: https_proxy, proxy_uri: http_proxy
scheme: http, host: example.com, http_proxy: nil, https_proxy: https_proxy, proxy_uri: nil
scheme: https, host: example.com, http_proxy: http_proxy, https_proxy: https_proxy, proxy_uri: https_proxy
scheme: https, host: example.com, http_proxy: http_proxy, https_proxy: nil, proxy_uri: nil
--- no_error_log
[error]
[warn]
=== TEST 4: request_uri uses http_proxy correctly for non-standard destination ports
--- http_config
lua_package_path "$TEST_NGINX_PWD/lib/?.lua;;";
error_log logs/error.log debug;
resolver 8.8.8.8;
server {
listen *:8080;
location / {
content_by_lua_block {
ngx.print(ngx.req.raw_header())
}
}
}
--- config
location /lua {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
httpc:set_proxy_options({
http_proxy = "http://127.0.0.1:8080",
https_proxy = "http://127.0.0.1:8080"
})
-- request should go to the proxy server
local res, err = httpc:request_uri("http://127.0.0.1:1234/target?a=1&b=2")
if not res then
ngx.log(ngx.ERR, err)
return
end
ngx.status = res.status
ngx.say(res.body)
}
}
--- request
GET /lua
--- response_body_like
^GET http://127.0.0.1:1234/target\?a=1&b=2 HTTP/.+\r\nHost: 127.0.0.1:1234.+
--- no_error_log
[error]
[warn]
=== TEST 5: request_uri uses http_proxy correctly for standard destination port
--- http_config
lua_package_path "$TEST_NGINX_PWD/lib/?.lua;;";
error_log logs/error.log debug;
resolver 8.8.8.8;
server {
listen *:8080;
location / {
content_by_lua_block {
ngx.print(ngx.req.raw_header())
}
}
}
--- config
location /lua {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
httpc:set_proxy_options({
http_proxy = "http://127.0.0.1:8080",
https_proxy = "http://127.0.0.1:8080"
})
-- request should go to the proxy server
local res, err = httpc:request_uri("http://127.0.0.1/target?a=1&b=2")
if not res then
ngx.log(ngx.ERR, err)
return
end
-- the proxy echoed the raw request header and we shall pass it onwards
-- to the test harness
ngx.status = res.status
ngx.say(res.body)
}
}
--- request
GET /lua
--- response_body_like
^GET http://127.0.0.1/target\?a=1&b=2 HTTP/.+\r\nHost: 127.0.0.1.+
--- no_error_log
[error]
[warn]
=== TEST 6: request_uri makes a proper CONNECT request when proxying https resources
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
httpc:set_proxy_options({
http_proxy = "http://127.0.0.1:12345",
https_proxy = "http://127.0.0.1:12345"
})
-- Slight Hack: temporarily change the module global user agent to make it
-- predictable for this test case
local ua = http._USER_AGENT
http._USER_AGENT = "test_ua"
local res, err = httpc:request_uri("https://127.0.0.1/target?a=1&b=2")
http._USER_AGENT = ua
if not err then
-- The proxy request should fail as the TCP server listening returns
-- 403 response. We cannot really test the success case here as that
-- would require an actual reverse proxy to be implemented through
-- the limited functionality we have available in the raw TCP sockets
ngx.log(ngx.ERR, "unexpected success")
return
end
ngx.status = 403
ngx.say(err)
}
}
--- tcp_listen: 12345
--- tcp_query eval
qr/CONNECT 127.0.0.1:443 HTTP\/1.1\r\n.*Host: 127.0.0.1:443\r\n.*/s
# The reply cannot be successful or otherwise the client would start
# to do a TLS handshake with the proxied host and that we cannot
# do with these sockets
--- tcp_reply
HTTP/1.1 403 Forbidden
Connection: close
--- request
GET /lua
--- error_code: 403
--- no_error_log
[error]
[warn]
=== TEST 7: request_uri uses http_proxy_authorization option
--- http_config
lua_package_path "$TEST_NGINX_PWD/lib/?.lua;;";
error_log logs/error.log debug;
resolver 8.8.8.8;
server {
listen *:8080;
location / {
content_by_lua_block {
ngx.print(ngx.var.http_proxy_authorization or "no-header")
}
}
}
--- config
location /lua {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
httpc:set_proxy_options({
http_proxy = "http://127.0.0.1:8080",
http_proxy_authorization = "Basic ZGVtbzp0ZXN0",
https_proxy = "http://127.0.0.1:8080",
https_proxy_authorization = "Basic ZGVtbzpwYXNz"
})
-- request should go to the proxy server
local res, err = httpc:request_uri("http://127.0.0.1/")
if not res then
ngx.log(ngx.ERR, err)
return
end
-- the proxy echoed the proxy authorization header
-- to the test harness
ngx.status = res.status
ngx.say(res.body)
}
}
--- request
GET /lua
--- response_body
Basic ZGVtbzp0ZXN0
--- no_error_log
[error]
[warn]
=== TEST 8: request_uri uses https_proxy_authorization option
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
httpc:set_proxy_options({
http_proxy = "http://127.0.0.1:12345",
http_proxy_authorization = "Basic ZGVtbzp0ZXN0",
https_proxy = "http://127.0.0.1:12345",
https_proxy_authorization = "Basic ZGVtbzpwYXNz"
})
-- Slight Hack: temporarily change the module global user agent to make it
-- predictable for this test case
local ua = http._USER_AGENT
http._USER_AGENT = "test_ua"
local res, err = httpc:request_uri("https://127.0.0.1/target?a=1&b=2")
http._USER_AGENT = ua
if not err then
-- The proxy request should fail as the TCP server listening returns
-- 403 response. We cannot really test the success case here as that
-- would require an actual reverse proxy to be implemented through
-- the limited functionality we have available in the raw TCP sockets
ngx.log(ngx.ERR, "unexpected success")
return
end
ngx.status = 403
ngx.say(err)
}
}
--- tcp_listen: 12345
--- tcp_query eval
qr/CONNECT 127.0.0.1:443 HTTP\/1.1\r\n.*Proxy-Authorization: Basic ZGVtbzpwYXNz\r\n.*/s
# The reply cannot be successful or otherwise the client would start
# to do a TLS handshake with the proxied host and that we cannot
# do with these sockets
--- tcp_reply
HTTP/1.1 403 Forbidden
Connection: close
--- request
GET /lua
--- error_code: 403
--- no_error_log
[error]
[warn]
=== TEST 9: request_uri does not use http_proxy_authorization option when overridden
--- http_config
lua_package_path "$TEST_NGINX_PWD/lib/?.lua;;";
error_log logs/error.log debug;
resolver 8.8.8.8;
server {
listen *:8080;
location / {
content_by_lua_block {
ngx.print(ngx.var.http_proxy_authorization or "no-header")
}
}
}
--- config
location /lua {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
httpc:set_proxy_options({
http_proxy = "http://127.0.0.1:8080",
http_proxy_authorization = "Basic ZGVtbzp0ZXN0",
https_proxy = "http://127.0.0.1:8080",
https_proxy_authorization = "Basic ZGVtbzpwYXNz"
})
-- request should go to the proxy server
local res, err = httpc:request_uri("http://127.0.0.1/", {
headers = {
["Proxy-Authorization"] = "Basic ZGVtbzp3b3Jk"
}
})
if not res then
ngx.log(ngx.ERR, err)
return
end
-- the proxy echoed the proxy authorization header
-- to the test harness
ngx.status = res.status
ngx.say(res.body)
}
}
--- request
GET /lua
--- response_body
Basic ZGVtbzp3b3Jk
--- no_error_log
[error]
[warn]
=== TEST 10: request_uri does not use https_proxy_authorization option when overridden
--- http_config eval: $::HttpConfig
--- config
location /lua {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
httpc:set_proxy_options({
http_proxy = "http://127.0.0.1:12345",
http_proxy_authorization = "Basic ZGVtbzp0ZXN0",
https_proxy = "http://127.0.0.1:12345",
https_proxy_authorization = "Basic ZGVtbzpwYXNz"
})
-- Slight Hack: temporarily change the module global user agent to make it
-- predictable for this test case
local ua = http._USER_AGENT
http._USER_AGENT = "test_ua"
local res, err = httpc:request_uri("https://127.0.0.1/target?a=1&b=2", {
headers = {
["Proxy-Authorization"] = "Basic ZGVtbzp3b3Jk"
}
})
http._USER_AGENT = ua
if not err then
-- The proxy request should fail as the TCP server listening returns
-- 403 response. We cannot really test the success case here as that
-- would require an actual reverse proxy to be implemented through
-- the limited functionality we have available in the raw TCP sockets
ngx.log(ngx.ERR, "unexpected success")
return
end
ngx.status = 403
ngx.say(err)
}
}
--- tcp_listen: 12345
--- tcp_query eval
qr/CONNECT 127.0.0.1:443 HTTP\/1.1\r\n.*Proxy-Authorization: Basic ZGVtbzp3b3Jk\r\n.*/s
# The reply cannot be successful or otherwise the client would start
# to do a TLS handshake with the proxied host and that we cannot
# do with these sockets
--- tcp_reply
HTTP/1.1 403 Forbidden
Connection: close
--- request
GET /lua
--- error_code: 403
--- no_error_log
[error]
[warn]

View File

@ -0,0 +1,58 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_NGINX_PWD} ||= $pwd;
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
resolver 8.8.8.8;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
run_tests();
__DATA__
=== TEST 1: Old connect syntax still works
--- http_config eval: $::HttpConfig
--- config
location /a {
content_by_lua_block {
local http = require "resty.http"
local httpc = http.new()
local ok, err = httpc:connect("127.0.0.1", ngx.var.server_port)
assert(ok, err)
local res, err = httpc:request{
path = "/b"
}
ngx.status = res.status
ngx.print(res:read_body())
httpc:close()
}
}
location = /b {
echo "OK";
}
--- request
GET /a
--- response_body
OK
--- no_error_log
[error]
[warn]
--- error_log
[debug]

View File

@ -0,0 +1,157 @@
use Test::Nginx::Socket 'no_plan';
use Cwd qw(cwd);
my $pwd = cwd();
$ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
$ENV{TEST_COVERAGE} ||= 0;
our $HttpConfig = qq{
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
error_log logs/error.log debug;
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: Parse URI errors if malformed
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require("resty.http").new()
local parts, err = http:parse_uri("http:///example.com")
if not parts then ngx.say(err) end
';
}
--- request
GET /a
--- response_body
bad uri: http:///example.com
--- no_error_log
[error]
[warn]
=== TEST 2: Parse URI fills in defaults correctly
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require("resty.http").new()
local function test_uri(uri)
local scheme, host, port, path, query = unpack(http:parse_uri(uri, false))
ngx.say("scheme: ", scheme, ", host: ", host, ", port: ", port, ", path: ", path, ", query: ", query)
end
test_uri("http://example.com")
test_uri("http://example.com/")
test_uri("https://example.com/foo/bar")
test_uri("https://example.com/foo/bar?a=1&b=2")
test_uri("http://example.com?a=1&b=2")
test_uri("//example.com")
test_uri("//example.com?a=1&b=2")
test_uri("//example.com/foo/bar?a=1&b=2")
';
}
--- request
GET /a
--- response_body
scheme: http, host: example.com, port: 80, path: /, query:
scheme: http, host: example.com, port: 80, path: /, query:
scheme: https, host: example.com, port: 443, path: /foo/bar, query:
scheme: https, host: example.com, port: 443, path: /foo/bar, query: a=1&b=2
scheme: http, host: example.com, port: 80, path: /, query: a=1&b=2
scheme: http, host: example.com, port: 80, path: /, query:
scheme: http, host: example.com, port: 80, path: /, query: a=1&b=2
scheme: http, host: example.com, port: 80, path: /foo/bar, query: a=1&b=2
--- no_error_log
[error]
[warn]
=== TEST 3: Parse URI fills in defaults correctly, using backwards compatible mode
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require("resty.http").new()
local function test_uri(uri)
local scheme, host, port, path, query = unpack(http:parse_uri(uri))
ngx.say("scheme: ", scheme, ", host: ", host, ", port: ", port, ", path: ", path)
end
test_uri("http://example.com")
test_uri("http://example.com/")
test_uri("https://example.com/foo/bar")
test_uri("https://example.com/foo/bar?a=1&b=2")
test_uri("http://example.com?a=1&b=2")
test_uri("//example.com")
test_uri("//example.com?a=1&b=2")
test_uri("//example.com/foo/bar?a=1&b=2")
';
}
--- request
GET /a
--- response_body
scheme: http, host: example.com, port: 80, path: /
scheme: http, host: example.com, port: 80, path: /
scheme: https, host: example.com, port: 443, path: /foo/bar
scheme: https, host: example.com, port: 443, path: /foo/bar?a=1&b=2
scheme: http, host: example.com, port: 80, path: /?a=1&b=2
scheme: http, host: example.com, port: 80, path: /
scheme: http, host: example.com, port: 80, path: /?a=1&b=2
scheme: http, host: example.com, port: 80, path: /foo/bar?a=1&b=2
--- no_error_log
[error]
[warn]
=== TEST 4: IPv6 notation
--- http_config eval: $::HttpConfig
--- config
location = /a {
content_by_lua '
local http = require("resty.http").new()
local function test_uri(uri)
local scheme, host, port, path, query = unpack(http:parse_uri(uri, false))
ngx.say("scheme: ", scheme, ", host: ", host, ", port: ", port, ", path: ", path, ", query: ", query)
end
test_uri("http://[::1]")
test_uri("http://[::1]/")
test_uri("https://[::1]/foo/bar")
test_uri("https://[::1]/foo/bar?a=1&b=2")
test_uri("http://[::1]?a=1&b=2")
test_uri("//[0:0:0:0:0:0:0:0]")
test_uri("//[0:0:0:0:0:0:0:0]?a=1&b=2")
test_uri("//[0:0:0:0:0:0:0:0]/foo/bar?a=1&b=2")
';
}
--- request
GET /a
--- response_body
scheme: http, host: [::1], port: 80, path: /, query:
scheme: http, host: [::1], port: 80, path: /, query:
scheme: https, host: [::1], port: 443, path: /foo/bar, query:
scheme: https, host: [::1], port: 443, path: /foo/bar, query: a=1&b=2
scheme: http, host: [::1], port: 80, path: /, query: a=1&b=2
scheme: http, host: [0:0:0:0:0:0:0:0], port: 80, path: /, query:
scheme: http, host: [0:0:0:0:0:0:0:0], port: 80, path: /, query: a=1&b=2
scheme: http, host: [0:0:0:0:0:0:0:0], port: 80, path: /foo/bar, query: a=1&b=2
--- no_error_log
[error]
[warn]

View File

@ -0,0 +1,136 @@
use Test::Nginx::Socket::Lua 'no_plan';
use Cwd qw(abs_path realpath);
use File::Basename;
$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';
$ENV{TEST_NGINX_CERT_DIR} ||= dirname(realpath(abs_path(__FILE__)));
$ENV{TEST_COVERAGE} ||= 0;
my $realpath = realpath();
our $HttpConfig = qq{
lua_package_path "$realpath/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
init_by_lua_block {
if $ENV{TEST_COVERAGE} == 1 then
jit.off()
require("luacov.runner").init()
end
TEST_SERVER_SOCK = "unix:/$ENV{TEST_NGINX_HTML_DIR}/nginx.sock"
num_handshakes = 0
}
server {
listen unix:$ENV{TEST_NGINX_HTML_DIR}/nginx.sock ssl;
server_name example.com;
ssl_certificate $ENV{TEST_NGINX_CERT_DIR}/cert/test.crt;
ssl_certificate_key $ENV{TEST_NGINX_CERT_DIR}/cert/test.key;
ssl_session_tickets off;
server_tokens off;
}
};
no_long_string();
#no_diff();
run_tests();
__DATA__
=== TEST 1: connect returns session userdata
--- http_config eval: $::HttpConfig
--- config
server_tokens off;
resolver $TEST_NGINX_RESOLVER ipv6=off;
lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;
location /t {
content_by_lua_block {
local httpc = assert(require("resty.http").new())
local ok, err, session = assert(httpc:connect {
scheme = "https",
host = TEST_SERVER_SOCK,
})
assert(type(session) == "userdata" or type(session) == "cdata", "expected session to be userdata or cdata")
assert(httpc:close())
}
}
--- request
GET /t
--- no_error_log
[error]
[alert]
=== TEST 2: ssl_reused_session false does not return session userdata
--- http_config eval: $::HttpConfig
--- config
server_tokens off;
resolver $TEST_NGINX_RESOLVER ipv6=off;
lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;
location /t {
content_by_lua_block {
local httpc = assert(require("resty.http").new())
local ok, err, session = assert(httpc:connect {
scheme = "https",
host = TEST_SERVER_SOCK,
ssl_reused_session = false,
})
assert(type(session) == "boolean", "expected session to be a boolean")
assert(session == true, "expected session to be true")
assert(httpc:close())
}
}
--- request
GET /t
--- no_error_log
[error]
[alert]
=== TEST 3: ssl_reused_session accepts userdata
--- http_config eval: $::HttpConfig
--- config
server_tokens off;
resolver $TEST_NGINX_RESOLVER ipv6=off;
lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;
location /t {
content_by_lua_block {
local httpc = assert(require("resty.http").new())
local ok, err, session = assert(httpc:connect {
scheme = "https",
host = TEST_SERVER_SOCK,
})
assert(type(session) == "userdata" or type(session) == "cdata", "expected session to be userdata or cdata")
local httpc2 = assert(require("resty.http").new())
local ok, err, session2 = assert(httpc2:connect {
scheme = "https",
host = TEST_SERVER_SOCK,
ssl_reused_session = session,
})
assert(type(session2) == "userdata" or type(session2) == "cdata", "expected session2 to be userdata or cdata")
assert(httpc:close())
assert(httpc2:close())
}
}
--- request
GET /t
--- no_error_log
[error]
[alert]

View File

@ -0,0 +1,211 @@
use Test::Nginx::Socket::Lua 'no_plan';
#$ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8';
#$ENV{TEST_COVERAGE} ||= 0;
log_level 'debug';
no_long_string();
#no_diff();
sub read_file {
my $infile = shift;
open my $in, $infile
or die "cannot open $infile for reading: $!";
my $cert = do { local $/; <$in> };
close $in;
$cert;
}
our $MTLSCA = read_file("t/cert/mtls_ca.crt");
our $MTLSClient = read_file("t/cert/mtls_client.crt");
our $MTLSClientKey = read_file("t/cert/mtls_client.key");
our $TestCert = read_file("t/cert/test.crt");
our $TestKey = read_file("t/cert/test.key");
our $HtmlDir = html_dir;
use Cwd qw(cwd);
my $pwd = cwd();
our $mtls_http_config = <<"_EOC_";
lua_package_path "$pwd/lib/?.lua;/usr/local/share/lua/5.1/?.lua;;";
server {
listen unix:$::HtmlDir/mtls.sock ssl;
ssl_certificate $::HtmlDir/test.crt;
ssl_certificate_key $::HtmlDir/test.key;
ssl_client_certificate $::HtmlDir/mtls_ca.crt;
ssl_verify_client on;
server_tokens off;
server_name example.com;
location / {
echo -n "hello, \$ssl_client_s_dn";
}
}
_EOC_
our $mtls_user_files = <<"_EOC_";
>>> mtls_ca.crt
$::MTLSCA
>>> mtls_client.key
$::MTLSClientKey
>>> mtls_client.crt
$::MTLSClient
>>> test.crt
$::TestCert
>>> test.key
$::TestKey
_EOC_
run_tests();
__DATA__
=== TEST 1: Connection fails during handshake without client cert and key
--- http_config eval: $::mtls_http_config
--- config eval
"
lua_ssl_trusted_certificate $::HtmlDir/test.crt;
location /t {
content_by_lua_block {
local httpc = assert(require('resty.http').new())
local ok, err = httpc:connect {
scheme = 'https',
host = 'unix:$::HtmlDir/mtls.sock',
}
if ok and not err then
local res, err = assert(httpc:request {
method = 'GET',
path = '/',
headers = {
['Host'] = 'example.com',
},
})
ngx.status = res.status -- expect 400
end
httpc:close()
}
}
"
--- user_files eval: $::mtls_user_files
--- request
GET /t
--- error_code: 400
--- no_error_log
[error]
[warn]
=== TEST 2: Connection fails during handshake with not priv_key
--- http_config eval: $::mtls_http_config
--- SKIP
--- config eval
"
lua_ssl_trusted_certificate $::HtmlDir/test.crt;
location /t {
content_by_lua_block {
local f = assert(io.open('$::HtmlDir/mtls_client.crt'))
local cert_data = f:read('*a')
f:close()
local ssl = require('ngx.ssl')
local cert = assert(ssl.parse_pem_cert(cert_data))
local httpc = assert(require('resty.http').new())
local ok, err = httpc:connect {
scheme = 'https',
host = 'unix:$::HtmlDir/mtls.sock',
ssl_client_cert = cert,
ssl_client_priv_key = 'foo',
}
if ok and not err then
local res, err = assert(httpc:request {
method = 'GET',
path = '/',
headers = {
['Host'] = 'example.com',
},
})
ngx.say(res:read_body())
end
httpc:close()
}
}
"
--- user_files eval: $::mtls_user_files
--- request
GET /t
--- error_code: 200
--- error_log
could not set client certificate: bad client pkey type
--- response_body_unlike: hello, CN=foo@example.com,O=OpenResty,ST=California,C=US
=== TEST 3: Connection succeeds with client cert and key. SKIP'd for CI until feature is merged.
--- SKIP
--- http_config eval: $::mtls_http_config
--- config eval
"
lua_ssl_trusted_certificate $::HtmlDir/test.crt;
location /t {
content_by_lua_block {
local f = assert(io.open('$::HtmlDir/mtls_client.crt'))
local cert_data = f:read('*a')
f:close()
f = assert(io.open('$::HtmlDir/mtls_client.key'))
local key_data = f:read('*a')
f:close()
local ssl = require('ngx.ssl')
local cert = assert(ssl.parse_pem_cert(cert_data))
local key = assert(ssl.parse_pem_priv_key(key_data))
local httpc = assert(require('resty.http').new())
local ok, err = httpc:connect {
scheme = 'https',
host = 'unix:$::HtmlDir/mtls.sock',
ssl_client_cert = cert,
ssl_client_priv_key = key,
}
if ok and not err then
local res, err = assert(httpc:request {
method = 'GET',
path = '/',
headers = {
['Host'] = 'example.com',
},
})
ngx.say(res:read_body())
end
httpc:close()
}
}
"
--- user_files eval: $::mtls_user_files
--- request
GET /t
--- no_error_log
[error]
[warn]
--- response_body
hello, CN=foo@example.com,O=OpenResty,ST=California,C=US

View File

@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFpTCCA42gAwIBAgIUfTh89NyxxmbVwNZ/YFddssWc+WkwDQYJKoZIhvcNAQEL
BQAwWjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoM
CU9wZW5SZXN0eTEiMCAGA1UEAwwZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAe
Fw0xOTA5MTMyMjI4MTJaFw0zOTA5MDgyMjI4MTJaMFoxCzAJBgNVBAYTAlVTMRMw
EQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQKDAlPcGVuUmVzdHkxIjAgBgNVBAMM
GU9wZW5SZXN0eSBUZXN0aW5nIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQDcMg2DeV8z+0E2ZiXUax111lKzhAbMCK0RJlV9tAi+YdcsDR/t
zvvAZNGONUoewUuz/7E88oweh+Xi1GJtvd0DjB70y7tgpf5PUXovstWVwy7s5jZo
kgn62yi9ZOOZpjwnYTBviirtRTnZRwkzL6wF0xMyJjAbKBJuPMrMiyFdh82lt7wI
NS4mhyEdM0UiVVxfC2uzsddTOcOJURfGbW7UZm4Xohzq4QZ8geQj2OT5YTqw7dZ7
Xxre5H7IcNcAh+vIk5SEBV1WE+S5MnFly7gaLYNc49OSfz5Hcpv59Vr+4bZ+olbW
nQ/uU8BQovtkW6pjuT8nC4OKs2e8osoAZuk0rFS1uC501C+yES48mzaU8ttAidu6
nb/JgsdkrnJQeTc5rAoER4M2ne5kqtEXN8wzf3/sazo2PLywbfrUXUTV6kJilrGr
RkBN+fr6HTBkf+ooQMBOQPTojUdwbR86CLCyiJov2bzmBfGcOgSakv59S+uvUZFp
FLTiahuzLfcgYsG3UKQA47pYlNdUqP8vCCaf1nwmqjx2KS3Z/YFnO/gQgtY+f0Bh
UpnUDv+zBxpVFfVCyxByEsDPdwDkqLSwB6+YZINl36S48iXpoPhNXIYmO6GnhNWV
k2/RyCDTxEO+MbXHVg6iyIVHJWth7m18vl4uuSK/LbJHV9Q9Z7G99DQ0NwIDAQAB
o2MwYTAdBgNVHQ4EFgQUuoo+ehdlDFcQU+j5qONMKh0NtFQwHwYDVR0jBBgwFoAU
uoo+ehdlDFcQU+j5qONMKh0NtFQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
BAMCAYYwDQYJKoZIhvcNAQELBQADggIBAM7c9q41K8306lfAVLqtbtaeETy+xxYG
XE2HfFW1IuukrXQ8d/JH4stL/HcHJzzhHPf5p3ja3Snu9zPmTk3pgUPDYPZf57tR
NCqwxjn6blwXWlzQqSavto9KAx3IWHuj0OTrZz/a1KPb9NGvatBhgthyRCRTbvhL
OA5tveuYSHb724cp3NZ1xaTQmDZsSgHCoCJ/7RnlbcJ7RsKCOzCWNFRomH410vdv
TajkUBlEC4OC1RIvxuVePHHb1ogbbe93SA/9mzw/E5SfoeF3mvByN4Ay8awXbNlH
26RfuIdGc4fZRc/87s4yPwhYScZBG+pHO0gn42E0FyiG6Jp3rhHMH5Sa2hNlPMpn
hYMaA6zQI4n/3AeFNM0VGxA+Yg/Al2WpXEJARrZqMW/qcrdMcPj5WeY6Tb6er04S
kfImwhMIajl3nNc9tHoad8r2VuMWMltH/dnWuEdo+pPdIY3fdJdyQeoLQDDLEQwL
AYrFy4uzKfQogfQBIHRdIMZTJh5v3mAFDpK59I5yzSt1GtUnFMC5MVOg+LbOo5UW
FCtwaW5EZiTszmakvvWMMZe9HwZMYNCeSGGtiPA/GA2zNci/n2TEcB11HgiY52y2
E/40nS61oL81zMwhV7l5psgJxQ2ORsKRJPHjADwvwh3xyCEJgVyBRCDX7J3PpAUO
79DprjVU8t7p
-----END CERTIFICATE-----

View File

@ -0,0 +1,111 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4096 (0x1000)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=California, O=OpenResty, CN=OpenResty Testing Root CA
Validity
Not Before: Sep 13 22:30:49 2019 GMT
Not After : Sep 10 22:30:49 2029 GMT
Subject: C=US, ST=California, O=OpenResty, CN=foo@example.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:d0:8c:1e:2c:25:7f:00:9d:8a:3d:8a:f3:b5:1d:
6b:24:f5:ac:35:7d:cd:b7:d0:af:db:88:7c:ee:82:
46:16:47:f3:43:08:a1:04:6e:0d:3e:ce:69:fa:d5:
89:14:82:20:f1:47:f2:38:c8:ab:ea:2f:1e:f0:15:
04:c0:f4:8b:3c:c3:d4:78:56:87:4c:f1:70:ac:11:
86:2e:c4:6a:6d:10:84:27:81:ca:2a:8b:85:3e:62:
13:5e:40:6c:19:e4:49:3d:f3:de:aa:e8:5e:11:a1:
f2:66:83:6a:40:d1:34:c5:bf:b8:cb:97:7c:6a:ea:
46:bf:17:be:32:8d:a8:31:56:e5:8b:6d:08:03:d0:
44:69:b9:af:1e:15:1d:a5:64:9e:12:84:83:db:d9:
c6:71:90:3b:c2:7b:41:21:57:af:70:15:0b:56:59:
21:a6:4e:46:71:66:90:f1:ef:bc:b2:48:f9:8b:ea:
e5:72:4a:ba:4a:ae:2d:74:0b:33:03:f6:2e:47:0f:
56:a4:00:e8:1e:62:cb:b8:af:9c:98:1a:89:7c:d0:
a3:7a:5a:e1:84:50:64:e4:5d:a5:70:a4:69:54:c4:
f4:76:44:a2:be:1b:ef:dc:a3:d8:1d:0d:30:a2:d4:
79:fb:39:76:ab:b7:18:f2:f7:92:f8:81:83:94:b8:
11:b1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Cert Type:
SSL Client, S/MIME
Netscape Comment:
OpenSSL Generated Client Certificate
X509v3 Subject Key Identifier:
D2:E4:F5:21:1C:17:A4:FF:13:F4:1A:28:A8:A7:DC:C6:DE:89:A0:31
X509v3 Authority Key Identifier:
keyid:BA:8A:3E:7A:17:65:0C:57:10:53:E8:F9:A8:E3:4C:2A:1D:0D:B4:54
X509v3 Key Usage: critical
Digital Signature, Non Repudiation, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Client Authentication, E-mail Protection
X509v3 Subject Alternative Name:
email:foo@example.com, email:bar@example.com
Signature Algorithm: sha256WithRSAEncryption
47:77:51:37:00:f7:28:da:c4:d9:4e:38:c4:ec:ea:24:c9:83:
36:4c:90:93:a7:b2:2b:10:bf:75:df:0b:72:d8:e7:4f:4f:68:
e5:32:2f:35:89:17:95:5c:bb:43:fc:70:89:46:08:43:61:ac:
41:62:84:01:94:88:d1:dc:8a:bd:30:2c:18:eb:51:79:0b:b7:
1b:b6:49:df:c9:85:55:f6:73:9f:b7:83:99:52:23:fe:e6:bd:
09:da:90:b9:e2:9b:68:c4:df:bd:fe:23:94:55:34:be:0d:7d:
84:0c:53:69:2a:0f:3c:47:68:34:3f:2a:3f:89:3f:3e:d3:26:
ce:b7:58:bc:d0:6f:ee:f8:bd:5d:c6:48:ae:a0:6c:1f:6d:e0:
66:93:7d:db:3c:07:e6:15:ae:aa:e3:d0:3d:ef:04:b6:dd:53:
16:93:61:70:e9:af:c0:e9:1d:ff:2b:e5:0a:03:56:48:3f:1c:
dc:fe:1b:a6:6d:f6:54:ab:41:e5:3b:5b:ab:f5:81:10:46:26:
bb:ea:d7:0e:33:b1:5e:30:4d:81:86:63:9a:4a:4f:1e:44:b9:
c2:c6:08:4e:da:fa:3a:55:da:96:7c:01:f6:d5:e8:3b:ba:e9:
31:3b:1c:51:39:1a:59:f0:e0:c7:17:2e:f6:18:9d:ec:a7:48:
30:b8:4c:6d:e5:4a:4f:43:41:cb:0e:6b:ac:ad:87:44:90:76:
85:23:2b:eb:8f:97:4b:22:13:60:20:3a:37:a4:dc:74:7d:85:
3d:a1:f5:1a:03:f6:d5:78:c7:bc:9b:09:f2:c8:05:27:43:2a:
ac:50:21:3a:ee:83:2d:db:02:6f:c7:91:de:63:d6:36:7d:7a:
9f:1f:fb:48:62:f4:fb:8e:3a:ea:61:9b:3c:03:f9:f8:a5:df:
1b:02:14:2c:de:e6:e3:47:d2:44:65:94:1a:c6:e1:fd:ba:8d:
b6:f8:93:a9:46:46:26:79:b0:bf:57:a8:a2:20:66:56:7e:c9:
f5:a4:0b:5e:76:70:0a:47:a4:db:45:2e:15:99:69:f9:6b:14:
93:2a:0a:b6:ee:53:a6:b9:02:9b:a2:25:37:1e:37:70:a2:7c:
7f:c3:ce:98:17:2f:9b:5b:fa:6f:ae:d8:0e:d4:6a:b2:03:5a:
fe:ba:4b:7f:f6:98:20:ea:cb:be:17:34:e0:43:74:d1:0c:e5:
d4:cc:5d:13:41:d3:5e:a4:f6:94:f7:15:b8:15:a9:65:f8:28:
3f:da:ef:b2:30:34:6d:96:3a:7a:f4:20:ec:9e:62:13:36:f1:
a7:04:e1:7a:d2:33:20:f6:61:4a:68:44:cb:92:d7:62:f0:e4:
70:f0:a5:e3:dd:2f:e2:a3
-----BEGIN CERTIFICATE-----
MIIFGTCCAwGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEiMCAGA1UE
AwwZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAeFw0xOTA5MTMyMjMwNDlaFw0y
OTA5MTAyMjMwNDlaMFAxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh
MRIwEAYDVQQKDAlPcGVuUmVzdHkxGDAWBgNVBAMMD2Zvb0BleGFtcGxlLmNvbTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANCMHiwlfwCdij2K87UdayT1
rDV9zbfQr9uIfO6CRhZH80MIoQRuDT7OafrViRSCIPFH8jjIq+ovHvAVBMD0izzD
1HhWh0zxcKwRhi7Eam0QhCeByiqLhT5iE15AbBnkST3z3qroXhGh8maDakDRNMW/
uMuXfGrqRr8XvjKNqDFW5YttCAPQRGm5rx4VHaVknhKEg9vZxnGQO8J7QSFXr3AV
C1ZZIaZORnFmkPHvvLJI+Yvq5XJKukquLXQLMwP2LkcPVqQA6B5iy7ivnJgaiXzQ
o3pa4YRQZORdpXCkaVTE9HZEor4b79yj2B0NMKLUefs5dqu3GPL3kviBg5S4EbEC
AwEAAaOB8jCB7zAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIFoDAzBglghkgB
hvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgQ2xpZW50IENlcnRpZmljYXRlMB0G
A1UdDgQWBBTS5PUhHBek/xP0Giiop9zG3omgMTAfBgNVHSMEGDAWgBS6ij56F2UM
VxBT6Pmo40wqHQ20VDAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYwFAYIKwYBBQUH
AwIGCCsGAQUFBwMEMCsGA1UdEQQkMCKBD2Zvb0BleGFtcGxlLmNvbYEPYmFyQGV4
YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQBHd1E3APco2sTZTjjE7OokyYM2
TJCTp7IrEL913wty2OdPT2jlMi81iReVXLtD/HCJRghDYaxBYoQBlIjR3Iq9MCwY
61F5C7cbtknfyYVV9nOft4OZUiP+5r0J2pC54ptoxN+9/iOUVTS+DX2EDFNpKg88
R2g0Pyo/iT8+0ybOt1i80G/u+L1dxkiuoGwfbeBmk33bPAfmFa6q49A97wS23VMW
k2Fw6a/A6R3/K+UKA1ZIPxzc/humbfZUq0HlO1ur9YEQRia76tcOM7FeME2BhmOa
Sk8eRLnCxghO2vo6VdqWfAH21eg7uukxOxxRORpZ8ODHFy72GJ3sp0gwuExt5UpP
Q0HLDmusrYdEkHaFIyvrj5dLIhNgIDo3pNx0fYU9ofUaA/bVeMe8mwnyyAUnQyqs
UCE67oMt2wJvx5HeY9Y2fXqfH/tIYvT7jjrqYZs8A/n4pd8bAhQs3ubjR9JEZZQa
xuH9uo22+JOpRkYmebC/V6iiIGZWfsn1pAtednAKR6TbRS4VmWn5axSTKgq27lOm
uQKboiU3Hjdwonx/w86YFy+bW/pvrtgO1GqyA1r+ukt/9pgg6su+FzTgQ3TRDOXU
zF0TQdNepPaU9xW4Fall+Cg/2u+yMDRtljp69CDsnmITNvGnBOF60jMg9mFKaETL
ktdi8ORw8KXj3S/iow==
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA0IweLCV/AJ2KPYrztR1rJPWsNX3Nt9Cv24h87oJGFkfzQwih
BG4NPs5p+tWJFIIg8UfyOMir6i8e8BUEwPSLPMPUeFaHTPFwrBGGLsRqbRCEJ4HK
KouFPmITXkBsGeRJPfPequheEaHyZoNqQNE0xb+4y5d8aupGvxe+Mo2oMVbli20I
A9BEabmvHhUdpWSeEoSD29nGcZA7wntBIVevcBULVlkhpk5GcWaQ8e+8skj5i+rl
ckq6Sq4tdAszA/YuRw9WpADoHmLLuK+cmBqJfNCjelrhhFBk5F2lcKRpVMT0dkSi
vhvv3KPYHQ0wotR5+zl2q7cY8veS+IGDlLgRsQIDAQABAoIBAEpoY++OZVT74LH6
nN+XIn5qZUokm7yk6cnjVefndUhH3aSiNIkXFwS8sxV7ENDPaR+NcwANoUEKFPjG
Fw8dcXx5xpo1DUtHrdLG4eBX1j0Zsn1CErbBVwYeChkL1UYbrII9O8ow5DdYV9t5
sfR0cGbJ9A43+31OH3XY69SvtD39xItgxK0Wg4Ciz475kCvG51Q1iBWAkm4koXhz
VCha4wghs81wJ28HRMFZAFf2C+72rk6EypMUX2dYirvW/+7zONirk298NDMAOSBh
mRWyPV8qipYx42hBQ9vSVm0UVb0ZbqVomKKUZfj11LL6Ad/OzCyVAiNLXyZREV6r
d324Bu0CgYEA/YPsE6p6H3MTPIoVTnsyyQw0pNmXtIAuYTgSveeuPoTYn3ZBWoGN
iLpbnW4EH3xNKfrdMjbqqLls1iwm7/ZAP5klAuL4s10onrcjMt65fyfa3Lw1gavG
SUFFdsueH2k3FohqNsbQUSXZILVQnXsRoldi38b7NKrAqABcEMAIqXMCgYEA0pde
nt4aMmrGBRPLnjCs1UlC5PbXzCE8XxxQ7HZKx4Sy5ErQ0EW1wzF6c0fEYI7i+j1/
ESKqekzc5ue0T8acoioB+VUybO1oxQZsZUPY7roqXOYwZH9LQOdPYUOh9k33CZHw
6KFfx8bKCpdXn7FkwR2UUtCSp/6CZcyYr89Qn0sCgYAQ0L5I86bUDTL6cgJFyWAt
+7RGNvScEWCCLFD57bMeDHu93/8nvK4hopLPF2wIlpsbrLsdSI06EcqJTjZq9j9+
uG6/CUULyKMYG/emuSU+rOsUdxtpdXZah4zO+2SKmtT/lp7M8VUB/OuxArXNLEuY
JAm35B/nd2f9/MAekE5CxwKBgQCV660w7G0590mB09XhiEWCkeVNm22FpSOVklMK
BCy4XX/9hkWh//6mN1M1PqJPG2n7PEx5pnQ3HQEmYU28fWiFCeLd3glIArvTh/8j
GGoXifEescFByl2IlyOr2roy3s4/weX/tuK5Fow/ff6jcWaJFMXDLzk437d1QXJx
tuVugQKBgByfr2eakXFQvAVGJUfVXA3M2BoBODZEPYTgryVMoEEduFy0HZiw4xKi
Dngwewy6/UJMAGA+8ak9Ca367FxnegZU9knm6ujYVyhU5WzbKpR8v7OaUP8d5icq
rCZZtglG0c8XfVpJjR4FsKA/qrFvKZpu5NdEw3o5/LSrV4HjqZQ6
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,112 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4097 (0x1001)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=California, O=OpenResty, CN=OpenResty Testing Root CA
Validity
Not Before: Sep 13 22:32:03 2019 GMT
Not After : Sep 10 22:32:03 2029 GMT
Subject: C=US, ST=California, O=OpenResty, CN=example.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:bb:69:2b:30:43:b0:b4:cc:84:3f:22:39:65:65:
6a:bd:75:a4:2b:7d:f7:ec:e4:12:e8:d1:c4:ce:7e:
4e:54:5c:22:cc:d2:18:7f:3b:9e:cb:70:d9:7d:79:
8f:05:93:b6:9f:2f:d5:33:d7:98:a2:ed:c5:00:93:
e4:ca:bc:cb:f0:e1:63:3e:07:6b:38:6f:4d:09:45:
f1:a1:3b:a3:ca:c0:47:c1:a1:0a:f8:c9:bb:c7:da:
26:9d:d3:0b:35:24:01:3e:16:14:2e:44:38:8c:c9:
09:02:41:9e:b6:fb:0c:aa:fc:d6:44:5e:27:ab:aa:
d5:c3:68:e1:dd:57:06:6c:4f:f6:24:33:a8:2b:49:
60:82:0e:15:aa:55:9f:61:cc:74:39:7e:9f:a6:4f:
71:4a:8b:eb:43:dd:c2:f7:90:38:df:a6:a6:a8:f6:
77:bc:9e:54:69:30:83:4c:2a:eb:b8:62:7c:c7:14:
84:9e:f3:e1:4a:15:33:51:65:a3:af:9d:09:c6:b8:
89:30:a3:d2:18:e9:dc:5d:6b:ea:68:ca:8b:5c:e4:
3b:fe:32:7f:48:c3:4c:f0:b5:06:f6:23:97:3e:f2:
50:90:68:26:39:6d:b2:e2:53:89:71:6a:48:f0:f1:
fc:89:3c:6d:db:87:6c:79:23:ed:87:5d:c5:fa:8a:
0d:b9
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Cert Type:
SSL Server
Netscape Comment:
OpenSSL Generated Server Certificate
X509v3 Subject Key Identifier:
8C:8E:18:2C:13:84:C9:2A:61:6B:73:3F:18:76:A4:85:55:5F:5C:5F
X509v3 Authority Key Identifier:
keyid:BA:8A:3E:7A:17:65:0C:57:10:53:E8:F9:A8:E3:4C:2A:1D:0D:B4:54
DirName:/C=US/ST=California/O=OpenResty/CN=OpenResty Testing Root CA
serial:7D:38:7C:F4:DC:B1:C6:66:D5:C0:D6:7F:60:57:5D:B2:C5:9C:F9:69
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
Signature Algorithm: sha256WithRSAEncryption
80:9f:3e:f5:8b:50:ee:cb:e4:c2:f0:16:01:07:a1:af:76:bc:
da:c8:cd:24:e9:63:df:d3:47:28:8c:7f:58:14:5d:d4:fd:44:
16:c3:06:15:be:90:ec:1c:8f:78:34:11:e7:cc:86:d8:2a:a2:
e5:99:70:83:76:4a:65:a4:e1:9a:68:20:29:c0:7a:c6:4a:08:
b3:74:c3:53:b5:7b:79:92:f1:99:b5:a1:a3:90:ce:9a:cb:26:
a5:a6:33:de:74:98:99:ec:18:d1:1e:41:be:f8:c3:d2:8d:aa:
07:de:9a:97:28:0d:bf:70:ac:2b:cf:b7:ff:bc:ac:e4:16:0c:
1c:03:a7:5a:2d:64:0d:90:16:bd:97:c3:1f:f5:bf:a9:fa:15:
d1:e0:d4:0d:f7:b3:51:23:ce:ad:16:4f:41:72:17:aa:01:d5:
44:e2:9e:d5:ce:ea:54:98:04:43:14:2e:51:4b:c7:d9:21:4f:
e1:a4:fa:dd:e0:f0:82:ec:6f:9f:be:a2:3c:3b:85:f7:6d:96:
ee:0d:e6:08:2b:1b:be:06:a4:b7:5f:a3:f2:f2:b9:d0:5a:8f:
90:86:1a:f4:7a:9f:c8:ae:09:1d:60:a2:8b:e0:0b:f6:00:21:
d9:df:33:4b:39:75:b6:64:9b:c7:df:e4:85:7a:ae:df:72:8c:
8b:7e:98:8e:47:0a:27:1f:8e:2c:11:7f:7b:fc:a0:db:1b:6e:
f6:de:4e:85:ac:30:e6:e8:6a:7a:e6:f9:f4:18:0a:c6:ad:1c:
e1:0c:dd:e0:e0:8d:5a:d7:08:34:e7:22:b4:44:bd:99:39:b1:
71:74:3f:7c:aa:65:f5:37:46:85:d3:79:f7:a8:35:8d:2b:30:
99:d2:47:ce:a6:74:eb:f3:9f:d3:9a:4e:99:96:50:7b:ba:22:
c8:72:47:d4:da:6e:9a:73:01:3c:89:e9:3f:56:17:b7:ba:22:
71:db:66:a2:d2:fb:33:51:36:f6:b6:f2:5b:32:70:9d:e7:e3:
36:d6:ae:cb:9b:62:ef:69:c7:f7:ba:95:49:16:f5:7c:d9:29:
bb:0a:02:b1:6b:72:15:ab:2c:27:7b:c8:bc:f6:15:1f:fa:ae:
08:fd:e0:11:36:b1:ab:9c:c8:11:d1:d3:0d:7d:49:4e:ca:e6:
73:ee:0d:c3:8d:6f:f5:a4:fe:a1:af:6b:91:f7:53:fd:10:df:
77:dd:ef:ec:7b:cf:32:75:df:04:8a:d1:a1:f7:36:68:ee:65:
e3:43:90:37:43:e8:d1:a8:e2:90:5c:1c:75:0a:29:94:4a:6a:
9b:89:28:43:bd:85:56:0d:f1:2b:44:bd:e6:7a:4c:b7:85:10:
77:b7:a8:0f:33:29:a7:26
-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAoMCU9wZW5SZXN0eTEiMCAGA1UE
AwwZT3BlblJlc3R5IFRlc3RpbmcgUm9vdCBDQTAeFw0xOTA5MTMyMjMyMDNaFw0y
OTA5MTAyMjMyMDNaMEwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh
MRIwEAYDVQQKDAlPcGVuUmVzdHkxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu2krMEOwtMyEPyI5ZWVqvXWkK333
7OQS6NHEzn5OVFwizNIYfzuey3DZfXmPBZO2ny/VM9eYou3FAJPkyrzL8OFjPgdr
OG9NCUXxoTujysBHwaEK+Mm7x9omndMLNSQBPhYULkQ4jMkJAkGetvsMqvzWRF4n
q6rVw2jh3VcGbE/2JDOoK0lggg4VqlWfYcx0OX6fpk9xSovrQ93C95A436amqPZ3
vJ5UaTCDTCrruGJ8xxSEnvPhShUzUWWjr50JxriJMKPSGOncXWvqaMqLXOQ7/jJ/
SMNM8LUG9iOXPvJQkGgmOW2y4lOJcWpI8PH8iTxt24dseSPth13F+ooNuQIDAQAB
o4IBNTCCATEwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4
QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNV
HQ4EFgQUjI4YLBOEySpha3M/GHakhVVfXF8wgZcGA1UdIwSBjzCBjIAUuoo+ehdl
DFcQU+j5qONMKh0NtFShXqRcMFoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
Zm9ybmlhMRIwEAYDVQQKDAlPcGVuUmVzdHkxIjAgBgNVBAMMGU9wZW5SZXN0eSBU
ZXN0aW5nIFJvb3QgQ0GCFH04fPTcscZm1cDWf2BXXbLFnPlpMA4GA1UdDwEB/wQE
AwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAgEAgJ8+
9YtQ7svkwvAWAQehr3a82sjNJOlj39NHKIx/WBRd1P1EFsMGFb6Q7ByPeDQR58yG
2Cqi5Zlwg3ZKZaThmmggKcB6xkoIs3TDU7V7eZLxmbWho5DOmssmpaYz3nSYmewY
0R5BvvjD0o2qB96alygNv3CsK8+3/7ys5BYMHAOnWi1kDZAWvZfDH/W/qfoV0eDU
DfezUSPOrRZPQXIXqgHVROKe1c7qVJgEQxQuUUvH2SFP4aT63eDwguxvn76iPDuF
922W7g3mCCsbvgakt1+j8vK50FqPkIYa9HqfyK4JHWCii+AL9gAh2d8zSzl1tmSb
x9/khXqu33KMi36YjkcKJx+OLBF/e/yg2xtu9t5Ohaww5uhqeub59BgKxq0c4Qzd
4OCNWtcINOcitES9mTmxcXQ/fKpl9TdGhdN596g1jSswmdJHzqZ06/Of05pOmZZQ
e7oiyHJH1NpumnMBPInpP1YXt7oicdtmotL7M1E29rbyWzJwnefjNtauy5ti72nH
97qVSRb1fNkpuwoCsWtyFassJ3vIvPYVH/quCP3gETaxq5zIEdHTDX1JTsrmc+4N
w41v9aT+oa9rkfdT/RDfd93v7HvPMnXfBIrRofc2aO5l40OQN0Po0ajikFwcdQop
lEpqm4koQ72FVg3xK0S95npMt4UQd7eoDzMppyY=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAu2krMEOwtMyEPyI5ZWVqvXWkK3337OQS6NHEzn5OVFwizNIY
fzuey3DZfXmPBZO2ny/VM9eYou3FAJPkyrzL8OFjPgdrOG9NCUXxoTujysBHwaEK
+Mm7x9omndMLNSQBPhYULkQ4jMkJAkGetvsMqvzWRF4nq6rVw2jh3VcGbE/2JDOo
K0lggg4VqlWfYcx0OX6fpk9xSovrQ93C95A436amqPZ3vJ5UaTCDTCrruGJ8xxSE
nvPhShUzUWWjr50JxriJMKPSGOncXWvqaMqLXOQ7/jJ/SMNM8LUG9iOXPvJQkGgm
OW2y4lOJcWpI8PH8iTxt24dseSPth13F+ooNuQIDAQABAoIBABnT/KfCLHA+X1t0
FATtXTCPLfjwe2KibBi6EC2FKrZlnEYuDkI6rT/MZaztO9DA8sItjWx/ogGSUzwp
JbbrHhAsf8jkrNoyPKOyiAJ4fbJLnZgJ4cE3zDFW10uY8kp4k9NCp7VYoZKFgkBV
WtJM9wn5nm39q+n0uVEc+0PN4oy6m54Aqb1HqVCyXFp+/pVhL6PtgaClbqJd3oKV
0/HLWfWaI3nvV6ltAphUfPoCmYIUtSl90sRgSeEaJ61UZXh0OkhhtD7Iw/JUlHDk
a0J7owrh0Wf1kDsaSn+j1ba8MELsspFYYVm0gAMKAvRXbVrYgUMdb+HVdZ0odULl
ezFWeAECgYEA9nnoZs+PzKWNxTzYPtgvvrSLmpXLzNrs/p41JUhy6GjQIixTSBFy
WHkjwu0k2fvRgODfcaetAyK6sV4uTpRgqUhtFSeyNMelZ2yiIEqvhUrtHoVov1C+
BqwwlUnmkQZNQODXOpKCvnqnOaPwMILKLtxDGmPtW0tCTR2dVVaht6ECgYEAwqb/
h0Fh3YtykOnhV8wOZRrpVr8jS1RIgg/hklt2xh6+OYtL16sKFaLBF/BhzZRBapqd
fB2Cx3B6rxZ5PLTse8yjEvjt6Ly7TusYWpaKbYKFnnEbmdsm5sBepuLUv4AoMYbk
99ZejFcQI2gNbzX7eIrFitCQGxT+Wu7Gncv+vxkCgYBvAYCVrS2KcZVkG38Y7qyy
KwYk3QoofQD3u7Eb1YFLAsmaWnQ3pQPmrMhaZguO0UcN0DlSKr5VBzMl5tDcOx89
noziVjqAYtovtlFeUcSzN4eLk3IVl/u9bZeD5QCemEP60Eie7JVNzFe8MgVfE8iT
Skg+fnrL/x0hNhFB+f5jgQKBgDgOEX4o5P8A3nA++gbnm6mgE1xI1OgnkG3sFuCn
+E9boRo/NAsalV/fq82yCuhB7oi9l+abNQMsMBhl12oVDBkmuDuJdjHUz/gNGclU
mu6obMRQ/ErVYqGG+nsCzZOMW4bPuvZoRHgTxnD70QqauB1hkTvFjgpOhGU5Z/cf
PPBZAoGBAJQK7NF6VoF9nm5CT8vufrQ2vvp2aiLdOLLx5JXt/6seEnPZWtEvjp8/
+ExIsfOIaU5elhv8ze8iKmRP9f04XdWpbRm6k6AR5cOkkQQ1oO7N9abU7KbD/gqX
pJIWOlaUrbKO4Dprx7HyMYYPs9mu/UoF0Dvd/+bYXM5ZKiFrQ3Ly
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDYzCCAkugAwIBAgIUXCmnoPKJ60jFkycVZ04mVj3B8aswDQYJKoZIhvcNAQEL
BQAwQTELMAkGA1UEBhMCUFQxDjAMBgNVBAgMBVBvcnRvMQ4wDAYDVQQHDAVQb3J0
bzESMBAGA1UECgwJbGVkZ2V0ZWNoMB4XDTIyMTIxNTEyMDQzMVoXDTMyMDkxMzEy
MDQzMVowQTELMAkGA1UEBhMCUFQxDjAMBgNVBAgMBVBvcnRvMQ4wDAYDVQQHDAVQ
b3J0bzESMBAGA1UECgwJbGVkZ2V0ZWNoMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAqzJINgQtchR799ahPpu2gweHCmx7cytQ59ZFW4J3QBRIB+DFnLSM
fJa797s0Sl6f+t9pT3QLYjbdNK+R60HGd5tM31cEIa518AcDokfkzGo/clY+dbs4
OcFe10HxAlXpu6S5/yiQ0u4CIf1uQSsS53kNpaRWcEz6Z/sw80ksCEnYSfD3YXEh
sJm41i5Hd4F2/Y1WrMg82YGZG7Y81cM9LgUxKcikTm4JnEn9G1yg56hSbKs1a0E0
J9Gk84lufWpiUqpX9ASsUPttnYzgljo24x7zeNEaXsVOKgu+88Cc+bBn7AORGXM4
YtYbxVGhAgPf0vOalNl/kDwfE1jwBz0r6QIDAQABo1MwUTAdBgNVHQ4EFgQUxuo7
FdlD/iQ7cMnORpKFQ0JW9I4wHwYDVR0jBBgwFoAUxuo7FdlD/iQ7cMnORpKFQ0JW
9I4wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAfINyfrlE/f3W
cTNFiVMzqCa+oeNtvlkaCKgtjJdow8xwAc/Mxae76dkN3Mh8yegKV9+QRY6rw6q0
dfma/Rg0tJgrI9El64XdGRcMpwGdtujZbE/bCGTJwLpcIM051cr/NCtYYduM2RU8
i5uc8z5sQbMIJHdwDDm4PevA4WrqteUo5bXFQp9jYessDIkjIg7n5hGNvSNtfpVV
0fGDYPD9yNydgRMe6EwqQ0Z9p6yfq4o60JceYt4MfbbcGxzwrQc41ou2wKM/iKnQ
FBwCOaa+imgn01Qno/PdisV05KM7uSv/dK1v33nrk4xPSVG8u0aGe440U1CcGXbB
jy3ACMw/ZQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrMkg2BC1yFHv3
1qE+m7aDB4cKbHtzK1Dn1kVbgndAFEgH4MWctIx8lrv3uzRKXp/632lPdAtiNt00
r5HrQcZ3m0zfVwQhrnXwBwOiR+TMaj9yVj51uzg5wV7XQfECVem7pLn/KJDS7gIh
/W5BKxLneQ2lpFZwTPpn+zDzSSwISdhJ8PdhcSGwmbjWLkd3gXb9jVasyDzZgZkb
tjzVwz0uBTEpyKRObgmcSf0bXKDnqFJsqzVrQTQn0aTziW59amJSqlf0BKxQ+22d
jOCWOjbjHvN40RpexU4qC77zwJz5sGfsA5EZczhi1hvFUaECA9/S85qU2X+QPB8T
WPAHPSvpAgMBAAECggEADrAtoUwCQpRYWIv9LkJ5sJjmPX8DDn9Fnaz099d9qy1L
5GuLRbLF9PCRdKO55k5jenvnOXs5RZym2QNEfHaA9GuKKmvF8N/awmFpSE46M4Gp
xOhHMdG1qFroOLhkv1cNfhB+XTjzrm1GS8VGZU5jr+5Jo4/lUxUZTO+XCq1mAXK0
io58x7K3Z7P26/46yeuuqNggbFCxeHfHSK8SXYg6QxgjlZyA/A8tHDuvhDCH81q/
AISHxIFmly8ZQ2KxuVCSyHXQkPwODPh2F/4UpOq5EdN781CuV/W4e73WKBwcDUQM
aEX5KF8D2C0nXjk592SS9SFmXqw9ZD77sAL2JmbE4wKBgQDGoKT8i3xn3x4nAju0
yTxK6jaV72Lrdn5HmOZyJb8irAUr9B0t33zaubzXlM1LpuS/JAQwtppShGo1jPlt
T8iE2+JR9UKi6siHV/3EKC15i5UT3p30txdR2KS+hAasb7qXvM6sgM0nwzoS9PUy
LbKnRN8UYTvJmEKU1vuyeFQAQwKBgQDcpUQpNhtf4GrT7EoBh8E24yOO2k9XludW
yhaEddTYe2FXmqf9MGuPPX+dBj3G1SY6RNXvjmVjr3uvFN5qz+mtOuAkm7g99KkI
bKjkVuCgx6mpiH89dt7d/3uTcKXEkai0F7JgSPd1mcSxjRybhyna4qC4CuCHjICU
Ug00LAqGYwKBgGjX6N6JPgySABd1HVDrG9ErWc7AwkUpkbR3J8S+2eoSRNSTkUdi
fUPy4JQmrkqteHbQKwoPiNvfmzRTCmHByEUgz5CVViwqo9iVAJUm5AIRRIptapD+
h+ei5CrQA7nHbAWmGq2Be0juytuwwzBOYMvcFahrPqTFovdvlwH4c9aDAoGAYAk6
4qkfPxrhxH3rNEFPUsGIX4wbzqbq6DarmFnlG5iQJN420hf6KO1+luz5hIqPyfre
Fxemf74Imor9yAXY0sJ2fticV7Mew4Dv/frmaHSfHyA/KZSMqpmhwunb7PPtNv29
cPUxaClWmGUwF228RP4xMAnj8nuwF16jSpsEtbsCgYEAxEaEzpuknqV4AOnlJFBQ
7JqSkvW7w4Zy8hnUNmqhOOX1KwB0YudfirHceGtOpjxWQmHqjCHZqau0nEU02+ev
qUQsdiGKeJvWHYyqEldOdNd49qaux19IPQBeKRJQGWheGHDWGlg1aSDw3jY0sESS
3VqdbL5Z3XRXozQdwmNyfbw=
-----END PRIVATE KEY-----