Skip to content
Open
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
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ install:
- git clone https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module
- git clone https://github.com/openresty/srcache-nginx-module.git ../srcache-nginx-module
- git clone https://github.com/openresty/redis2-nginx-module.git ../redis2-nginx-module
- git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core
- git clone https://github.com/bzp2010/lua-resty-core.git -b "bzp/feat-tcpsock-sslhandshake-alpn" ../lua-resty-core
- git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
- git clone https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql
- git clone https://github.com/spacewander/lua-resty-rsa.git ../lua-resty-rsa
Expand Down
6 changes: 5 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -8191,7 +8191,7 @@ This method was first introduced in the `v0.10.22` release.
tcpsock:sslhandshake
--------------------

**syntax:** *session, err = tcpsock:sslhandshake(reused_session?, server_name?, ssl_verify?, send_status_req?)*
**syntax:** *session, err = tcpsock:sslhandshake(reused_session?, server_name?, ssl_verify?, send_status_req?, alpn?)*

**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua**

Expand Down Expand Up @@ -8227,6 +8227,10 @@ to validate the server name in the server certificate.
The optional `send_status_req` argument takes a boolean that controls whether to send
the OCSP status request in the SSL handshake request (which is for requesting OCSP stapling).

The optional `alpn` argument specifies the Application-Layer Protocol Negotiation (ALPN)
extension value to be sent during the SSL handshake. It accepts a Lua table where each
element is a string representing a protocol name. For example: `{ "h2", "http/1.1" }`.

For connections that have already done SSL/TLS handshake, this method returns
immediately.

Expand Down
25 changes: 24 additions & 1 deletion src/ngx_http_lua_socket_tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1676,7 +1676,7 @@ ngx_http_lua_ffi_socket_tcp_sslhandshake(ngx_http_request_t *r,
ngx_http_lua_socket_tcp_upstream_t *u, ngx_ssl_session_t *sess,
int enable_session_reuse, ngx_str_t *server_name, int verify,
int ocsp_status_req, STACK_OF(X509) *chain, EVP_PKEY *pkey,
const char **errmsg)
ngx_str_t *alpn, const char **errmsg)
{
ngx_int_t rc, i;
ngx_connection_t *c;
Expand Down Expand Up @@ -1843,6 +1843,29 @@ ngx_http_lua_ffi_socket_tcp_sslhandshake(ngx_http_request_t *r,
#endif
}

if (alpn != NULL && alpn->data != NULL) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua ssl ALPN: \"%V\"", alpn);

#if (defined TLSEXT_TYPE_application_layer_protocol_negotiation)
if (alpn->len > 255) {
*errmsg = "too large ALPN list";
return NGX_ERROR;
}

if (SSL_set_alpn_protos(c->ssl->connection,
alpn->data,
(unsigned int) (alpn->len)) != 0)
{
*errmsg = "SSL_set_alpn_protos failed";
return NGX_ERROR;
}
#else
*errmsg = "no ALPN support";
return NGX_ERROR;
#endif
}

if (server_name == NULL || server_name->len == 0) {
u->ssl_name.len = 0;

Expand Down
138 changes: 138 additions & 0 deletions t/129-ssl-socket.t
Original file line number Diff line number Diff line change
Expand Up @@ -3046,3 +3046,141 @@ SSL reused session
[error]
[alert]
--- timeout: 10



=== TEST 36: sslhandshake (ALPN, h2)
--- skip_openssl: 8: < 1.1.1
--- http_config
server {
listen $TEST_NGINX_SERVER_SSL_PORT ssl;
server_name test.com;
ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;
ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;
ssl_protocols TLSv1.3;

http2 on;

location / {
content_by_lua_block {
ngx.exit(200)
}
}
}
--- config
server_tokens off;
lua_ssl_protocols TLSv1.3;

location /t {
content_by_lua_block {
local sock = ngx.socket.tcp()
sock:settimeout(2000)

do
local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_SSL_PORT)
if not ok then
ngx.say("failed to connect: ", err)
return
end

ngx.say("connected: ", ok)

local session, err = sock:sslhandshake(nil, "test.com", nil, nil, {"h2"})
if not session then
ngx.say("failed to do SSL handshake: ", err)
return
end

ngx.say("ssl handshake: ", type(session))

local ok, err = sock:close()
ngx.say("close: ", ok, " ", err)
end -- do
collectgarbage()
}
}
--- request
GET /t
--- response_body_like
connected: 1
ssl handshake: cdata
close: 1 nil

--- log_level: debug
--- error_log
SSL ALPN supported by client: h2
--- no_error_log
SSL reused session
[error]
[alert]
--- timeout: 10



=== TEST 37: sslhandshake (ALPN, h2, http/1.1, my-proto)
--- skip_openssl: 8: < 1.1.1
--- http_config
server {
listen $TEST_NGINX_SERVER_SSL_PORT ssl;
server_name test.com;
ssl_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;
ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;
ssl_protocols TLSv1.3;

http2 on;

location / {
content_by_lua_block {
ngx.exit(200)
}
}
}
--- config
server_tokens off;
lua_ssl_protocols TLSv1.3;

location /t {
content_by_lua_block {
local sock = ngx.socket.tcp()
sock:settimeout(2000)

do
local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_SSL_PORT)
if not ok then
ngx.say("failed to connect: ", err)
return
end

ngx.say("connected: ", ok)

local session, err = sock:sslhandshake(nil, "test.com", nil, nil, {"h2", "http/1.1", "my-proto"})
if not session then
ngx.say("failed to do SSL handshake: ", err)
return
end

ngx.say("ssl handshake: ", type(session))

local ok, err = sock:close()
ngx.say("close: ", ok, " ", err)
end -- do
collectgarbage()
}
}
--- request
GET /t
--- response_body_like
connected: 1
ssl handshake: cdata
close: 1 nil

--- log_level: debug
--- error_log
SSL ALPN supported by client: h2
SSL ALPN supported by client: http/1.1
SSL ALPN supported by client: my-proto
--- no_error_log
SSL reused session
[error]
[alert]
--- timeout: 10