Skip to content

feature: client connect response #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -396,19 +396,36 @@ SSL handshake if the `wss://` scheme is used.

* `client_cert`

Specifies a client certificate chain cdata object that will be used while TLS handshaking with remote server.
These objects can be created using
[ngx.ssl.parse_pem_cert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_cert)
function provided by lua-resty-core.
Specifies a client certificate chain cdata object that will be used while TLS handshaking with remote server.
These objects can be created using
[ngx.ssl.parse_pem_cert](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_cert)
function provided by lua-resty-core.
Note that specifying the `client_cert` option requires corresponding `client_priv_key` be provided too. See below.

* `client_priv_key`

Specifies a private key corresponds to the `client_cert` option above.
These objects can be created using
[ngx.ssl.parse_pem_priv_key](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_priv_key)
Specifies a private key corresponds to the `client_cert` option above.
These objects can be created using
[ngx.ssl.parse_pem_priv_key](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#parse_pem_priv_key)
function provided by lua-resty-core.

* `host`

Specifies the value of the `Host` header sent in the handshake request. If not provided, the `Host` header will be derived from the hostname/address and port in the connection URI.

* `server_name`

Specifies the server name (SNI) to use when performing the TLS handshake with the server. If not provided, the `host` value or the `<host/addr>:<port>` from the connection URI will be used.

* `key`

Specifies the value of the `Sec-WebSocket-Key` header in the handshake request. The value should be a base64-encoded, 16 byte string conforming to the client handshake requirements of the [WebSocket RFC](https://datatracker.ietf.org/doc/html/rfc6455#section-4.1). If not provided, a key is randomly generated.

* `keep_response`

If truth-y, the raw, plain-text response (status line and headers) will be returned as the 3rd return value from `connect()`


The SSL connection mode (`wss://`) requires at least `ngx_lua` 0.9.11 or OpenResty 1.7.4.1.

[Back to TOC](#table-of-contents)
Expand Down
75 changes: 53 additions & 22 deletions lib/resty/websocket/client.lua
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,20 @@ function _M.connect(self, uri, opts)
end

local scheme = m[1]
local host = m[2]
local addr = m[2]
local port = m[3]
local path = m[4]

-- ngx.say("host: ", host)
-- ngx.say("port: ", port)

local ssl = scheme == "wss"
if ssl and not ssl_support then
return nil, "ngx_lua 0.9.11+ required for SSL sockets"
end

if not port then
port = scheme == 'wss' and 443 or 80
port = ssl and 443 or 80
end

if path == "" then
Expand All @@ -102,6 +107,9 @@ function _M.connect(self, uri, opts)
local ssl_verify, server_name, headers, proto_header, origin_header
local sock_opts = false
local client_cert, client_priv_key
local host
local key
local keep_response

if opts then
local protos = opts.protocols
Expand Down Expand Up @@ -133,12 +141,13 @@ function _M.connect(self, uri, opts)
"client_priv_key must be provided with client_cert")
end

if opts.ssl_verify or opts.server_name then
if not ssl_support then
return nil, "ngx_lua 0.9.11+ required for SSL sockets"
end
if opts.ssl_verify then
ssl_verify = opts.ssl_verify
server_name = opts.server_name or host
end

server_name = opts.server_name
if server_name ~= nil and type(server_name) ~= "string" then
return nil, "SSL server_name must be a string"
end

if opts.headers then
Expand All @@ -147,28 +156,41 @@ function _M.connect(self, uri, opts)
return nil, "custom headers must be a table"
end
end

host = opts.host
if host ~= nil and type(host) ~= "string" then
return nil, "custom host header must be a string"
end

key = opts.key
if key ~= nil and type(key) ~= "string" then
return nil, "custom Sec-WebSocket-Key must be a string"
end

if opts.keep_response then
keep_response = true
end
end

local ok, err
if sock_opts then
ok, err = sock:connect(host, port, sock_opts)
ok, err = sock:connect(addr, port, sock_opts)
else
ok, err = sock:connect(host, port)
ok, err = sock:connect(addr, port)
end
if not ok then
return nil, "failed to connect: " .. err
end

if scheme == "wss" then
if not ssl_support then
return nil, "ngx_lua 0.9.11+ required for SSL sockets"
end
if ssl then
if client_cert then
ok, err = sock:setclientcert(client_cert, client_priv_key)
if not ok then
return nil, "failed to set TLS client certificate: " .. err
end
end

server_name = server_name or host or addr
ok, err = sock:sslhandshake(false, server_name, ssl_verify)
if not ok then
return nil, "ssl handshake failed: " .. err
Expand All @@ -194,16 +216,21 @@ function _M.connect(self, uri, opts)

-- do the websocket handshake:

local bytes = char(rand(256) - 1, rand(256) - 1, rand(256) - 1,
rand(256) - 1, rand(256) - 1, rand(256) - 1,
rand(256) - 1, rand(256) - 1, rand(256) - 1,
rand(256) - 1, rand(256) - 1, rand(256) - 1,
rand(256) - 1, rand(256) - 1, rand(256) - 1,
rand(256) - 1)
if not key then
local bytes = char(rand(256) - 1, rand(256) - 1, rand(256) - 1,
rand(256) - 1, rand(256) - 1, rand(256) - 1,
rand(256) - 1, rand(256) - 1, rand(256) - 1,
rand(256) - 1, rand(256) - 1, rand(256) - 1,
rand(256) - 1, rand(256) - 1, rand(256) - 1,
rand(256) - 1)

key = encode_base64(bytes)
end

local host_header = host or (addr .. ":" .. port)

local key = encode_base64(bytes)
local req = "GET " .. path .. " HTTP/1.1\r\nUpgrade: websocket\r\nHost: "
.. host .. ":" .. port
.. host_header
.. "\r\nSec-WebSocket-Key: " .. key
.. (proto_header or "")
.. "\r\nSec-WebSocket-Version: 13"
Expand Down Expand Up @@ -233,7 +260,11 @@ function _M.connect(self, uri, opts)
return nil, "bad HTTP response status line: " .. header
end

return 1
if not keep_response then
header = nil
end

return 1, nil, header
end


Expand Down
Loading