From 249a35ee20f91d33ec5ccaf2f8877916b2549ba7 Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Fri, 9 Nov 2018 21:04:05 +1100 Subject: [PATCH 01/16] merge incr code --- src/ngx_http_lua_shdict.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/ngx_http_lua_shdict.c b/src/ngx_http_lua_shdict.c index b017bea658..ebba138d5a 100644 --- a/src/ngx_http_lua_shdict.c +++ b/src/ngx_http_lua_shdict.c @@ -2641,14 +2641,15 @@ ngx_http_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key, uint32_t hash; ngx_int_t rc; ngx_time_t *tp = NULL; - ngx_http_lua_shdict_ctx_t *ctx; - ngx_http_lua_shdict_node_t *sd; double num; ngx_rbtree_node_t *node; u_char *p; ngx_queue_t *queue, *q; - if (init_ttl > 0) { + ngx_http_lua_shdict_ctx_t *ctx; + ngx_http_lua_shdict_node_t *sd; + + if (init_ttl > 0 || exptime > 0) { tp = ngx_timeofday(); } @@ -2725,6 +2726,17 @@ ngx_http_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key, ngx_memcpy(p, (double *) &num, sizeof(double)); + if (exptime > 0) { + dd("setting expire time to %d", exptime); + + tp = ngx_timeofday(); + sd->expires = (uint64_t)tp->sec * 1000 + tp->msec + + (uint64_t)(exptime * 1000); + } else if (exptime == 0) { + dd("setting key to never expire"); + sd->expires = 0; + } + ngx_shmtx_unlock(&ctx->shpool->mutex); *value = num; From 5d30b4ac50675a11e23ad7663c9d6f802dbc0d2e Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Fri, 9 Nov 2018 21:05:09 +1100 Subject: [PATCH 02/16] update tests --- t/144-shdict-incr-init.t | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/t/144-shdict-incr-init.t b/t/144-shdict-incr-init.t index 71bb56603f..58b929cb61 100644 --- a/t/144-shdict-incr-init.t +++ b/t/144-shdict-incr-init.t @@ -27,7 +27,7 @@ __DATA__ content_by_lua_block { local dogs = ngx.shared.dogs dogs:set("foo", 32) - local res, err = dogs:incr("foo", 10502, 1) + local res, err = dogs:incr("foo", 10502, -1, 1) ngx.say("incr: ", res, " ", err) ngx.say("foo = ", dogs:get("foo")) } @@ -51,7 +51,7 @@ foo = 10534 local dogs = ngx.shared.dogs dogs:flush_all() dogs:set("bah", 32) - local res, err = dogs:incr("foo", 10502, 1) + local res, err = dogs:incr("foo", 10502, -1, 1) ngx.say("incr: ", res, " ", err) ngx.say("foo = ", dogs:get("foo")) } @@ -78,7 +78,7 @@ foo = 10503 end dogs:set("foo", "32", 0.001) ngx.location.capture("/sleep/0.002") - local res, err = dogs:incr("foo", 10502, 0) + local res, err = dogs:incr("foo", 10502, -1, 0) ngx.say("incr: ", res, " ", err) ngx.say("foo = ", dogs:get("foo")) } @@ -108,7 +108,7 @@ foo = 10502 end dogs:set("foo", 32, 0.001) ngx.location.capture("/sleep/0.002") - local res, err = dogs:incr("foo", 10502, 0) + local res, err = dogs:incr("foo", 10502, -1, 0) ngx.say("incr: ", res, " ", err) ngx.say("foo = ", dogs:get("foo")) } @@ -142,9 +142,9 @@ foo = 10502 break end end - local res, err, forcible = dogs:incr(long_prefix .. "bar", 10502, 0) + local res, err, forcible = dogs:incr(long_prefix .. "bar", 10502, -1, 0) ngx.say("incr: ", res, " ", err, " ", forcible) - local res, err, forcible = dogs:incr(long_prefix .. "foo", 10502, 0) + local res, err, forcible = dogs:incr(long_prefix .. "foo", 10502, -1, 0) ngx.say("incr: ", res, " ", err, " ", forcible) ngx.say("foo = ", dogs:get(long_prefix .. "foo")) } @@ -191,7 +191,7 @@ foo = 2 content_by_lua_block { local dogs = ngx.shared.dogs dogs:set("foo", true) - local res, err = dogs:incr("foo", 1, 0) + local res, err = dogs:incr("foo", 1, -1, 0) ngx.say("incr: ", res, " ", err) ngx.say("foo = ", dogs:get("foo")) } @@ -213,7 +213,7 @@ foo = true location = /test { content_by_lua_block { local dogs = ngx.shared.dogs - local res, err, forcible = dogs:incr("foo", 1, "bar") + local res, err, forcible = dogs:incr("foo", 1, -1, "bar") ngx.say("incr: ", res, " ", err, " ", forcible) ngx.say("foo = ", dogs:get("foo")) } From 625ec740e5d79a9982f62cf183b80595c1eb48e1 Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Fri, 9 Nov 2018 21:13:41 +1100 Subject: [PATCH 03/16] missing exptime arg --- src/ngx_http_lua_shdict.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ngx_http_lua_shdict.c b/src/ngx_http_lua_shdict.c index ebba138d5a..667ab18939 100644 --- a/src/ngx_http_lua_shdict.c +++ b/src/ngx_http_lua_shdict.c @@ -2634,7 +2634,7 @@ ngx_http_lua_ffi_shdict_get(ngx_shm_zone_t *zone, u_char *key, int ngx_http_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key, - size_t key_len, double *value, char **err, int has_init, double init, + size_t key_len, double *value, int exptime, char **err, int has_init, double init, long init_ttl, int *forcible) { int i, n; From ac9f37c3c4d16823ac4c5460dd6f3eedfa837c7c Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Fri, 9 Nov 2018 21:17:05 +1100 Subject: [PATCH 04/16] shorter line --- src/ngx_http_lua_shdict.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_shdict.c b/src/ngx_http_lua_shdict.c index 667ab18939..b7093743be 100644 --- a/src/ngx_http_lua_shdict.c +++ b/src/ngx_http_lua_shdict.c @@ -2634,8 +2634,8 @@ ngx_http_lua_ffi_shdict_get(ngx_shm_zone_t *zone, u_char *key, int ngx_http_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key, - size_t key_len, double *value, int exptime, char **err, int has_init, double init, - long init_ttl, int *forcible) + size_t key_len, double *value, int exptime, char **err, int has_init, + double init, long init_ttl, int *forcible) { int i, n; uint32_t hash; From adc161976830f0ba5478c46b200673c06413d81c Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Fri, 9 Nov 2018 21:37:37 +1100 Subject: [PATCH 05/16] fix non ffi method --- src/ngx_http_lua_shdict.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ngx_http_lua_shdict.c b/src/ngx_http_lua_shdict.c index b7093743be..6ae1db3de2 100644 --- a/src/ngx_http_lua_shdict.c +++ b/src/ngx_http_lua_shdict.c @@ -1277,8 +1277,8 @@ ngx_http_lua_shdict_incr(lua_State *L) n = lua_gettop(L); - if (n != 3 && n != 4) { - return luaL_error(L, "expecting 3 or 4 arguments, but only seen %d", n); + if (n != 3 && n != 4 && n != 5) { + return luaL_error(L, "expecting 3-5 arguments, but only seen %d", n); } if (lua_type(L, 1) != LUA_TTABLE) { @@ -1316,8 +1316,8 @@ ngx_http_lua_shdict_incr(lua_State *L) value = luaL_checknumber(L, 3); - if (n == 4) { - init = luaL_checknumber(L, 4); + if (n == 5) { + init = luaL_checknumber(L, 5); } dd("looking up key %.*s in shared dict %.*s", (int) key.len, key.data, From 4d944fa4167d17a039962f2366134fe84effe687 Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Mon, 3 Jun 2019 21:59:57 +1000 Subject: [PATCH 06/16] getfd --- src/ngx_http_lua_socket_tcp.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index af7b28921e..2668beeb90 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -20,6 +20,7 @@ static int ngx_http_lua_socket_tcp(lua_State *L); +static int ngx_http_lua_socket_tcp_getfd(lua_State *L); static int ngx_http_lua_socket_tcp_connect(lua_State *L); #if (NGX_HTTP_SSL) static int ngx_http_lua_socket_tcp_sslhandshake(lua_State *L); @@ -250,6 +251,9 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeouts); lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */ + lua_pushcfunction(L, ngx_http_lua_socket_tcp_getfd); + lua_setfield(L, -2, "getfd"); /* ngx socket mt */ + lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); @@ -390,6 +394,29 @@ ngx_http_lua_inject_req_socket_api(lua_State *L) lua_setfield(L, -2, "socket"); } +static int +ngx_http_lua_socket_tcp_getfd(lua_State *L) +{ + ngx_connection_t *c; + ngx_http_lua_socket_tcp_upstream_t *u; + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u == NULL || u->peer.connection == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + c = u->peer.connection; + lua_pushinteger(L,(int) c->fd); + + return 1; +} static int ngx_http_lua_socket_tcp(lua_State *L) From 644d4a6c6f2e20c4c1794a1a53d33c2e94b0c1f6 Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Tue, 4 Jun 2019 12:03:51 +1000 Subject: [PATCH 07/16] ngx.req.getfd --- src/ngx_http_lua_socket_tcp.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 2668beeb90..9071f51586 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -21,6 +21,7 @@ static int ngx_http_lua_socket_tcp(lua_State *L); static int ngx_http_lua_socket_tcp_getfd(lua_State *L); +static int ngx_http_lua_socket_req_getfd(lua_State *L); static int ngx_http_lua_socket_tcp_connect(lua_State *L); #if (NGX_HTTP_SSL) static int ngx_http_lua_socket_tcp_sslhandshake(lua_State *L); @@ -280,6 +281,9 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeouts); lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */ + lua_pushcfunction(L, ngx_http_lua_socket_tcp_getfd); + lua_setfield(L, -2, "getfd"); /* ngx socket mt */ + lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); @@ -392,6 +396,9 @@ ngx_http_lua_inject_req_socket_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_req_socket); lua_setfield(L, -2, "socket"); + + lua_pushcfunction(L, ngx_http_lua_socket_req_getfd); + lua_setfield(L, -2, "getfd"); /* ngx socket mt */ } static int @@ -400,9 +407,9 @@ ngx_http_lua_socket_tcp_getfd(lua_State *L) ngx_connection_t *c; ngx_http_lua_socket_tcp_upstream_t *u; - luaL_checktype(L, 1, LUA_TTABLE); + luaL_checktype(L, 1, LUA_TTABLE); - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); @@ -412,7 +419,7 @@ ngx_http_lua_socket_tcp_getfd(lua_State *L) return 2; } - c = u->peer.connection; + c = u->peer.connection; lua_pushinteger(L,(int) c->fd); return 1; @@ -4324,6 +4331,22 @@ ngx_http_lua_socket_cleanup_compiled_pattern(lua_State *L) return 0; } +static int +ngx_http_lua_socket_req_getfd(lua_State *L) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + + r = ngx_http_lua_get_req(L); + c = r->connection; + if(c == NULL){ + return luaL_error(L, "unknown connection"); + } + lua_pushinteger(L,(int) c->fd); + + return 1; +} + static int ngx_http_lua_req_socket(lua_State *L) From eca4cd45eeb7ecc7f053dceecf0cd4d1f96ac158 Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Sun, 5 Apr 2020 00:39:17 +1100 Subject: [PATCH 08/16] add ssl client hello support --- config | 2 + src/ngx_http_lua_common.h | 31 +- src/ngx_http_lua_control.c | 8 +- src/ngx_http_lua_coroutine.c | 4 + src/ngx_http_lua_module.c | 54 + src/ngx_http_lua_phase.c | 4 + src/ngx_http_lua_pipe.c | 1 + src/ngx_http_lua_sleep.c | 1 + src/ngx_http_lua_socket_tcp.c | 2 + src/ngx_http_lua_socket_tcp.c.orig | 6221 +++++++++++++++++++++++++ src/ngx_http_lua_ssl.h | 2 + src/ngx_http_lua_ssl_client_helloby.c | 724 +++ src/ngx_http_lua_ssl_client_helloby.h | 32 + src/ngx_http_lua_util.h | 2 + t/162-ssl-client-hello.t | 1826 ++++++++ 15 files changed, 8899 insertions(+), 15 deletions(-) create mode 100644 src/ngx_http_lua_socket_tcp.c.orig create mode 100644 src/ngx_http_lua_ssl_client_helloby.c create mode 100644 src/ngx_http_lua_ssl_client_helloby.h create mode 100644 t/162-ssl-client-hello.t diff --git a/config b/config index 3ef8f0749f..db8146737b 100644 --- a/config +++ b/config @@ -353,6 +353,7 @@ HTTP_LUA_SRCS=" \ $ngx_addon_dir/src/ngx_http_lua_timer.c \ $ngx_addon_dir/src/ngx_http_lua_config.c \ $ngx_addon_dir/src/ngx_http_lua_worker.c \ + $ngx_addon_dir/src/ngx_http_lua_ssl_client_helloby.c \ $ngx_addon_dir/src/ngx_http_lua_ssl_certby.c \ $ngx_addon_dir/src/ngx_http_lua_ssl_ocsp.c \ $ngx_addon_dir/src/ngx_http_lua_lex.c \ @@ -417,6 +418,7 @@ HTTP_LUA_DEPS=" \ $ngx_addon_dir/src/ngx_http_lua_timer.h \ $ngx_addon_dir/src/ngx_http_lua_config.h \ $ngx_addon_dir/src/ngx_http_lua_worker.h \ + $ngx_addon_dir/src/ngx_http_lua_ssl_client_helloby.h \ $ngx_addon_dir/src/ngx_http_lua_ssl_certby.h \ $ngx_addon_dir/src/ngx_http_lua_lex.h \ $ngx_addon_dir/src/ngx_http_lua_balancer.h \ diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index dae524516f..17c926056e 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -119,19 +119,20 @@ typedef struct { /* must be within 16 bit */ -#define NGX_HTTP_LUA_CONTEXT_SET 0x0001 -#define NGX_HTTP_LUA_CONTEXT_REWRITE 0x0002 -#define NGX_HTTP_LUA_CONTEXT_ACCESS 0x0004 -#define NGX_HTTP_LUA_CONTEXT_CONTENT 0x0008 -#define NGX_HTTP_LUA_CONTEXT_LOG 0x0010 -#define NGX_HTTP_LUA_CONTEXT_HEADER_FILTER 0x0020 -#define NGX_HTTP_LUA_CONTEXT_BODY_FILTER 0x0040 -#define NGX_HTTP_LUA_CONTEXT_TIMER 0x0080 -#define NGX_HTTP_LUA_CONTEXT_INIT_WORKER 0x0100 -#define NGX_HTTP_LUA_CONTEXT_BALANCER 0x0200 -#define NGX_HTTP_LUA_CONTEXT_SSL_CERT 0x0400 -#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE 0x0800 -#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH 0x1000 +#define NGX_HTTP_LUA_CONTEXT_SET 0x0001 +#define NGX_HTTP_LUA_CONTEXT_REWRITE 0x0002 +#define NGX_HTTP_LUA_CONTEXT_ACCESS 0x0004 +#define NGX_HTTP_LUA_CONTEXT_CONTENT 0x0008 +#define NGX_HTTP_LUA_CONTEXT_LOG 0x0010 +#define NGX_HTTP_LUA_CONTEXT_HEADER_FILTER 0x0020 +#define NGX_HTTP_LUA_CONTEXT_BODY_FILTER 0x0040 +#define NGX_HTTP_LUA_CONTEXT_TIMER 0x0080 +#define NGX_HTTP_LUA_CONTEXT_INIT_WORKER 0x0100 +#define NGX_HTTP_LUA_CONTEXT_BALANCER 0x0200 +#define NGX_HTTP_LUA_CONTEXT_SSL_CERT 0x0400 +#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE 0x0800 +#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH 0x1000 +#define NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO 0x2000 #ifndef NGX_LUA_NO_FFI_API @@ -278,6 +279,10 @@ struct ngx_http_lua_main_conf_s { union ngx_http_lua_srv_conf_u { #if (NGX_HTTP_SSL) struct { + ngx_http_lua_srv_conf_handler_pt ssl_client_hello_handler; + ngx_str_t ssl_client_hello_src; + u_char *ssl_client_hello_src_key; + ngx_http_lua_srv_conf_handler_pt ssl_cert_handler; ngx_str_t ssl_cert_src; u_char *ssl_cert_src_key; diff --git a/src/ngx_http_lua_control.c b/src/ngx_http_lua_control.c index 5cd1d64c8e..e0f9ac9811 100644 --- a/src/ngx_http_lua_control.c +++ b/src/ngx_http_lua_control.c @@ -322,13 +322,15 @@ ngx_http_lua_ngx_exit(lua_State *L) | NGX_HTTP_LUA_CONTEXT_TIMER | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER | NGX_HTTP_LUA_CONTEXT_BALANCER + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); rc = (ngx_int_t) luaL_checkinteger(L, 1); - if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT + if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO + | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH)) { @@ -474,6 +476,7 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, | NGX_HTTP_LUA_CONTEXT_TIMER | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER | NGX_HTTP_LUA_CONTEXT_BALANCER + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH, @@ -483,7 +486,8 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, return NGX_ERROR; } - if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT + if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO + | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH)) { diff --git a/src/ngx_http_lua_coroutine.c b/src/ngx_http_lua_coroutine.c index 99a24235a3..1905f02977 100644 --- a/src/ngx_http_lua_coroutine.c +++ b/src/ngx_http_lua_coroutine.c @@ -77,6 +77,7 @@ ngx_http_lua_coroutine_create_helper(lua_State *L, ngx_http_request_t *r, | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); @@ -158,6 +159,7 @@ ngx_http_lua_coroutine_resume(lua_State *L) | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); @@ -219,6 +221,7 @@ ngx_http_lua_coroutine_yield(lua_State *L) | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); @@ -381,6 +384,7 @@ ngx_http_lua_coroutine_status(lua_State *L) | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index 331bd04152..9a1f3415e6 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -25,6 +25,7 @@ #include "ngx_http_lua_probe.h" #include "ngx_http_lua_semaphore.h" #include "ngx_http_lua_balancer.h" +#include "ngx_http_lua_ssl_client_helloby.h" #include "ngx_http_lua_ssl_certby.h" #include "ngx_http_lua_ssl_session_storeby.h" #include "ngx_http_lua_ssl_session_fetchby.h" @@ -533,6 +534,20 @@ static ngx_command_t ngx_http_lua_cmds[] = { offsetof(ngx_http_lua_loc_conf_t, ssl_ciphers), NULL }, + { ngx_string("ssl_client_hello_by_lua_block"), + NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_http_lua_ssl_client_hello_by_lua_block, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + (void *) ngx_http_lua_ssl_client_hello_handler_inline }, + + { ngx_string("ssl_client_hello_by_lua_file"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_http_lua_ssl_client_hello_by_lua, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + (void *) ngx_http_lua_ssl_client_hello_handler_file }, + { ngx_string("ssl_certificate_by_lua_block"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, ngx_http_lua_ssl_cert_by_lua_block, @@ -969,6 +984,10 @@ ngx_http_lua_create_srv_conf(ngx_conf_t *cf) } /* set by ngx_pcalloc: + * lscf->srv.ssl_client_hello_handler = NULL; + * lscf->srv.ssl_client_hello_src = { 0, NULL }; + * lscf->srv.ssl_client_hello_src_key = NULL; + * * lscf->srv.ssl_cert_handler = NULL; * lscf->srv.ssl_cert_src = { 0, NULL }; * lscf->srv.ssl_cert_src_key = NULL; @@ -1001,6 +1020,41 @@ ngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) dd("merge srv conf"); + if (conf->srv.ssl_client_hello_src.len == 0) { + conf->srv.ssl_client_hello_src = prev->srv.ssl_client_hello_src; + conf->srv.ssl_client_hello_src_key = prev->srv.ssl_client_hello_src_key; + conf->srv.ssl_client_hello_handler = prev->srv.ssl_client_hello_handler; + } + + if (conf->srv.ssl_client_hello_src.len) { + sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); + if (sscf && sscf->ssl.ctx) { +#ifdef LIBRESSL_VERSION_NUMBER + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "LibreSSL does not support " + "ssl_client_hello_by_lua*"); + return NGX_CONF_ERROR; + +#else + +# ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + + SSL_CTX_set_client_hello_cb(sscf->ssl.ctx, + ngx_http_lua_ssl_client_hello_handler, + NULL); + +# else + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "OpenSSL too old to support " + "ssl_client_hello_by_lua*"); + return NGX_CONF_ERROR; + +# endif + } +#endif + } + if (conf->srv.ssl_cert_src.len == 0) { conf->srv.ssl_cert_src = prev->srv.ssl_cert_src; conf->srv.ssl_cert_src_key = prev->srv.ssl_cert_src_key; diff --git a/src/ngx_http_lua_phase.c b/src/ngx_http_lua_phase.c index 304c88ae13..2deb561776 100644 --- a/src/ngx_http_lua_phase.c +++ b/src/ngx_http_lua_phase.c @@ -80,6 +80,10 @@ ngx_http_lua_ngx_get_phase(lua_State *L) lua_pushliteral(L, "balancer"); break; + case NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO: + lua_pushliteral(L, "ssl_client_hello"); + break; + case NGX_HTTP_LUA_CONTEXT_SSL_CERT: lua_pushliteral(L, "ssl_cert"); break; diff --git a/src/ngx_http_lua_pipe.c b/src/ngx_http_lua_pipe.c index f9ae6cd455..6d842f8349 100644 --- a/src/ngx_http_lua_pipe.c +++ b/src/ngx_http_lua_pipe.c @@ -1159,6 +1159,7 @@ ngx_http_lua_pipe_get_lua_ctx(ngx_http_request_t *r, | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH, errbuf, errbuf_size); diff --git a/src/ngx_http_lua_sleep.c b/src/ngx_http_lua_sleep.c index 09ea0f6d40..fc40557a03 100644 --- a/src/ngx_http_lua_sleep.c +++ b/src/ngx_http_lua_sleep.c @@ -56,6 +56,7 @@ ngx_http_lua_ngx_sleep(lua_State *L) | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 9e1752f01c..eabdbe8dfa 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -469,6 +469,7 @@ ngx_http_lua_socket_tcp(lua_State *L) | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); @@ -919,6 +920,7 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER + | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); diff --git a/src/ngx_http_lua_socket_tcp.c.orig b/src/ngx_http_lua_socket_tcp.c.orig new file mode 100644 index 0000000000..9e1752f01c --- /dev/null +++ b/src/ngx_http_lua_socket_tcp.c.orig @@ -0,0 +1,6221 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_http_lua_socket_tcp.h" +#include "ngx_http_lua_input_filters.h" +#include "ngx_http_lua_util.h" +#include "ngx_http_lua_uthread.h" +#include "ngx_http_lua_output.h" +#include "ngx_http_lua_contentby.h" +#include "ngx_http_lua_probe.h" + + +static int ngx_http_lua_socket_tcp(lua_State *L); +static int ngx_http_lua_socket_tcp_getfd(lua_State *L); +static int ngx_http_lua_socket_req_getfd(lua_State *L); +static int ngx_http_lua_socket_tcp_connect(lua_State *L); +#if (NGX_HTTP_SSL) +static int ngx_http_lua_socket_tcp_sslhandshake(lua_State *L); +#endif +static int ngx_http_lua_socket_tcp_receive(lua_State *L); +static int ngx_http_lua_socket_tcp_receiveany(lua_State *L); +static int ngx_http_lua_socket_tcp_send(lua_State *L); +static int ngx_http_lua_socket_tcp_close(lua_State *L); +static int ngx_http_lua_socket_tcp_setoption(lua_State *L); +static int ngx_http_lua_socket_tcp_settimeout(lua_State *L); +static int ngx_http_lua_socket_tcp_settimeouts(lua_State *L); +static void ngx_http_lua_socket_tcp_handler(ngx_event_t *ev); +static ngx_int_t ngx_http_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, + void *data); +static void ngx_http_lua_socket_init_peer_connection_addr_text( + ngx_peer_connection_t *pc); +static void ngx_http_lua_socket_read_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static void ngx_http_lua_socket_send_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static void ngx_http_lua_socket_connected_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static void ngx_http_lua_socket_tcp_cleanup(void *data); +static void ngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static void ngx_http_lua_socket_tcp_finalize_read_part(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static void ngx_http_lua_socket_tcp_finalize_write_part(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static ngx_int_t ngx_http_lua_socket_send(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static ngx_int_t ngx_http_lua_socket_test_connect(ngx_http_request_t *r, + ngx_connection_t *c); +static void ngx_http_lua_socket_handle_conn_error(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type); +static void ngx_http_lua_socket_handle_read_error(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type); +static void ngx_http_lua_socket_handle_write_error(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type); +static void ngx_http_lua_socket_handle_conn_success(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static void ngx_http_lua_socket_handle_read_success(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static void ngx_http_lua_socket_handle_write_success(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static int ngx_http_lua_socket_tcp_send_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static int ngx_http_lua_socket_tcp_conn_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static void ngx_http_lua_socket_dummy_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static int ngx_http_lua_socket_tcp_receive_helper(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static ngx_int_t ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static int ngx_http_lua_socket_tcp_receive_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static ngx_int_t ngx_http_lua_socket_read_line(void *data, ssize_t bytes); +static void ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx); +static int ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static int ngx_http_lua_socket_conn_error_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static int ngx_http_lua_socket_read_error_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static int ngx_http_lua_socket_write_error_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static ngx_int_t ngx_http_lua_socket_read_all(void *data, ssize_t bytes); +static ngx_int_t ngx_http_lua_socket_read_until(void *data, ssize_t bytes); +static ngx_int_t ngx_http_lua_socket_read_chunk(void *data, ssize_t bytes); +static ngx_int_t ngx_http_lua_socket_read_any(void *data, ssize_t bytes); +static int ngx_http_lua_socket_tcp_receiveuntil(lua_State *L); +static int ngx_http_lua_socket_receiveuntil_iterator(lua_State *L); +static ngx_int_t ngx_http_lua_socket_compile_pattern(u_char *data, size_t len, + ngx_http_lua_socket_compiled_pattern_t *cp, ngx_log_t *log); +static int ngx_http_lua_socket_cleanup_compiled_pattern(lua_State *L); +static int ngx_http_lua_req_socket(lua_State *L); +static void ngx_http_lua_req_socket_rev_handler(ngx_http_request_t *r); +static int ngx_http_lua_socket_tcp_getreusedtimes(lua_State *L); +static int ngx_http_lua_socket_tcp_setkeepalive(lua_State *L); +static void ngx_http_lua_socket_tcp_create_socket_pool(lua_State *L, + ngx_http_request_t *r, ngx_str_t key, ngx_int_t pool_size, + ngx_int_t backlog, ngx_http_lua_socket_pool_t **spool); +static ngx_int_t ngx_http_lua_get_keepalive_peer(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static void ngx_http_lua_socket_keepalive_dummy_handler(ngx_event_t *ev); +static int ngx_http_lua_socket_tcp_connect_helper(lua_State *L, + ngx_http_lua_socket_tcp_upstream_t *u, ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx, u_char *host_ref, size_t host_len, in_port_t port, + unsigned resuming); +static void ngx_http_lua_socket_tcp_conn_op_timeout_handler( + ngx_event_t *ev); +static int ngx_http_lua_socket_tcp_conn_op_timeout_retval_handler( + ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static void ngx_http_lua_socket_tcp_resume_conn_op( + ngx_http_lua_socket_pool_t *spool); +static void ngx_http_lua_socket_tcp_conn_op_ctx_cleanup(void *data); +static void ngx_http_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev); +static ngx_int_t ngx_http_lua_socket_keepalive_close_handler(ngx_event_t *ev); +static void ngx_http_lua_socket_keepalive_rev_handler(ngx_event_t *ev); +static int ngx_http_lua_socket_tcp_conn_op_resume_retval_handler( + ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static int ngx_http_lua_socket_tcp_upstream_destroy(lua_State *L); +static int ngx_http_lua_socket_downstream_destroy(lua_State *L); +static ngx_int_t ngx_http_lua_socket_push_input_data(ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx, ngx_http_lua_socket_tcp_upstream_t *u, + lua_State *L); +static ngx_int_t ngx_http_lua_socket_add_pending_data(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, u_char *pos, size_t len, u_char *pat, + int prefix, int old_state); +static ngx_int_t ngx_http_lua_socket_add_input_buffer(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static ngx_int_t ngx_http_lua_socket_insert_buffer(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, u_char *pat, size_t prefix); +static ngx_int_t ngx_http_lua_socket_tcp_conn_op_resume(ngx_http_request_t *r); +static ngx_int_t ngx_http_lua_socket_tcp_conn_resume(ngx_http_request_t *r); +static ngx_int_t ngx_http_lua_socket_tcp_read_resume(ngx_http_request_t *r); +static ngx_int_t ngx_http_lua_socket_tcp_write_resume(ngx_http_request_t *r); +static ngx_int_t ngx_http_lua_socket_tcp_resume_helper(ngx_http_request_t *r, + int socket_op); +static void ngx_http_lua_tcp_queue_conn_op_cleanup(void *data); +static void ngx_http_lua_tcp_resolve_cleanup(void *data); +static void ngx_http_lua_coctx_cleanup(void *data); +static void ngx_http_lua_socket_free_pool(ngx_log_t *log, + ngx_http_lua_socket_pool_t *spool); +static int ngx_http_lua_socket_shutdown_pool(lua_State *L); +static void ngx_http_lua_socket_shutdown_pool_helper( + ngx_http_lua_socket_pool_t *spool); +static void + ngx_http_lua_socket_empty_resolve_handler(ngx_resolver_ctx_t *ctx); +static int ngx_http_lua_socket_prepare_error_retvals(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L, ngx_uint_t ft_type); +#if (NGX_HTTP_SSL) +static int ngx_http_lua_ssl_handshake_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static void ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c); +static int ngx_http_lua_ssl_free_session(lua_State *L); +#endif +static void ngx_http_lua_socket_tcp_close_connection(ngx_connection_t *c); + + +enum { + SOCKET_CTX_INDEX = 1, + SOCKET_KEY_INDEX = 3, + SOCKET_CONNECT_TIMEOUT_INDEX = 2, + SOCKET_SEND_TIMEOUT_INDEX = 4, + SOCKET_READ_TIMEOUT_INDEX = 5, +}; + + +enum { + SOCKET_OP_CONNECT, + SOCKET_OP_READ, + SOCKET_OP_WRITE, + SOCKET_OP_RESUME_CONN +}; + + +#define ngx_http_lua_socket_check_busy_connecting(r, u, L) \ + if ((u)->conn_waiting) { \ + lua_pushnil(L); \ + lua_pushliteral(L, "socket busy connecting"); \ + return 2; \ + } + + +#define ngx_http_lua_socket_check_busy_reading(r, u, L) \ + if ((u)->read_waiting) { \ + lua_pushnil(L); \ + lua_pushliteral(L, "socket busy reading"); \ + return 2; \ + } + + +#define ngx_http_lua_socket_check_busy_writing(r, u, L) \ + if ((u)->write_waiting) { \ + lua_pushnil(L); \ + lua_pushliteral(L, "socket busy writing"); \ + return 2; \ + } \ + if ((u)->raw_downstream \ + && ((r)->connection->buffered & NGX_HTTP_LOWLEVEL_BUFFERED)) \ + { \ + lua_pushnil(L); \ + lua_pushliteral(L, "socket busy writing"); \ + return 2; \ + } + + +static char ngx_http_lua_req_socket_metatable_key; +static char ngx_http_lua_raw_req_socket_metatable_key; +static char ngx_http_lua_tcp_socket_metatable_key; +static char ngx_http_lua_upstream_udata_metatable_key; +static char ngx_http_lua_downstream_udata_metatable_key; +static char ngx_http_lua_pool_udata_metatable_key; +static char ngx_http_lua_pattern_udata_metatable_key; +#if (NGX_HTTP_SSL) +static char ngx_http_lua_ssl_session_metatable_key; +#endif + + +void +ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) +{ + ngx_int_t rc; + + lua_createtable(L, 0, 4 /* nrec */); /* ngx.socket */ + + lua_pushcfunction(L, ngx_http_lua_socket_tcp); + lua_pushvalue(L, -1); + lua_setfield(L, -3, "tcp"); + lua_setfield(L, -2, "stream"); + + { + const char buf[] = "local sock = ngx.socket.tcp()" + " local ok, err = sock:connect(...)" + " if ok then return sock else return nil, err end"; + + rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=ngx.socket.connect"); + } + + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_CRIT, log, 0, + "failed to load Lua code for ngx.socket.connect(): %i", + rc); + + } else { + lua_setfield(L, -2, "connect"); + } + + lua_setfield(L, -2, "socket"); + + /* {{{req socket object metatable */ + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + req_socket_metatable_key)); + lua_createtable(L, 0 /* narr */, 5 /* nrec */); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); + lua_setfield(L, -2, "receive"); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil); + lua_setfield(L, -2, "receiveuntil"); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeout); + lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeouts); + lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_getfd); + lua_setfield(L, -2, "getfd"); /* ngx socket mt */ + + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* {{{raw req socket object metatable */ + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + raw_req_socket_metatable_key)); + lua_createtable(L, 0 /* narr */, 6 /* nrec */); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); + lua_setfield(L, -2, "receive"); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil); + lua_setfield(L, -2, "receiveuntil"); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_send); + lua_setfield(L, -2, "send"); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeout); + lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeouts); + lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_getfd); + lua_setfield(L, -2, "getfd"); /* ngx socket mt */ + + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* {{{tcp object metatable */ + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + tcp_socket_metatable_key)); + lua_createtable(L, 0 /* narr */, 12 /* nrec */); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_connect); + lua_setfield(L, -2, "connect"); + +#if (NGX_HTTP_SSL) + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_sslhandshake); + lua_setfield(L, -2, "sslhandshake"); + +#endif + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); + lua_setfield(L, -2, "receive"); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveany); + lua_setfield(L, -2, "receiveany"); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil); + lua_setfield(L, -2, "receiveuntil"); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_send); + lua_setfield(L, -2, "send"); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_close); + lua_setfield(L, -2, "close"); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_setoption); + lua_setfield(L, -2, "setoption"); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeout); + lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeouts); + lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_getreusedtimes); + lua_setfield(L, -2, "getreusedtimes"); + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_setkeepalive); + lua_setfield(L, -2, "setkeepalive"); + + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* {{{upstream userdata metatable */ + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + upstream_udata_metatable_key)); + lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ + lua_pushcfunction(L, ngx_http_lua_socket_tcp_upstream_destroy); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* {{{downstream userdata metatable */ + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + downstream_udata_metatable_key)); + lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ + lua_pushcfunction(L, ngx_http_lua_socket_downstream_destroy); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* {{{socket pool userdata metatable */ + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + pool_udata_metatable_key)); + lua_createtable(L, 0, 1); /* metatable */ + lua_pushcfunction(L, ngx_http_lua_socket_shutdown_pool); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* {{{socket compiled pattern userdata metatable */ + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + pattern_udata_metatable_key)); + lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ + lua_pushcfunction(L, ngx_http_lua_socket_cleanup_compiled_pattern); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + +#if (NGX_HTTP_SSL) + + /* {{{ssl session userdata metatable */ + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + ssl_session_metatable_key)); + lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ + lua_pushcfunction(L, ngx_http_lua_ssl_free_session); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + +#endif +} + + +void +ngx_http_lua_inject_req_socket_api(lua_State *L) +{ + lua_pushcfunction(L, ngx_http_lua_req_socket); + lua_setfield(L, -2, "socket"); + + lua_pushcfunction(L, ngx_http_lua_socket_req_getfd); + lua_setfield(L, -2, "getfd"); /* ngx socket mt */ +} + +static int +ngx_http_lua_socket_tcp_getfd(lua_State *L) +{ + ngx_connection_t *c; + ngx_http_lua_socket_tcp_upstream_t *u; + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u == NULL || u->peer.connection == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + c = u->peer.connection; + lua_pushinteger(L,(int) c->fd); + + return 1; +} + +static int +ngx_http_lua_socket_tcp(lua_State *L) +{ + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + + if (lua_gettop(L) != 0) { + return luaL_error(L, "expecting zero arguments, but got %d", + lua_gettop(L)); + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_ACCESS + | NGX_HTTP_LUA_CONTEXT_CONTENT + | NGX_HTTP_LUA_CONTEXT_TIMER + | NGX_HTTP_LUA_CONTEXT_SSL_CERT + | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); + + lua_createtable(L, 5 /* narr */, 1 /* nrec */); + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + tcp_socket_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + + dd("top: %d", lua_gettop(L)); + + return 1; +} + + +static void +ngx_http_lua_socket_tcp_create_socket_pool(lua_State *L, ngx_http_request_t *r, + ngx_str_t key, ngx_int_t pool_size, ngx_int_t backlog, + ngx_http_lua_socket_pool_t **spool) +{ + u_char *p; + size_t size, key_len; + ngx_int_t i; + ngx_http_lua_socket_pool_t *sp; + ngx_http_lua_socket_pool_item_t *items; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket connection pool size: %i, backlog: %i", + pool_size, backlog); + + key_len = ngx_align(key.len + 1, sizeof(void *)); + + size = sizeof(ngx_http_lua_socket_pool_t) - 1 + key_len + + sizeof(ngx_http_lua_socket_pool_item_t) * pool_size; + + /* before calling this function, the Lua stack is: + * -1 key + * -2 pools + */ + sp = lua_newuserdata(L, size); + if (sp == NULL) { + luaL_error(L, "no memory"); + return; + } + + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + pool_udata_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket keepalive create connection pool for key" + " \"%V\"", &key); + + /* a new socket pool with metatable is push to the stack, so now we have: + * -1 sp + * -2 key + * -3 pools + * + * it is time to set pools[key] to sp. + */ + lua_rawset(L, -3); + + /* clean up the stack for consistency's sake */ + lua_pop(L, 1); + + sp->backlog = backlog; + sp->size = pool_size; + sp->connections = 0; + sp->lua_vm = ngx_http_lua_get_lua_vm(r, NULL); + + ngx_queue_init(&sp->cache_connect_op); + ngx_queue_init(&sp->wait_connect_op); + ngx_queue_init(&sp->cache); + ngx_queue_init(&sp->free); + + p = ngx_copy(sp->key, key.data, key.len); + *p++ = '\0'; + + items = (ngx_http_lua_socket_pool_item_t *) (sp->key + key_len); + + dd("items: %p", items); + + ngx_http_lua_assert((void *) items == ngx_align_ptr(items, sizeof(void *))); + + for (i = 0; i < pool_size; i++) { + ngx_queue_insert_head(&sp->free, &items[i].queue); + items[i].socket_pool = sp; + } + + *spool = sp; +} + + +static int +ngx_http_lua_socket_tcp_connect_helper(lua_State *L, + ngx_http_lua_socket_tcp_upstream_t *u, ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx, u_char *host_ref, size_t host_len, in_port_t port, + unsigned resuming) +{ + int n; + int host_size; + int saved_top; + ngx_int_t rc; + ngx_str_t host; + ngx_str_t *conn_op_host; + ngx_url_t url; + ngx_queue_t *q; + ngx_resolver_ctx_t *rctx, temp; + ngx_http_lua_co_ctx_t *coctx; + ngx_http_core_loc_conf_t *clcf; + ngx_http_lua_socket_pool_t *spool; + ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + spool = u->socket_pool; + if (spool != NULL) { + rc = ngx_http_lua_get_keepalive_peer(r, u); + + if (rc == NGX_OK) { + lua_pushinteger(L, 1); + return 1; + } + + /* rc == NGX_DECLINED */ + + spool->connections++; + + /* check if backlog is enabled and + * don't queue resuming connection operation */ + if (spool->backlog >= 0 && !resuming) { + + dd("lua tcp socket %s connections %ld", + spool->key, spool->connections); + + if (spool->connections > spool->size + spool->backlog) { + spool->connections--; + lua_pushnil(L); + lua_pushliteral(L, "too many waiting connect operations"); + return 2; + } + + if (spool->connections > spool->size) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, u->peer.log, 0, + "lua tcp socket queue connect operation for " + "connection pool \"%s\", connections: %i", + spool->key, spool->connections); + + host_size = sizeof(u_char) * + (ngx_max(host_len, NGX_INET_ADDRSTRLEN) + 1); + + if (!ngx_queue_empty(&spool->cache_connect_op)) { + q = ngx_queue_last(&spool->cache_connect_op); + ngx_queue_remove(q); + conn_op_ctx = ngx_queue_data( + q, ngx_http_lua_socket_tcp_conn_op_ctx_t, queue); + + conn_op_host = &conn_op_ctx->host; + if (host_len > conn_op_host->len + && host_len > NGX_INET_ADDRSTRLEN) + { + ngx_free(conn_op_host->data); + conn_op_host->data = ngx_alloc(host_size, + ngx_cycle->log); + if (conn_op_host->data == NULL) { + ngx_free(conn_op_ctx); + goto no_memory_and_not_resuming; + } + } + + } else { + conn_op_ctx = ngx_alloc( + sizeof(ngx_http_lua_socket_tcp_conn_op_ctx_t), + ngx_cycle->log); + if (conn_op_ctx == NULL) { + goto no_memory_and_not_resuming; + } + + conn_op_host = &conn_op_ctx->host; + conn_op_host->data = ngx_alloc(host_size, ngx_cycle->log); + if (conn_op_host->data == NULL) { + ngx_free(conn_op_ctx); + goto no_memory_and_not_resuming; + } + } + + conn_op_ctx->cleanup = NULL; + + ngx_memcpy(conn_op_host->data, host_ref, host_len); + conn_op_host->data[host_len] = '\0'; + conn_op_host->len = host_len; + + conn_op_ctx->port = port; + + u->write_co_ctx = ctx->cur_co_ctx; + + conn_op_ctx->u = u; + ctx->cur_co_ctx->cleanup = + ngx_http_lua_tcp_queue_conn_op_cleanup; + ctx->cur_co_ctx->data = conn_op_ctx; + + ngx_memzero(&conn_op_ctx->event, sizeof(ngx_event_t)); + conn_op_ctx->event.handler = + ngx_http_lua_socket_tcp_conn_op_timeout_handler; + conn_op_ctx->event.data = conn_op_ctx; + conn_op_ctx->event.log = ngx_cycle->log; + + ngx_add_timer(&conn_op_ctx->event, u->connect_timeout); + + ngx_queue_insert_tail(&spool->wait_connect_op, + &conn_op_ctx->queue); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua tcp socket queued connect operation for " + "%d(ms), u: %p, ctx: %p", + u->connect_timeout, conn_op_ctx->u, conn_op_ctx); + + return lua_yield(L, 0); + } + } + + } /* end spool != NULL */ + + host.data = ngx_palloc(r->pool, host_len + 1); + if (host.data == NULL) { + return luaL_error(L, "no memory"); + } + + host.len = host_len; + + ngx_memcpy(host.data, host_ref, host_len); + host.data[host_len] = '\0'; + + ngx_memzero(&url, sizeof(ngx_url_t)); + url.url = host; + url.default_port = port; + url.no_resolve = 1; + + coctx = ctx->cur_co_ctx; + + if (ngx_parse_url(r->pool, &url) != NGX_OK) { + lua_pushnil(L); + + if (url.err) { + lua_pushfstring(L, "failed to parse host name \"%s\": %s", + url.url.data, url.err); + + } else { + lua_pushfstring(L, "failed to parse host name \"%s\"", + url.url.data); + } + + goto failed; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket connect timeout: %M", u->connect_timeout); + + u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); + if (u->resolved == NULL) { + if (resuming) { + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + goto failed; + } + + goto no_memory_and_not_resuming; + } + + if (url.addrs && url.addrs[0].sockaddr) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket network address given directly"); + + u->resolved->sockaddr = url.addrs[0].sockaddr; + u->resolved->socklen = url.addrs[0].socklen; + u->resolved->naddrs = 1; + u->resolved->host = url.addrs[0].name; + + } else { + u->resolved->host = host; + u->resolved->port = url.default_port; + } + + if (u->resolved->sockaddr) { + rc = ngx_http_lua_socket_resolve_retval_handler(r, u, L); + if (rc == NGX_AGAIN && !resuming) { + return lua_yield(L, 0); + } + + if (rc > 1) { + goto failed; + } + + return rc; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + temp.name = host; + rctx = ngx_resolve_start(clcf->resolver, &temp); + if (rctx == NULL) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; + lua_pushnil(L); + lua_pushliteral(L, "failed to start the resolver"); + goto failed; + } + + if (rctx == NGX_NO_RESOLVER) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; + lua_pushnil(L); + lua_pushfstring(L, "no resolver defined to resolve \"%s\"", host.data); + goto failed; + } + + rctx->name = host; +#if !defined(nginx_version) || nginx_version < 1005008 + rctx->type = NGX_RESOLVE_A; +#endif + rctx->handler = ngx_http_lua_socket_resolve_handler; + rctx->data = u; + rctx->timeout = clcf->resolver_timeout; + + u->resolved->ctx = rctx; + u->write_co_ctx = ctx->cur_co_ctx; + + ngx_http_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_http_lua_tcp_resolve_cleanup; + coctx->data = u; + + saved_top = lua_gettop(L); + + if (ngx_resolve_name(rctx) != NGX_OK) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket fail to run resolver immediately"); + + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; + + coctx->cleanup = NULL; + coctx->data = NULL; + + u->resolved->ctx = NULL; + lua_pushnil(L); + lua_pushfstring(L, "%s could not be resolved", host.data); + goto failed; + } + + if (u->conn_waiting) { + dd("resolved and already connecting"); + + if (resuming) { + return NGX_AGAIN; + } + + return lua_yield(L, 0); + } + + n = lua_gettop(L) - saved_top; + if (n) { + dd("errors occurred during resolving or connecting" + "or already connected"); + + if (n > 1) { + goto failed; + } + + return n; + } + + /* still resolving */ + + u->conn_waiting = 1; + u->write_prepare_retvals = ngx_http_lua_socket_resolve_retval_handler; + + dd("setting data to %p", u); + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_http_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_http_core_run_phases; + } + + if (resuming) { + return NGX_AGAIN; + } + + return lua_yield(L, 0); + +failed: + + if (spool != NULL) { + spool->connections--; + ngx_http_lua_socket_tcp_resume_conn_op(spool); + } + + return 2; + +no_memory_and_not_resuming: + + if (spool != NULL) { + spool->connections--; + ngx_http_lua_socket_tcp_resume_conn_op(spool); + } + + return luaL_error(L, "no memory"); +} + + +static int +ngx_http_lua_socket_tcp_connect(lua_State *L) +{ + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + int port; + int n; + u_char *p; + size_t len; + ngx_http_lua_loc_conf_t *llcf; + ngx_peer_connection_t *pc; + int connect_timeout, send_timeout, read_timeout; + unsigned custom_pool; + int key_index; + ngx_int_t backlog; + ngx_int_t pool_size; + ngx_str_t key; + const char *msg; + + ngx_http_lua_socket_tcp_upstream_t *u; + + ngx_http_lua_socket_pool_t *spool; + + n = lua_gettop(L); + if (n != 2 && n != 3 && n != 4) { + return luaL_error(L, "ngx.socket connect: expecting 2, 3, or 4 " + "arguments (including the object), but seen %d", n); + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_ACCESS + | NGX_HTTP_LUA_CONTEXT_CONTENT + | NGX_HTTP_LUA_CONTEXT_TIMER + | NGX_HTTP_LUA_CONTEXT_SSL_CERT + | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); + + luaL_checktype(L, 1, LUA_TTABLE); + + p = (u_char *) luaL_checklstring(L, 2, &len); + + backlog = -1; + key_index = 2; + pool_size = 0; + custom_pool = 0; + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (lua_type(L, n) == LUA_TTABLE) { + + /* found the last optional option table */ + + lua_getfield(L, n, "pool_size"); + + if (lua_isnumber(L, -1)) { + pool_size = (ngx_int_t) lua_tointeger(L, -1); + + if (pool_size <= 0) { + msg = lua_pushfstring(L, "bad \"pool_size\" option value: %i", + pool_size); + return luaL_argerror(L, n, msg); + } + + } else if (!lua_isnil(L, -1)) { + msg = lua_pushfstring(L, "bad \"pool_size\" option type: %s", + lua_typename(L, lua_type(L, -1))); + return luaL_argerror(L, n, msg); + } + + lua_pop(L, 1); + + lua_getfield(L, n, "backlog"); + + if (lua_isnumber(L, -1)) { + backlog = (ngx_int_t) lua_tointeger(L, -1); + + if (backlog < 0) { + msg = lua_pushfstring(L, "bad \"backlog\" option value: %i", + backlog); + return luaL_argerror(L, n, msg); + } + + /* use default value for pool size if only backlog specified */ + if (pool_size == 0) { + pool_size = llcf->pool_size; + } + } + + lua_pop(L, 1); + + lua_getfield(L, n, "pool"); + + switch (lua_type(L, -1)) { + case LUA_TNUMBER: + lua_tostring(L, -1); + /* FALLTHROUGH */ + + case LUA_TSTRING: + custom_pool = 1; + + lua_pushvalue(L, -1); + lua_rawseti(L, 1, SOCKET_KEY_INDEX); + + key_index = n + 1; + + break; + + case LUA_TNIL: + lua_pop(L, 2); + break; + + default: + msg = lua_pushfstring(L, "bad \"pool\" option type: %s", + luaL_typename(L, -1)); + luaL_argerror(L, n, msg); + break; + } + + n--; + } + + /* the fourth argument is not a table */ + if (n == 4) { + lua_pop(L, 1); + n--; + } + + if (n == 3) { + port = luaL_checkinteger(L, 3); + + if (port < 0 || port > 65535) { + lua_pushnil(L); + lua_pushfstring(L, "bad port number: %d", port); + return 2; + } + + if (!custom_pool) { + lua_pushliteral(L, ":"); + lua_insert(L, 3); + lua_concat(L, 3); + } + + dd("socket key: %s", lua_tostring(L, -1)); + + } else { /* n == 2 */ + port = 0; + } + + if (!custom_pool) { + /* the key's index is 2 */ + + lua_pushvalue(L, 2); + lua_rawseti(L, 1, SOCKET_KEY_INDEX); + } + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u) { + if (u->request && u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_http_lua_socket_check_busy_connecting(r, u, L); + ngx_http_lua_socket_check_busy_reading(r, u, L); + ngx_http_lua_socket_check_busy_writing(r, u, L); + + if (u->body_downstream || u->raw_downstream) { + return luaL_error(L, "attempt to re-connect a request socket"); + } + + if (u->peer.connection) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket reconnect without shutting down"); + + ngx_http_lua_socket_tcp_finalize(r, u); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua reuse socket upstream ctx"); + + } else { + u = lua_newuserdata(L, sizeof(ngx_http_lua_socket_tcp_upstream_t)); + if (u == NULL) { + return luaL_error(L, "no memory"); + } + +#if 1 + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + upstream_udata_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); +#endif + + lua_rawseti(L, 1, SOCKET_CTX_INDEX); + } + + ngx_memzero(u, sizeof(ngx_http_lua_socket_tcp_upstream_t)); + + u->request = r; /* set the controlling request */ + + u->conf = llcf; + + pc = &u->peer; + + pc->log = r->connection->log; + pc->log_error = NGX_ERROR_ERR; + + dd("lua peer connection log: %p", pc->log); + + lua_rawgeti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX); + lua_rawgeti(L, 1, SOCKET_SEND_TIMEOUT_INDEX); + lua_rawgeti(L, 1, SOCKET_READ_TIMEOUT_INDEX); + + read_timeout = (ngx_int_t) lua_tointeger(L, -1); + send_timeout = (ngx_int_t) lua_tointeger(L, -2); + connect_timeout = (ngx_int_t) lua_tointeger(L, -3); + + lua_pop(L, 3); + + if (connect_timeout > 0) { + u->connect_timeout = (ngx_msec_t) connect_timeout; + + } else { + u->connect_timeout = u->conf->connect_timeout; + } + + if (send_timeout > 0) { + u->send_timeout = (ngx_msec_t) send_timeout; + + } else { + u->send_timeout = u->conf->send_timeout; + } + + if (read_timeout > 0) { + u->read_timeout = (ngx_msec_t) read_timeout; + + } else { + u->read_timeout = u->conf->read_timeout; + } + + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(socket_pool_key)); + lua_rawget(L, LUA_REGISTRYINDEX); /* table */ + lua_pushvalue(L, key_index); /* key */ + + lua_rawget(L, -2); + spool = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (spool != NULL) { + u->socket_pool = spool; + + } else if (pool_size > 0) { + lua_pushvalue(L, key_index); + key.data = (u_char *) lua_tolstring(L, -1, &key.len); + + ngx_http_lua_socket_tcp_create_socket_pool(L, r, key, pool_size, + backlog, &spool); + u->socket_pool = spool; + } + + return ngx_http_lua_socket_tcp_connect_helper(L, u, r, ctx, p, + len, port, 0); +} + + +static void +ngx_http_lua_socket_empty_resolve_handler(ngx_resolver_ctx_t *ctx) +{ + /* do nothing */ +} + + +static void +ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) +{ + ngx_http_request_t *r; + ngx_connection_t *c; + ngx_http_upstream_resolved_t *ur; + ngx_http_lua_ctx_t *lctx; + lua_State *L; + ngx_http_lua_socket_tcp_upstream_t *u; + u_char *p; + size_t len; +#if defined(nginx_version) && nginx_version >= 1005008 + socklen_t socklen; + struct sockaddr *sockaddr; +#else + struct sockaddr_in *sin; +#endif + ngx_uint_t i; + unsigned waiting; + + u = ctx->data; + r = u->request; + c = r->connection; + ur = u->resolved; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua tcp socket resolve handler"); + + lctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (lctx == NULL) { + return; + } + + lctx->cur_co_ctx = u->write_co_ctx; + + u->write_co_ctx->cleanup = NULL; + + L = lctx->cur_co_ctx->co; + + waiting = u->conn_waiting; + + if (ctx->state) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua tcp socket resolver error: %s " + "(connect waiting: %d)", + ngx_resolver_strerror(ctx->state), (int) waiting); + + lua_pushnil(L); + lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len); + lua_pushfstring(L, " could not be resolved (%d: %s)", + (int) ctx->state, + ngx_resolver_strerror(ctx->state)); + lua_concat(L, 2); + + u->write_prepare_retvals = + ngx_http_lua_socket_conn_error_retval_handler; + ngx_http_lua_socket_handle_conn_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_RESOLVER); + + if (waiting) { + ngx_http_run_posted_requests(c); + } + + return; + } + + ur->naddrs = ctx->naddrs; + ur->addrs = ctx->addrs; + +#if (NGX_DEBUG) + { +# if defined(nginx_version) && nginx_version >= 1005008 + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_str_t addr; +# else + in_addr_t addr; +# endif + ngx_uint_t i; + +# if defined(nginx_version) && nginx_version >= 1005008 + addr.data = text; + + for (i = 0; i < ctx->naddrs; i++) { + addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "name was resolved to %V", &addr); + } +# else + for (i = 0; i < ctx->naddrs; i++) { + dd("addr i: %d %p", (int) i, &ctx->addrs[i]); + + addr = ntohl(ctx->addrs[i]); + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "name was resolved to %ud.%ud.%ud.%ud", + (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff); + } +# endif + } +#endif + + ngx_http_lua_assert(ur->naddrs > 0); + + if (ur->naddrs == 1) { + i = 0; + + } else { + i = ngx_random() % ur->naddrs; + } + + dd("selected addr index: %d", (int) i); + +#if defined(nginx_version) && nginx_version >= 1005008 + socklen = ur->addrs[i].socklen; + + sockaddr = ngx_palloc(r->pool, socklen); + if (sockaddr == NULL) { + goto nomem; + } + + ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen); + + switch (sockaddr->sa_family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port); + break; +#endif + default: /* AF_INET */ + ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port); + } + + p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN); + if (p == NULL) { + goto nomem; + } + + len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); + ur->sockaddr = sockaddr; + ur->socklen = socklen; + +#else + /* for nginx older than 1.5.8 */ + + len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; + + p = ngx_pnalloc(r->pool, len + sizeof(struct sockaddr_in)); + if (p == NULL) { + goto nomem; + } + + sin = (struct sockaddr_in *) &p[len]; + ngx_memzero(sin, sizeof(struct sockaddr_in)); + + len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN); + len = ngx_sprintf(&p[len], ":%d", ur->port) - p; + + sin->sin_family = AF_INET; + sin->sin_port = htons(ur->port); + sin->sin_addr.s_addr = ur->addrs[i]; + + ur->sockaddr = (struct sockaddr *) sin; + ur->socklen = sizeof(struct sockaddr_in); +#endif + + ur->host.data = p; + ur->host.len = len; + ur->naddrs = 1; + + ngx_resolve_name_done(ctx); + ur->ctx = NULL; + + u->conn_waiting = 0; + u->write_co_ctx = NULL; + + if (waiting) { + lctx->resume_handler = ngx_http_lua_socket_tcp_conn_resume; + r->write_event_handler(r); + ngx_http_run_posted_requests(c); + + } else { + (void) ngx_http_lua_socket_resolve_retval_handler(r, u, L); + } + + return; + +nomem: + + if (ur->ctx) { + ngx_resolve_name_done(ctx); + ur->ctx = NULL; + } + + u->write_prepare_retvals = ngx_http_lua_socket_conn_error_retval_handler; + ngx_http_lua_socket_handle_conn_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_NOMEM); + + if (waiting) { + dd("run posted requests"); + ngx_http_run_posted_requests(c); + + } else { + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + } +} + + +static void +ngx_http_lua_socket_init_peer_connection_addr_text(ngx_peer_connection_t *pc) +{ + ngx_connection_t *c; + size_t addr_text_max_len; + + c = pc->connection; + + switch (pc->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + addr_text_max_len = NGX_INET6_ADDRSTRLEN; + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + addr_text_max_len = NGX_UNIX_ADDRSTRLEN; + break; +#endif + + case AF_INET: + addr_text_max_len = NGX_INET_ADDRSTRLEN; + break; + + default: + addr_text_max_len = NGX_SOCKADDR_STRLEN; + break; + } + + c->addr_text.data = ngx_pnalloc(c->pool, addr_text_max_len); + if (c->addr_text.data == NULL) { + ngx_log_error(NGX_LOG_ERR, pc->log, 0, + "init peer connection addr_text failed: no memory"); + return; + } + + c->addr_text.len = ngx_sock_ntop(pc->sockaddr, pc->socklen, + c->addr_text.data, + addr_text_max_len, 0); +} + + +static int +ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_http_lua_ctx_t *ctx; + ngx_peer_connection_t *pc; + ngx_connection_t *c; + ngx_http_cleanup_t *cln; + ngx_http_upstream_resolved_t *ur; + ngx_int_t rc; + ngx_http_lua_co_ctx_t *coctx; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket resolve retval handler"); + + if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_RESOLVER) { + return 2; + } + + pc = &u->peer; + + ur = u->resolved; + + if (ur->sockaddr) { + pc->sockaddr = ur->sockaddr; + pc->socklen = ur->socklen; + pc->name = &ur->host; + + } else { + lua_pushnil(L); + lua_pushliteral(L, "resolver not working"); + return 2; + } + + pc->get = ngx_http_lua_socket_tcp_get_peer; + + rc = ngx_event_connect_peer(pc); + + if (rc == NGX_ERROR) { + u->socket_errno = ngx_socket_errno; + } + + if (u->cleanup == NULL) { + cln = ngx_http_lua_cleanup_add(r, 0); + if (cln == NULL) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + return 2; + } + + cln->handler = ngx_http_lua_socket_tcp_cleanup; + cln->data = u; + u->cleanup = &cln->handler; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket connect: %i", rc); + + if (rc == NGX_ERROR) { + return ngx_http_lua_socket_conn_error_retval_handler(r, u, L); + } + + if (rc == NGX_BUSY) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; + lua_pushnil(L); + lua_pushliteral(L, "no live connection"); + return 2; + } + + if (rc == NGX_DECLINED) { + dd("socket errno: %d", (int) ngx_socket_errno); + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; + u->socket_errno = ngx_socket_errno; + return ngx_http_lua_socket_conn_error_retval_handler(r, u, L); + } + + /* rc == NGX_OK || rc == NGX_AGAIN */ + + c = pc->connection; + + c->data = u; + + c->write->handler = ngx_http_lua_socket_tcp_handler; + c->read->handler = ngx_http_lua_socket_tcp_handler; + + u->write_event_handler = ngx_http_lua_socket_connected_handler; + u->read_event_handler = ngx_http_lua_socket_connected_handler; + + c->sendfile &= r->connection->sendfile; + + if (c->pool == NULL) { + + /* we need separate pool here to be able to cache SSL connections */ + + c->pool = ngx_create_pool(128, r->connection->log); + if (c->pool == NULL) { + return ngx_http_lua_socket_prepare_error_retvals(r, u, L, + NGX_HTTP_LUA_SOCKET_FT_NOMEM); + } + } + + c->log = r->connection->log; + c->pool->log = c->log; + c->read->log = c->log; + c->write->log = c->log; + + /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */ + +#if 0 + u->writer.out = NULL; + u->writer.last = &u->writer.out; +#endif + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + coctx = ctx->cur_co_ctx; + + dd("setting data to %p", u); + + if (rc == NGX_OK) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket connected: fd:%d", (int) c->fd); + + /* We should delete the current write/read event + * here because the socket object may not be used immediately + * on the Lua land, thus causing hot spin around level triggered + * event poll and wasting CPU cycles. */ + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_http_lua_socket_handle_conn_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + lua_pushnil(L); + lua_pushliteral(L, "failed to handle write event"); + return 2; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_http_lua_socket_handle_conn_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + lua_pushnil(L); + lua_pushliteral(L, "failed to handle read event"); + return 2; + } + + u->read_event_handler = ngx_http_lua_socket_dummy_handler; + u->write_event_handler = ngx_http_lua_socket_dummy_handler; + + lua_pushinteger(L, 1); + return 1; + } + + /* rc == NGX_AGAIN */ + + ngx_http_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_http_lua_coctx_cleanup; + coctx->data = u; + + ngx_add_timer(c->write, u->connect_timeout); + + u->write_co_ctx = ctx->cur_co_ctx; + u->conn_waiting = 1; + u->write_prepare_retvals = ngx_http_lua_socket_tcp_conn_retval_handler; + + dd("setting data to %p", u); + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_http_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_http_core_run_phases; + } + + return NGX_AGAIN; +} + + +static int +ngx_http_lua_socket_conn_error_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_uint_t ft_type; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket error retval handler"); + + if (u->write_co_ctx) { + u->write_co_ctx->cleanup = NULL; + } + + ngx_http_lua_socket_tcp_finalize(r, u); + + ft_type = u->ft_type; + u->ft_type = 0; + return ngx_http_lua_socket_prepare_error_retvals(r, u, L, ft_type); +} + + +#if (NGX_HTTP_SSL) + +static int +ngx_http_lua_socket_tcp_sslhandshake(lua_State *L) +{ + int n, top; + ngx_int_t rc; + ngx_str_t name = ngx_null_string; + ngx_connection_t *c; + ngx_ssl_session_t **psession; + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx; + + ngx_http_lua_socket_tcp_upstream_t *u; + + /* Lua function arguments: self [,session] [,host] [,verify] + [,send_status_req] */ + + n = lua_gettop(L); + if (n < 1 || n > 5) { + return luaL_error(L, "ngx.socket sslhandshake: expecting 1 ~ 5 " + "arguments (including the object), but seen %d", n); + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket ssl handshake"); + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u == NULL + || u->peer.connection == NULL + || u->read_closed + || u->write_closed) + { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_http_lua_socket_check_busy_connecting(r, u, L); + ngx_http_lua_socket_check_busy_reading(r, u, L); + ngx_http_lua_socket_check_busy_writing(r, u, L); + + if (u->raw_downstream || u->body_downstream) { + lua_pushnil(L); + lua_pushliteral(L, "not supported for downstream"); + return 2; + } + + c = u->peer.connection; + + u->ssl_session_reuse = 1; + + if (c->ssl && c->ssl->handshaked) { + switch (lua_type(L, 2)) { + case LUA_TUSERDATA: + lua_pushvalue(L, 2); + break; + + case LUA_TBOOLEAN: + if (!lua_toboolean(L, 2)) { + /* avoid generating the ssl session */ + lua_pushboolean(L, 1); + break; + } + /* fall through */ + + default: + ngx_http_lua_ssl_handshake_retval_handler(r, u, L); + break; + } + + return 1; + } + + if (ngx_ssl_create_connection(u->conf->ssl, c, + NGX_SSL_BUFFER|NGX_SSL_CLIENT) + != NGX_OK) + { + lua_pushnil(L); + lua_pushliteral(L, "failed to create ssl connection"); + return 2; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + coctx = ctx->cur_co_ctx; + + c->sendfile = 0; + + if (n >= 2) { + if (lua_type(L, 2) == LUA_TBOOLEAN) { + u->ssl_session_reuse = lua_toboolean(L, 2); + + } else { + psession = lua_touserdata(L, 2); + + if (psession != NULL && *psession != NULL) { + if (ngx_ssl_set_session(c, *psession) != NGX_OK) { + lua_pushnil(L); + lua_pushliteral(L, "lua ssl set session failed"); + return 2; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua ssl set session: %p", *psession); + } + } + + if (n >= 3) { + name.data = (u_char *) lua_tolstring(L, 3, &name.len); + + if (name.data) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua ssl server name: \"%*s\"", name.len, + name.data); + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + + if (SSL_set_tlsext_host_name(c->ssl->connection, + (char *) name.data) + == 0) + { + lua_pushnil(L); + lua_pushliteral(L, "SSL_set_tlsext_host_name failed"); + return 2; + } + +#else + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua socket SNI disabled because the current " + "version of OpenSSL lacks the support"); + +#endif + } + + if (n >= 4) { + u->ssl_verify = lua_toboolean(L, 4); + + if (n >= 5) { + if (lua_toboolean(L, 5)) { +#ifdef NGX_HTTP_LUA_USE_OCSP + SSL_set_tlsext_status_type(c->ssl->connection, + TLSEXT_STATUSTYPE_ocsp); +#else + return luaL_error(L, "no OCSP support"); +#endif + } + } + } + } + } + + dd("found sni name: %.*s %p", (int) name.len, name.data, name.data); + + if (name.len == 0) { + u->ssl_name.len = 0; + + } else { + if (u->ssl_name.data) { + /* buffer already allocated */ + + if (u->ssl_name.len >= name.len) { + /* reuse it */ + ngx_memcpy(u->ssl_name.data, name.data, name.len); + u->ssl_name.len = name.len; + + } else { + ngx_free(u->ssl_name.data); + goto new_ssl_name; + } + + } else { + +new_ssl_name: + + u->ssl_name.data = ngx_alloc(name.len, ngx_cycle->log); + if (u->ssl_name.data == NULL) { + u->ssl_name.len = 0; + + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + return 2; + } + + ngx_memcpy(u->ssl_name.data, name.data, name.len); + u->ssl_name.len = name.len; + } + } + + u->write_co_ctx = coctx; + +#if 0 +#ifdef NGX_HTTP_LUA_USE_OCSP + SSL_set_tlsext_status_type(c->ssl->connection, TLSEXT_STATUSTYPE_ocsp); +#endif +#endif + + rc = ngx_ssl_handshake(c); + + dd("ngx_ssl_handshake returned %d", (int) rc); + + if (rc == NGX_AGAIN) { + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + ngx_add_timer(c->read, u->connect_timeout); + + u->conn_waiting = 1; + u->write_prepare_retvals = ngx_http_lua_ssl_handshake_retval_handler; + + ngx_http_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_http_lua_coctx_cleanup; + coctx->data = u; + + c->ssl->handler = ngx_http_lua_ssl_handshake_handler; + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_http_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_http_core_run_phases; + } + + return lua_yield(L, 0); + } + + top = lua_gettop(L); + ngx_http_lua_ssl_handshake_handler(c); + return lua_gettop(L) - top; +} + + +static void +ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c) +{ + const char *err; + int waiting; + lua_State *L; + ngx_int_t rc; + ngx_connection_t *dc; /* downstream connection */ + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_loc_conf_t *llcf; + + ngx_http_lua_socket_tcp_upstream_t *u; + + u = c->data; + r = u->request; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + c->write->handler = ngx_http_lua_socket_tcp_handler; + c->read->handler = ngx_http_lua_socket_tcp_handler; + + waiting = u->conn_waiting; + + dc = r->connection; + L = u->write_co_ctx->co; + + if (c->read->timedout) { + lua_pushnil(L); + lua_pushliteral(L, "timeout"); + goto failed; + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->ssl->handshaked) { + + if (u->ssl_verify) { + rc = SSL_get_verify_result(c->ssl->connection); + + if (rc != X509_V_OK) { + lua_pushnil(L); + err = lua_pushfstring(L, "%d: %s", (int) rc, + X509_verify_cert_error_string(rc)); + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, dc->log, 0, "lua ssl " + "certificate verify error: (%s)", err); + } + + goto failed; + } + +#if defined(nginx_version) && nginx_version >= 1007000 + + if (u->ssl_name.len + && ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) + { + lua_pushnil(L); + lua_pushliteral(L, "certificate host mismatch"); + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, dc->log, 0, "lua ssl " + "certificate does not match host \"%V\"", + &u->ssl_name); + } + + goto failed; + } + +#endif + } + + if (waiting) { + ngx_http_lua_socket_handle_conn_success(r, u); + + } else { + (void) ngx_http_lua_ssl_handshake_retval_handler(r, u, L); + } + + if (waiting) { + ngx_http_run_posted_requests(dc); + } + + return; + } + + lua_pushnil(L); + lua_pushliteral(L, "handshake failed"); + +failed: + + if (waiting) { + u->write_prepare_retvals = + ngx_http_lua_socket_conn_error_retval_handler; + ngx_http_lua_socket_handle_conn_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_SSL); + ngx_http_run_posted_requests(dc); + + } else { + (void) ngx_http_lua_socket_conn_error_retval_handler(r, u, L); + } +} + + +static int +ngx_http_lua_ssl_handshake_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_connection_t *c; + ngx_ssl_session_t *ssl_session, **ud; + + if (!u->ssl_session_reuse) { + lua_pushboolean(L, 1); + return 1; + } + + ud = lua_newuserdata(L, sizeof(ngx_ssl_session_t *)); + + c = u->peer.connection; + + ssl_session = ngx_ssl_get_session(c); + if (ssl_session == NULL) { + *ud = NULL; + + } else { + *ud = ssl_session; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua ssl save session: %p", ssl_session); + + /* set up the __gc metamethod */ + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + ssl_session_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + } + + return 1; +} + +#endif /* NGX_HTTP_SSL */ + + +static int +ngx_http_lua_socket_read_error_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_uint_t ft_type; + + if (u->read_co_ctx) { + u->read_co_ctx->cleanup = NULL; + } + + ft_type = u->ft_type; + u->ft_type = 0; + + if (u->no_close) { + u->no_close = 0; + + } else { + ngx_http_lua_socket_tcp_finalize_read_part(r, u); + } + + return ngx_http_lua_socket_prepare_error_retvals(r, u, L, ft_type); +} + + +static int +ngx_http_lua_socket_write_error_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_uint_t ft_type; + + if (u->write_co_ctx) { + u->write_co_ctx->cleanup = NULL; + } + + ngx_http_lua_socket_tcp_finalize_write_part(r, u); + + ft_type = u->ft_type; + u->ft_type = 0; + return ngx_http_lua_socket_prepare_error_retvals(r, u, L, ft_type); +} + + +static int +ngx_http_lua_socket_prepare_error_retvals(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L, ngx_uint_t ft_type) +{ + u_char errstr[NGX_MAX_ERROR_STR]; + u_char *p; + + if (ft_type & (NGX_HTTP_LUA_SOCKET_FT_RESOLVER + | NGX_HTTP_LUA_SOCKET_FT_SSL)) + { + return 2; + } + + lua_pushnil(L); + + if (ft_type & NGX_HTTP_LUA_SOCKET_FT_TIMEOUT) { + lua_pushliteral(L, "timeout"); + + } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_CLOSED) { + lua_pushliteral(L, "closed"); + + } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_BUFTOOSMALL) { + lua_pushliteral(L, "buffer too small"); + + } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_NOMEM) { + lua_pushliteral(L, "no memory"); + + } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT) { + lua_pushliteral(L, "client aborted"); + + } else { + + if (u->socket_errno) { +#if defined(nginx_version) && nginx_version >= 9000 + p = ngx_strerror(u->socket_errno, errstr, sizeof(errstr)); +#else + p = ngx_strerror_r(u->socket_errno, errstr, sizeof(errstr)); +#endif + /* for compatibility with LuaSocket */ + ngx_strlow(errstr, errstr, p - errstr); + lua_pushlstring(L, (char *) errstr, p - errstr); + + } else { + lua_pushliteral(L, "error"); + } + } + + return 2; +} + + +static int +ngx_http_lua_socket_tcp_conn_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + if (u->ft_type) { + return ngx_http_lua_socket_conn_error_retval_handler(r, u, L); + } + + lua_pushinteger(L, 1); + return 1; +} + + +static int +ngx_http_lua_socket_tcp_receive_helper(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_int_t rc; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx; + + u->input_filter_ctx = u; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + if (u->bufs_in == NULL) { + u->bufs_in = + ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_recv_bufs, + u->conf->buffer_size); + + if (u->bufs_in == NULL) { + return luaL_error(L, "no memory"); + } + + u->buf_in = u->bufs_in; + u->buffer = *u->buf_in->buf; + } + + dd("tcp receive: buf_in: %p, bufs_in: %p", u->buf_in, u->bufs_in); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket read timeout: %M", u->read_timeout); + + if (u->raw_downstream || u->body_downstream) { + r->read_event_handler = ngx_http_lua_req_socket_rev_handler; + } + + u->read_waiting = 0; + u->read_co_ctx = NULL; + + rc = ngx_http_lua_socket_tcp_read(r, u); + + if (rc == NGX_ERROR) { + dd("read failed: %d", (int) u->ft_type); + rc = ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); + dd("tcp receive retval returned: %d", (int) rc); + return rc; + } + + if (rc == NGX_OK) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket receive done in a single run"); + + return ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); + } + + /* rc == NGX_AGAIN */ + + u->read_event_handler = ngx_http_lua_socket_read_handler; + + coctx = ctx->cur_co_ctx; + + ngx_http_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_http_lua_coctx_cleanup; + coctx->data = u; + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_http_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_http_core_run_phases; + } + + u->read_co_ctx = coctx; + u->read_waiting = 1; + u->read_prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler; + + dd("setting data to %p, coctx:%p", u, coctx); + + if (u->raw_downstream || u->body_downstream) { + ctx->downstream = u; + } + + return lua_yield(L, 0); +} + + +static int +ngx_http_lua_socket_tcp_receiveany(lua_State *L) +{ + int n; + lua_Integer bytes; + ngx_http_request_t *r; + ngx_http_lua_loc_conf_t *llcf; + ngx_http_lua_socket_tcp_upstream_t *u; + + n = lua_gettop(L); + if (n != 2) { + return luaL_error(L, "expecting 2 arguments " + "(including the object), but got %d", n); + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u == NULL || u->peer.connection == NULL || u->read_closed) { + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "attempt to receive data on a closed socket: u:%p, " + "c:%p, ft:%d eof:%d", + u, u ? u->peer.connection : NULL, + u ? (int) u->ft_type : 0, u ? (int) u->eof : 0); + } + + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_http_lua_socket_check_busy_connecting(r, u, L); + ngx_http_lua_socket_check_busy_reading(r, u, L); + + if (!lua_isnumber(L, 2)) { + return luaL_argerror(L, 2, "bad max argument"); + } + + bytes = lua_tointeger(L, 2); + if (bytes <= 0) { + return luaL_argerror(L, 2, "bad max argument"); + } + + u->input_filter = ngx_http_lua_socket_read_any; + u->rest = (size_t) bytes; + u->length = u->rest; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket calling receiveany() method to read at " + "most %uz bytes", u->rest); + + return ngx_http_lua_socket_tcp_receive_helper(r, u, L); +} + + +static int +ngx_http_lua_socket_tcp_receive(lua_State *L) +{ + ngx_http_request_t *r; + ngx_http_lua_socket_tcp_upstream_t *u; + int n; + ngx_str_t pat; + lua_Integer bytes; + char *p; + int typ; + ngx_http_lua_loc_conf_t *llcf; + + n = lua_gettop(L); + if (n != 1 && n != 2) { + return luaL_error(L, "expecting 1 or 2 arguments " + "(including the object), but got %d", n); + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket calling receive() method"); + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u == NULL || u->peer.connection == NULL || u->read_closed) { + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "attempt to receive data on a closed socket: u:%p, " + "c:%p, ft:%d eof:%d", + u, u ? u->peer.connection : NULL, + u ? (int) u->ft_type : 0, u ? (int) u->eof : 0); + } + + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_http_lua_socket_check_busy_connecting(r, u, L); + ngx_http_lua_socket_check_busy_reading(r, u, L); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket read timeout: %M", u->read_timeout); + + if (n > 1) { + if (lua_isnumber(L, 2)) { + typ = LUA_TNUMBER; + + } else { + typ = lua_type(L, 2); + } + + switch (typ) { + case LUA_TSTRING: + pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); + if (pat.len != 2 || pat.data[0] != '*') { + p = (char *) lua_pushfstring(L, "bad pattern argument: %s", + (char *) pat.data); + + return luaL_argerror(L, 2, p); + } + + switch (pat.data[1]) { + case 'l': + u->input_filter = ngx_http_lua_socket_read_line; + break; + + case 'a': + u->input_filter = ngx_http_lua_socket_read_all; + break; + + default: + return luaL_argerror(L, 2, "bad pattern argument"); + break; + } + + u->length = 0; + u->rest = 0; + + break; + + case LUA_TNUMBER: + bytes = lua_tointeger(L, 2); + if (bytes < 0) { + return luaL_argerror(L, 2, "bad pattern argument"); + } + +#if 1 + if (bytes == 0) { + lua_pushliteral(L, ""); + return 1; + } +#endif + + u->input_filter = ngx_http_lua_socket_read_chunk; + u->length = (size_t) bytes; + u->rest = u->length; + + break; + + default: + return luaL_argerror(L, 2, "bad pattern argument"); + break; + } + + } else { + u->input_filter = ngx_http_lua_socket_read_line; + u->length = 0; + u->rest = 0; + } + + return ngx_http_lua_socket_tcp_receive_helper(r, u, L); +} + + +static ngx_int_t +ngx_http_lua_socket_read_chunk(void *data, ssize_t bytes) +{ + ngx_int_t rc; + ngx_http_lua_socket_tcp_upstream_t *u = data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, + "lua tcp socket read chunk %z", bytes); + + rc = ngx_http_lua_read_bytes(&u->buffer, u->buf_in, &u->rest, + bytes, u->request->connection->log); + if (rc == NGX_ERROR) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_http_lua_socket_read_all(void *data, ssize_t bytes) +{ + ngx_http_lua_socket_tcp_upstream_t *u = data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, + "lua tcp socket read all"); + return ngx_http_lua_read_all(&u->buffer, u->buf_in, bytes, + u->request->connection->log); +} + + +static ngx_int_t +ngx_http_lua_socket_read_line(void *data, ssize_t bytes) +{ + ngx_http_lua_socket_tcp_upstream_t *u = data; + + ngx_int_t rc; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, + "lua tcp socket read line"); + + rc = ngx_http_lua_read_line(&u->buffer, u->buf_in, bytes, + u->request->connection->log); + if (rc == NGX_ERROR) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_http_lua_socket_read_any(void *data, ssize_t bytes) +{ + ngx_http_lua_socket_tcp_upstream_t *u = data; + + ngx_int_t rc; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, + "lua tcp socket read any"); + + rc = ngx_http_lua_read_any(&u->buffer, u->buf_in, &u->rest, bytes, + u->request->connection->log); + if (rc == NGX_ERROR) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; + return NGX_ERROR; + } + + return rc; +} + + +static ngx_int_t +ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_buf_t *b; + ngx_event_t *rev; + size_t size; + ssize_t n; + unsigned read; + off_t preread = 0; + ngx_http_lua_loc_conf_t *llcf; + + c = u->peer.connection; + rev = c->read; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua tcp socket read data: wait:%d", + (int) u->read_waiting); + + b = &u->buffer; + read = 0; + + for ( ;; ) { + + size = b->last - b->pos; + + if (size || u->eof) { + + rc = u->input_filter(u->input_filter_ctx, size); + + if (rc == NGX_OK) { + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket receive done: wait:%d, eof:%d, " + "uri:\"%V?%V\"", (int) u->read_waiting, + (int) u->eof, &r->uri, &r->args); + + if (u->body_downstream + && b->last == b->pos + && r->request_body->rest == 0) + { + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf->check_client_abort) { + rc = ngx_http_lua_check_broken_connection(r, rev); + + if (rc == NGX_OK) { + goto success; + } + + if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST) { + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); + + } else { + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + } + + return NGX_ERROR; + } + } + +#if 1 + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } +#endif + +success: + + ngx_http_lua_socket_handle_read_success(r, u); + return NGX_OK; + } + + if (rc == NGX_ERROR) { + dd("input filter error: ft_type:%d wait:%d", + (int) u->ft_type, (int) u->read_waiting); + + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + /* rc == NGX_AGAIN */ + + if (u->body_downstream && r->request_body->rest == 0) { + u->eof = 1; + } + + continue; + } + + if (read && !rev->ready) { + rc = NGX_AGAIN; + break; + } + + size = b->end - b->last; + + if (size == 0) { + rc = ngx_http_lua_socket_add_input_buffer(r, u); + if (rc == NGX_ERROR) { + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_NOMEM); + + return NGX_ERROR; + } + + b = &u->buffer; + size = (size_t) (b->end - b->last); + } + + if (u->raw_downstream) { + preread = r->header_in->last - r->header_in->pos; + + if (preread) { + + if ((off_t) size > preread) { + size = (size_t) preread; + } + + ngx_http_lua_probe_req_socket_consume_preread(r, + r->header_in->pos, + size); + + b->last = ngx_copy(b->last, r->header_in->pos, size); + r->header_in->pos += size; + continue; + } + + } else if (u->body_downstream) { + + if (r->request_body->rest == 0) { + + dd("request body rest is zero"); + + u->eof = 1; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua request body exhausted"); + + continue; + } + + /* try to process the preread body */ + + preread = r->header_in->last - r->header_in->pos; + + if (preread) { + + /* there is the pre-read part of the request body */ + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http client request body preread %O", preread); + + if (preread >= r->request_body->rest) { + preread = r->request_body->rest; + } + + if ((off_t) size > preread) { + size = (size_t) preread; + } + + ngx_http_lua_probe_req_socket_consume_preread(r, + r->header_in->pos, + size); + + b->last = ngx_copy(b->last, r->header_in->pos, size); + + r->header_in->pos += size; + r->request_length += size; + + if (r->request_body->rest) { + r->request_body->rest -= size; + } + + continue; + } + + if (size > (size_t) r->request_body->rest) { + size = (size_t) r->request_body->rest; + } + } + +#if 1 + if (rev->active && !rev->ready) { + rc = NGX_AGAIN; + break; + } +#endif + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket try to recv data %uz: \"%V?%V\"", + size, &r->uri, &r->args); + + n = c->recv(c, b->last, size); + + dd("read event ready: %d", (int) c->read->ready); + + read = 1; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket recv returned %d: \"%V?%V\"", + (int) n, &r->uri, &r->args); + + if (n == NGX_AGAIN) { + rc = NGX_AGAIN; + dd("socket recv busy"); + break; + } + + if (n == 0) { + + if (u->raw_downstream || u->body_downstream) { + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf->check_client_abort) { + + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); + return NGX_ERROR; + } + + /* llcf->check_client_abort == 0 */ + + if (u->body_downstream && r->request_body->rest) { + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); + return NGX_ERROR; + } + } + + u->eof = 1; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket closed"); + + continue; + } + + if (n == NGX_ERROR) { + u->socket_errno = ngx_socket_errno; + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + b->last += n; + + if (u->body_downstream) { + r->request_length += n; + r->request_body->rest -= n; + } + } + +#if 1 + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } +#endif + + if (rev->active) { + ngx_add_timer(rev, u->read_timeout); + + } else if (rev->timer_set) { + ngx_del_timer(rev); + } + + return rc; +} + + +static int +ngx_http_lua_socket_tcp_send(lua_State *L) +{ + ngx_int_t rc; + ngx_http_request_t *r; + u_char *p; + size_t len; + ngx_chain_t *cl; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_socket_tcp_upstream_t *u; + int type; + int tcp_nodelay; + const char *msg; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_http_lua_loc_conf_t *llcf; + ngx_http_core_loc_conf_t *clcf; + ngx_http_lua_co_ctx_t *coctx; + + /* TODO: add support for the optional "i" and "j" arguments */ + + if (lua_gettop(L) != 2) { + return luaL_error(L, "expecting 2 arguments (including the object), " + "but got %d", lua_gettop(L)); + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + dd("tcp send: u=%p, u->write_closed=%d", u, (unsigned) u->write_closed); + + if (u == NULL || u->peer.connection == NULL || u->write_closed) { + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "attempt to send data on a closed socket: u:%p, " + "c:%p, ft:%d eof:%d", + u, u ? u->peer.connection : NULL, + u ? (int) u->ft_type : 0, u ? (int) u->eof : 0); + } + + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_http_lua_socket_check_busy_connecting(r, u, L); + ngx_http_lua_socket_check_busy_writing(r, u, L); + + if (u->body_downstream) { + return luaL_error(L, "attempt to write to request sockets"); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket send timeout: %M", u->send_timeout); + + type = lua_type(L, 2); + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + lua_tolstring(L, 2, &len); + break; + + case LUA_TTABLE: + len = ngx_http_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */); + break; + + case LUA_TNIL: + len = sizeof("nil") - 1; + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, 2)) { + len = sizeof("true") - 1; + + } else { + len = sizeof("false") - 1; + } + + break; + + default: + msg = lua_pushfstring(L, "string, number, boolean, nil, " + "or array table expected, got %s", + lua_typename(L, type)); + + return luaL_argerror(L, 2, msg); + } + + if (len == 0) { + lua_pushinteger(L, 0); + return 1; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_bufs, len); + + if (cl == NULL) { + return luaL_error(L, "no memory"); + } + + b = cl->buf; + + switch (type) { + case LUA_TNUMBER: + case LUA_TSTRING: + p = (u_char *) lua_tolstring(L, -1, &len); + b->last = ngx_copy(b->last, (u_char *) p, len); + break; + + case LUA_TTABLE: + b->last = ngx_http_lua_copy_str_in_table(L, -1, b->last); + break; + + case LUA_TNIL: + *b->last++ = 'n'; + *b->last++ = 'i'; + *b->last++ = 'l'; + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, 2)) { + *b->last++ = 't'; + *b->last++ = 'r'; + *b->last++ = 'u'; + *b->last++ = 'e'; + + } else { + *b->last++ = 'f'; + *b->last++ = 'a'; + *b->last++ = 'l'; + *b->last++ = 's'; + *b->last++ = 'e'; + } + + break; + + default: + return luaL_error(L, "impossible to reach here"); + } + + u->request_bufs = cl; + + u->request_len = len; + + /* mimic ngx_http_upstream_init_request here */ + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + c = u->peer.connection; + + if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua socket tcp_nodelay"); + + tcp_nodelay = 1; + + if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, + (const void *) &tcp_nodelay, sizeof(int)) + == -1) + { + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + if (llcf->log_socket_errors) { + ngx_connection_error(c, ngx_socket_errno, + "setsockopt(TCP_NODELAY) " + "failed"); + } + + lua_pushnil(L); + lua_pushliteral(L, "setsocketopt tcp_nodelay failed"); + return 2; + } + + c->tcp_nodelay = NGX_TCP_NODELAY_SET; + } + +#if 1 + u->write_waiting = 0; + u->write_co_ctx = NULL; +#endif + + ngx_http_lua_probe_socket_tcp_send_start(r, u, b->pos, len); + + rc = ngx_http_lua_socket_send(r, u); + + dd("socket send returned %d", (int) rc); + + if (rc == NGX_ERROR) { + return ngx_http_lua_socket_write_error_retval_handler(r, u, L); + } + + if (rc == NGX_OK) { + lua_pushinteger(L, len); + return 1; + } + + /* rc == NGX_AGAIN */ + + coctx = ctx->cur_co_ctx; + + ngx_http_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_http_lua_coctx_cleanup; + coctx->data = u; + + if (u->raw_downstream) { + ctx->writing_raw_req_socket = 1; + } + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_http_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_http_core_run_phases; + } + + u->write_co_ctx = coctx; + u->write_waiting = 1; + u->write_prepare_retvals = ngx_http_lua_socket_tcp_send_retval_handler; + + dd("setting data to %p", u); + + return lua_yield(L, 0); +} + + +static int +ngx_http_lua_socket_tcp_send_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket send return value handler"); + + if (u->ft_type) { + return ngx_http_lua_socket_write_error_retval_handler(r, u, L); + } + + lua_pushinteger(L, u->request_len); + return 1; +} + + +static int +ngx_http_lua_socket_tcp_receive_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + int n; + ngx_int_t rc; + ngx_http_lua_ctx_t *ctx; + ngx_event_t *ev; + + ngx_http_lua_loc_conf_t *llcf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket receive return value handler"); + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + +#if 1 + if (u->raw_downstream || u->body_downstream) { + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf->check_client_abort) { + + r->read_event_handler = ngx_http_lua_rd_check_broken_connection; + + ev = r->connection->read; + + dd("rev active: %d", ev->active); + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && !ev->active) { + if (ngx_add_event(ev, NGX_READ_EVENT, 0) != NGX_OK) { + lua_pushnil(L); + lua_pushliteral(L, "failed to add event"); + return 2; + } + } + + } else { + /* llcf->check_client_abort == 0 */ + r->read_event_handler = ngx_http_block_reading; + } + } +#endif + + if (u->ft_type) { + + if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_TIMEOUT) { + u->no_close = 1; + } + + dd("u->bufs_in: %p", u->bufs_in); + + if (u->bufs_in) { + rc = ngx_http_lua_socket_push_input_data(r, ctx, u, L); + if (rc == NGX_ERROR) { + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + return 2; + } + + (void) ngx_http_lua_socket_read_error_retval_handler(r, u, L); + + lua_pushvalue(L, -3); + lua_remove(L, -4); + return 3; + } + + n = ngx_http_lua_socket_read_error_retval_handler(r, u, L); + lua_pushliteral(L, ""); + return n + 1; + } + + rc = ngx_http_lua_socket_push_input_data(r, ctx, u, L); + if (rc == NGX_ERROR) { + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + return 2; + } + + return 1; +} + + +static int +ngx_http_lua_socket_tcp_close(lua_State *L) +{ + ngx_http_request_t *r; + ngx_http_lua_socket_tcp_upstream_t *u; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting 1 argument " + "(including the object) but seen %d", lua_gettop(L)); + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u == NULL + || u->peer.connection == NULL + || (u->read_closed && u->write_closed)) + { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_http_lua_socket_check_busy_connecting(r, u, L); + ngx_http_lua_socket_check_busy_reading(r, u, L); + ngx_http_lua_socket_check_busy_writing(r, u, L); + + if (u->raw_downstream || u->body_downstream) { + lua_pushnil(L); + lua_pushliteral(L, "attempt to close a request socket"); + return 2; + } + + ngx_http_lua_socket_tcp_finalize(r, u); + + lua_pushinteger(L, 1); + return 1; +} + + +static int +ngx_http_lua_socket_tcp_setoption(lua_State *L) +{ + /* TODO */ + return 0; +} + + +static int +ngx_http_lua_socket_tcp_settimeout(lua_State *L) +{ + int n; + ngx_int_t timeout; + + ngx_http_lua_socket_tcp_upstream_t *u; + + n = lua_gettop(L); + + if (n != 2) { + return luaL_error(L, "ngx.socket settimout: expecting 2 arguments " + "(including the object) but seen %d", lua_gettop(L)); + } + + timeout = (ngx_int_t) lua_tonumber(L, 2); + if (timeout >> 31) { + return luaL_error(L, "bad timeout value"); + } + + lua_pushinteger(L, timeout); + lua_pushinteger(L, timeout); + + lua_rawseti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX); + lua_rawseti(L, 1, SOCKET_SEND_TIMEOUT_INDEX); + lua_rawseti(L, 1, SOCKET_READ_TIMEOUT_INDEX); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u) { + if (timeout > 0) { + u->read_timeout = (ngx_msec_t) timeout; + u->send_timeout = (ngx_msec_t) timeout; + u->connect_timeout = (ngx_msec_t) timeout; + + } else { + u->read_timeout = u->conf->read_timeout; + u->send_timeout = u->conf->send_timeout; + u->connect_timeout = u->conf->connect_timeout; + } + } + + return 0; +} + + +static int +ngx_http_lua_socket_tcp_settimeouts(lua_State *L) +{ + int n; + ngx_int_t connect_timeout, send_timeout, read_timeout; + + ngx_http_lua_socket_tcp_upstream_t *u; + + n = lua_gettop(L); + + if (n != 4) { + return luaL_error(L, "ngx.socket settimout: expecting 4 arguments " + "(including the object) but seen %d", lua_gettop(L)); + } + + connect_timeout = (ngx_int_t) lua_tonumber(L, 2); + if (connect_timeout >> 31) { + return luaL_error(L, "bad timeout value"); + } + + send_timeout = (ngx_int_t) lua_tonumber(L, 3); + if (send_timeout >> 31) { + return luaL_error(L, "bad timeout value"); + } + + read_timeout = (ngx_int_t) lua_tonumber(L, 4); + if (read_timeout >> 31) { + return luaL_error(L, "bad timeout value"); + } + + lua_rawseti(L, 1, SOCKET_READ_TIMEOUT_INDEX); + lua_rawseti(L, 1, SOCKET_SEND_TIMEOUT_INDEX); + lua_rawseti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u) { + if (connect_timeout > 0) { + u->connect_timeout = (ngx_msec_t) connect_timeout; + + } else { + u->connect_timeout = u->conf->connect_timeout; + } + + if (send_timeout > 0) { + u->send_timeout = (ngx_msec_t) send_timeout; + + } else { + u->send_timeout = u->conf->send_timeout; + } + + if (read_timeout > 0) { + u->read_timeout = (ngx_msec_t) read_timeout; + + } else { + u->read_timeout = u->conf->read_timeout; + } + } + + return 0; +} + + +static void +ngx_http_lua_socket_tcp_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_log_ctx_t *ctx; + + ngx_http_lua_socket_tcp_upstream_t *u; + + c = ev->data; + u = c->data; + r = u->request; + c = r->connection; + + if (c->fd != (ngx_socket_t) -1) { /* not a fake connection */ + ctx = c->log->data; + ctx->current_request = r; + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua tcp socket handler for \"%V?%V\", wev %d", &r->uri, + &r->args, (int) ev->write); + + if (ev->write) { + u->write_event_handler(r, u); + + } else { + u->read_event_handler(r, u); + } + + ngx_http_run_posted_requests(c); +} + + +static ngx_int_t +ngx_http_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, void *data) +{ + /* empty */ + return NGX_OK; +} + + +static void +ngx_http_lua_socket_read_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_connection_t *c; + ngx_http_lua_loc_conf_t *llcf; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket read handler"); + + if (c->read->timedout) { + c->read->timedout = 0; + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua tcp socket read timed out"); + } + + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); + return; + } + +#if 1 + if (c->read->timer_set) { + ngx_del_timer(c->read); + } +#endif + + if (u->buffer.start != NULL) { + (void) ngx_http_lua_socket_tcp_read(r, u); + } +} + + +static void +ngx_http_lua_socket_send_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_connection_t *c; + ngx_http_lua_loc_conf_t *llcf; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket send handler"); + + if (c->write->timedout) { + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua tcp socket write timed out"); + } + + ngx_http_lua_socket_handle_write_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); + return; + } + + if (u->request_bufs) { + (void) ngx_http_lua_socket_send(r, u); + } +} + + +static ngx_int_t +ngx_http_lua_socket_send(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_int_t n; + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + ngx_buf_t *b; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket send data"); + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + ngx_http_lua_socket_handle_write_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + b = u->request_bufs->buf; + + for (;;) { + n = c->send(c, b->pos, b->last - b->pos); + + if (n >= 0) { + b->pos += n; + + if (b->pos == b->last) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua tcp socket sent all the data"); + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + +#if defined(nginx_version) && nginx_version >= 1001004 + ngx_chain_update_chains(r->pool, +#else + ngx_chain_update_chains( +#endif + &ctx->free_bufs, &ctx->busy_bufs, + &u->request_bufs, + (ngx_buf_tag_t) &ngx_http_lua_module); + + u->write_event_handler = ngx_http_lua_socket_dummy_handler; + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_http_lua_socket_handle_write_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + ngx_http_lua_socket_handle_write_success(r, u); + return NGX_OK; + } + + /* keep sending more data */ + continue; + } + + /* NGX_ERROR || NGX_AGAIN */ + break; + } + + if (n == NGX_ERROR) { + c->error = 1; + u->socket_errno = ngx_socket_errno; + ngx_http_lua_socket_handle_write_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + /* n == NGX_AGAIN */ + + if (u->raw_downstream) { + ctx->writing_raw_req_socket = 1; + } + + u->write_event_handler = ngx_http_lua_socket_send_handler; + + ngx_add_timer(c->write, u->send_timeout); + + if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) { + ngx_http_lua_socket_handle_write_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + return NGX_ERROR; + } + + return NGX_AGAIN; +} + + +static void +ngx_http_lua_socket_handle_conn_success(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx; + +#if 1 + u->read_event_handler = ngx_http_lua_socket_dummy_handler; + u->write_event_handler = ngx_http_lua_socket_dummy_handler; +#endif + + if (u->conn_waiting) { + u->conn_waiting = 0; + + coctx = u->write_co_ctx; + coctx->cleanup = NULL; + u->write_co_ctx = NULL; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_http_lua_socket_tcp_conn_resume; + ctx->cur_co_ctx = coctx; + + ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket waking up the current request (conn)"); + + r->write_event_handler(r); + } +} + + +static void +ngx_http_lua_socket_handle_read_success(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx; + +#if 1 + u->read_event_handler = ngx_http_lua_socket_dummy_handler; +#endif + + if (u->read_waiting) { + u->read_waiting = 0; + + coctx = u->read_co_ctx; + coctx->cleanup = NULL; + u->read_co_ctx = NULL; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_http_lua_socket_tcp_read_resume; + ctx->cur_co_ctx = coctx; + + ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket waking up the current request (read)"); + + r->write_event_handler(r); + } +} + + +static void +ngx_http_lua_socket_handle_write_success(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx; + +#if 1 + u->write_event_handler = ngx_http_lua_socket_dummy_handler; +#endif + + if (u->write_waiting) { + u->write_waiting = 0; + + coctx = u->write_co_ctx; + coctx->cleanup = NULL; + u->write_co_ctx = NULL; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_http_lua_socket_tcp_write_resume; + ctx->cur_co_ctx = coctx; + + ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket waking up the current request (read)"); + + r->write_event_handler(r); + } +} + + +static void +ngx_http_lua_socket_handle_conn_error(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type) +{ + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket handle connect error"); + + u->ft_type |= ft_type; + +#if 1 + ngx_http_lua_socket_tcp_finalize(r, u); +#endif + + u->read_event_handler = ngx_http_lua_socket_dummy_handler; + u->write_event_handler = ngx_http_lua_socket_dummy_handler; + + dd("connection waiting: %d", (int) u->conn_waiting); + + coctx = u->write_co_ctx; + + if (u->conn_waiting) { + u->conn_waiting = 0; + + coctx->cleanup = NULL; + u->write_co_ctx = NULL; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + ctx->resume_handler = ngx_http_lua_socket_tcp_conn_resume; + ctx->cur_co_ctx = coctx; + + ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket waking up the current request"); + + r->write_event_handler(r); + } +} + + +static void +ngx_http_lua_socket_handle_read_error(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type) +{ + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket handle read error"); + + u->ft_type |= ft_type; + +#if 0 + ngx_http_lua_socket_tcp_finalize(r, u); +#endif + + u->read_event_handler = ngx_http_lua_socket_dummy_handler; + + if (u->read_waiting) { + u->read_waiting = 0; + + coctx = u->read_co_ctx; + coctx->cleanup = NULL; + u->read_co_ctx = NULL; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + ctx->resume_handler = ngx_http_lua_socket_tcp_read_resume; + ctx->cur_co_ctx = coctx; + + ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket waking up the current request"); + + r->write_event_handler(r); + } +} + + +static void +ngx_http_lua_socket_handle_write_error(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type) +{ + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket handle write error"); + + u->ft_type |= ft_type; + +#if 0 + ngx_http_lua_socket_tcp_finalize(r, u); +#endif + + u->write_event_handler = ngx_http_lua_socket_dummy_handler; + + if (u->write_waiting) { + u->write_waiting = 0; + + coctx = u->write_co_ctx; + coctx->cleanup = NULL; + u->write_co_ctx = NULL; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + ctx->resume_handler = ngx_http_lua_socket_tcp_write_resume; + ctx->cur_co_ctx = coctx; + + ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket waking up the current request"); + + r->write_event_handler(r); + } +} + + +static void +ngx_http_lua_socket_connected_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_lua_loc_conf_t *llcf; + + c = u->peer.connection; + + if (c->write->timedout) { + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf->log_socket_errors) { + ngx_http_lua_socket_init_peer_connection_addr_text(&u->peer); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua tcp socket connect timed out," + " when connecting to %V:%ud", + &c->addr_text, ngx_inet_get_port(u->peer.sockaddr)); + } + + ngx_http_lua_socket_handle_conn_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); + return; + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + rc = ngx_http_lua_socket_test_connect(r, c); + if (rc != NGX_OK) { + if (rc > 0) { + u->socket_errno = (ngx_err_t) rc; + } + + ngx_http_lua_socket_handle_conn_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket connected"); + + /* We should delete the current write/read event + * here because the socket object may not be used immediately + * on the Lua land, thus causing hot spin around level triggered + * event poll and wasting CPU cycles. */ + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_http_lua_socket_handle_conn_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + return; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_http_lua_socket_handle_conn_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); + return; + } + + ngx_http_lua_socket_handle_conn_success(r, u); +} + + +static void +ngx_http_lua_socket_tcp_cleanup(void *data) +{ + ngx_http_lua_socket_tcp_upstream_t *u = data; + + ngx_http_request_t *r; + + r = u->request; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "cleanup lua tcp socket request: \"%V\"", &r->uri); + + ngx_http_lua_socket_tcp_finalize(r, u); +} + + +static void +ngx_http_lua_socket_tcp_finalize_read_part(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_chain_t *cl; + ngx_chain_t **ll; + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + + if (u->read_closed) { + return; + } + + u->read_closed = 1; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + if (ctx && u->bufs_in) { + + ll = &u->bufs_in; + for (cl = u->bufs_in; cl; cl = cl->next) { + dd("bufs_in chain: %p, next %p", cl, cl->next); + cl->buf->pos = cl->buf->last; + ll = &cl->next; + } + + dd("ctx: %p", ctx); + dd("free recv bufs: %p", ctx->free_recv_bufs); + *ll = ctx->free_recv_bufs; + ctx->free_recv_bufs = u->bufs_in; + u->bufs_in = NULL; + u->buf_in = NULL; + ngx_memzero(&u->buffer, sizeof(ngx_buf_t)); + } + + if (u->raw_downstream || u->body_downstream) { + if (r->connection->read->timer_set) { + ngx_del_timer(r->connection->read); + } + return; + } + + c = u->peer.connection; + + if (c) { + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->read->active || c->read->disabled) { + ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); + } + +#if defined(nginx_version) && nginx_version >= 1007005 + if (c->read->posted) { +#else + if (c->read->prev) { +#endif + ngx_delete_posted_event(c->read); + } + + c->read->closed = 1; + + /* TODO: shutdown the reading part of the connection */ + } +} + + +static void +ngx_http_lua_socket_tcp_finalize_write_part(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + + if (u->write_closed) { + return; + } + + u->write_closed = 1; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + if (u->raw_downstream || u->body_downstream) { + if (ctx && ctx->writing_raw_req_socket) { + ctx->writing_raw_req_socket = 0; + if (r->connection->write->timer_set) { + ngx_del_timer(r->connection->write); + } + + r->connection->write->error = 1; + } + return; + } + + c = u->peer.connection; + + if (c) { + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + if (c->write->active || c->write->disabled) { + ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT); + } + +#if defined(nginx_version) && nginx_version >= 1007005 + if (c->write->posted) { +#else + if (c->write->prev) { +#endif + ngx_delete_posted_event(c->write); + } + + c->write->closed = 1; + + /* TODO: shutdown the writing part of the connection */ + } +} + + +static void +ngx_http_lua_socket_tcp_conn_op_timeout_handler(ngx_event_t *ev) +{ + ngx_http_lua_socket_tcp_upstream_t *u; + ngx_http_lua_ctx_t *ctx; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_lua_co_ctx_t *coctx; + ngx_http_lua_loc_conf_t *llcf; + ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + conn_op_ctx = ev->data; + ngx_queue_remove(&conn_op_ctx->queue); + + u = conn_op_ctx->u; + r = u->request; + + coctx = u->write_co_ctx; + coctx->cleanup = NULL; + /* note that we store conn_op_ctx in coctx->data instead of u */ + coctx->data = conn_op_ctx; + u->write_co_ctx = NULL; + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf->log_socket_errors) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua tcp socket queued connect timed out," + " when trying to connect to %V:%ud", + &conn_op_ctx->host, conn_op_ctx->port); + } + + ngx_queue_insert_head(&u->socket_pool->cache_connect_op, + &conn_op_ctx->queue); + u->socket_pool->connections--; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + ctx->cur_co_ctx = coctx; + + ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket waking up the current request"); + + u->write_prepare_retvals = + ngx_http_lua_socket_tcp_conn_op_timeout_retval_handler; + + c = r->connection; + + if (ctx->entered_content_phase) { + (void) ngx_http_lua_socket_tcp_conn_op_resume(r); + + } else { + ctx->resume_handler = ngx_http_lua_socket_tcp_conn_op_resume; + ngx_http_core_run_phases(r); + } + + ngx_http_run_posted_requests(c); +} + + +static int +ngx_http_lua_socket_tcp_conn_op_timeout_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + lua_pushnil(L); + lua_pushliteral(L, "timeout"); + return 2; +} + + +static void +ngx_http_lua_socket_tcp_resume_conn_op(ngx_http_lua_socket_pool_t *spool) +{ + ngx_queue_t *q; + ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + +#if (NGX_DEBUG) + ngx_http_lua_assert(spool->connections >= 0); + +#else + if (spool->connections < 0) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "lua tcp socket connections count mismatched for " + "connection pool \"%s\", connections: %i, size: %i", + spool->key, spool->connections, spool->size); + spool->connections = 0; + } +#endif + + /* we manually destroy wait_connect_op before triggering connect + * operation resumption, so that there is no resumption happens when Nginx + * is exiting. + */ + if (ngx_queue_empty(&spool->wait_connect_op)) { + return; + } + + q = ngx_queue_head(&spool->wait_connect_op); + conn_op_ctx = ngx_queue_data(q, ngx_http_lua_socket_tcp_conn_op_ctx_t, + queue); + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua tcp socket post connect operation resumption " + "u: %p, ctx: %p for connection pool \"%s\", " + "connections: %i", + conn_op_ctx->u, conn_op_ctx, spool->key, spool->connections); + + if (conn_op_ctx->event.timer_set) { + ngx_del_timer(&conn_op_ctx->event); + } + + conn_op_ctx->event.handler = + ngx_http_lua_socket_tcp_conn_op_resume_handler; + + ngx_post_event((&conn_op_ctx->event), &ngx_posted_events); +} + + +static void +ngx_http_lua_socket_tcp_conn_op_ctx_cleanup(void *data) +{ + ngx_http_lua_socket_tcp_upstream_t *u; + ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx = data; + + u = conn_op_ctx->u; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, + "cleanup lua tcp socket conn_op_ctx: \"%V\"", + &u->request->uri); + + ngx_queue_insert_head(&u->socket_pool->cache_connect_op, + &conn_op_ctx->queue); +} + + +static void +ngx_http_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev) +{ + ngx_queue_t *q; + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + ngx_http_request_t *r; + ngx_http_cleanup_t *cln; + ngx_http_lua_co_ctx_t *coctx; + ngx_http_lua_socket_pool_t *spool; + ngx_http_lua_socket_tcp_upstream_t *u; + ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + conn_op_ctx = ev->data; + u = conn_op_ctx->u; + r = u->request; + spool = u->socket_pool; + + if (ngx_queue_empty(&spool->wait_connect_op)) { +#if (NGX_DEBUG) + ngx_http_lua_assert(!(spool->backlog >= 0 + && spool->connections > spool->size)); + +#else + if (spool->backlog >= 0 && spool->connections > spool->size) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, + "lua tcp socket connections count mismatched for " + "connection pool \"%s\", connections: %i, size: %i", + spool->key, spool->connections, spool->size); + spool->connections = spool->size; + } +#endif + + return; + } + + q = ngx_queue_head(&spool->wait_connect_op); + ngx_queue_remove(q); + + coctx = u->write_co_ctx; + coctx->cleanup = NULL; + /* note that we store conn_op_ctx in coctx->data instead of u */ + coctx->data = conn_op_ctx; + /* clear ngx_http_lua_tcp_queue_conn_op_cleanup */ + u->write_co_ctx = NULL; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + ngx_queue_insert_head(&spool->cache_connect_op, + &conn_op_ctx->queue); + return; + } + + ctx->cur_co_ctx = coctx; + + ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) + || coctx->co_ref >= 0)); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket waking up the current request"); + + u->write_prepare_retvals = + ngx_http_lua_socket_tcp_conn_op_resume_retval_handler; + + c = r->connection; + + if (ctx->entered_content_phase) { + (void) ngx_http_lua_socket_tcp_conn_op_resume(r); + + } else { + cln = ngx_http_lua_cleanup_add(r, 0); + if (cln != NULL) { + cln->handler = ngx_http_lua_socket_tcp_conn_op_ctx_cleanup; + cln->data = conn_op_ctx; + conn_op_ctx->cleanup = &cln->handler; + } + + ctx->resume_handler = ngx_http_lua_socket_tcp_conn_op_resume; + ngx_http_core_run_phases(r); + } + + ngx_http_run_posted_requests(c); +} + + +static int +ngx_http_lua_socket_tcp_conn_op_resume_retval_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + int nret; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx; + ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + coctx = ctx->cur_co_ctx; + dd("coctx: %p", coctx); + conn_op_ctx = coctx->data; + if (conn_op_ctx->cleanup != NULL) { + *conn_op_ctx->cleanup = NULL; + ngx_http_lua_cleanup_free(r, conn_op_ctx->cleanup); + conn_op_ctx->cleanup = NULL; + } + + /* decrease pending connect operation counter */ + u->socket_pool->connections--; + + nret = ngx_http_lua_socket_tcp_connect_helper(L, u, r, ctx, + conn_op_ctx->host.data, + conn_op_ctx->host.len, + conn_op_ctx->port, 1); + ngx_queue_insert_head(&u->socket_pool->cache_connect_op, + &conn_op_ctx->queue); + + return nret; +} + + +static void +ngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_connection_t *c; + ngx_http_lua_socket_pool_t *spool; + + dd("request: %p, u: %p, u->cleanup: %p", r, u, u->cleanup); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua finalize socket"); + + if (u->cleanup) { + *u->cleanup = NULL; + ngx_http_lua_cleanup_free(r, u->cleanup); + u->cleanup = NULL; + } + + ngx_http_lua_socket_tcp_finalize_read_part(r, u); + ngx_http_lua_socket_tcp_finalize_write_part(r, u); + + if (u->raw_downstream || u->body_downstream) { + u->peer.connection = NULL; + return; + } + + if (u->resolved && u->resolved->ctx) { + ngx_resolve_name_done(u->resolved->ctx); + u->resolved->ctx = NULL; + } + + if (u->peer.free) { + u->peer.free(&u->peer, u->peer.data, 0); + } + +#if (NGX_HTTP_SSL) + if (u->ssl_name.data) { + ngx_free(u->ssl_name.data); + u->ssl_name.data = NULL; + u->ssl_name.len = 0; + } +#endif + + c = u->peer.connection; + if (c) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua close socket connection"); + + ngx_http_lua_socket_tcp_close_connection(c); + u->peer.connection = NULL; + u->conn_closed = 1; + + spool = u->socket_pool; + if (spool == NULL) { + return; + } + + spool->connections--; + + if (spool->connections == 0) { + ngx_http_lua_socket_free_pool(r->connection->log, spool); + return; + } + + ngx_http_lua_socket_tcp_resume_conn_op(spool); + } +} + + +static void +ngx_http_lua_socket_tcp_close_connection(ngx_connection_t *c) +{ +#if (NGX_HTTP_SSL) + + if (c->ssl) { + c->ssl->no_wait_shutdown = 1; + c->ssl->no_send_shutdown = 1; + + (void) ngx_ssl_shutdown(c); + } + +#endif + + if (c->pool) { + ngx_destroy_pool(c->pool); + c->pool = NULL; + } + + ngx_close_connection(c); +} + + +static ngx_int_t +ngx_http_lua_socket_test_connect(ngx_http_request_t *r, ngx_connection_t *c) +{ + int err; + socklen_t len; + + ngx_http_lua_loc_conf_t *llcf; + +#if (NGX_HAVE_KQUEUE) + + ngx_event_t *ev; + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + dd("pending eof: (%p)%d (%p)%d", c->write, c->write->pending_eof, + c->read, c->read->pending_eof); + + if (c->write->pending_eof) { + ev = c->write; + + } else if (c->read->pending_eof) { + ev = c->read; + + } else { + ev = NULL; + } + + if (ev) { + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + if (llcf->log_socket_errors) { + (void) ngx_connection_error(c, ev->kq_errno, + "kevent() reported that " + "connect() failed"); + } + return ev->kq_errno; + } + + } else +#endif + { + err = 0; + len = sizeof(int); + + /* + * BSDs and Linux return 0 and set a pending error in err + * Solaris returns -1 and sets errno + */ + + if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) + == -1) + { + err = ngx_errno; + } + + if (err) { + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + if (llcf->log_socket_errors) { + (void) ngx_connection_error(c, err, "connect() failed"); + } + return err; + } + } + + return NGX_OK; +} + + +static void +ngx_http_lua_socket_dummy_handler(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket dummy handler"); +} + + +static int +ngx_http_lua_socket_tcp_receiveuntil(lua_State *L) +{ + ngx_http_request_t *r; + int n; + ngx_str_t pat; + ngx_int_t rc; + size_t size; + unsigned inclusive = 0; + + ngx_http_lua_socket_compiled_pattern_t *cp; + + n = lua_gettop(L); + if (n != 2 && n != 3) { + return luaL_error(L, "expecting 2 or 3 arguments " + "(including the object), but got %d", n); + } + + if (n == 3) { + /* check out the options table */ + + luaL_checktype(L, 3, LUA_TTABLE); + + lua_getfield(L, 3, "inclusive"); + + switch (lua_type(L, -1)) { + case LUA_TNIL: + /* do nothing */ + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, -1)) { + inclusive = 1; + } + break; + + default: + return luaL_error(L, "bad \"inclusive\" option value type: %s", + luaL_typename(L, -1)); + + } + + lua_pop(L, 2); + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket calling receiveuntil() method"); + + luaL_checktype(L, 1, LUA_TTABLE); + + pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); + if (pat.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "pattern is empty"); + return 2; + } + + size = sizeof(ngx_http_lua_socket_compiled_pattern_t); + + cp = lua_newuserdata(L, size); + if (cp == NULL) { + return luaL_error(L, "no memory"); + } + + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + pattern_udata_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + + ngx_memzero(cp, size); + + cp->inclusive = inclusive; + + rc = ngx_http_lua_socket_compile_pattern(pat.data, pat.len, cp, + r->connection->log); + + if (rc != NGX_OK) { + lua_pushnil(L); + lua_pushliteral(L, "failed to compile pattern"); + return 2; + } + + lua_pushcclosure(L, ngx_http_lua_socket_receiveuntil_iterator, 3); + return 1; +} + + +static int +ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) +{ + ngx_http_request_t *r; + ngx_http_lua_socket_tcp_upstream_t *u; + ngx_int_t rc; + ngx_http_lua_ctx_t *ctx; + lua_Integer bytes; + int n; + ngx_http_lua_co_ctx_t *coctx; + + ngx_http_lua_socket_compiled_pattern_t *cp; + + n = lua_gettop(L); + if (n > 1) { + return luaL_error(L, "expecting 0 or 1 arguments, " + "but seen %d", n); + } + + if (n >= 1) { + bytes = luaL_checkinteger(L, 1); + if (bytes < 0) { + bytes = 0; + } + + } else { + bytes = 0; + } + + lua_rawgeti(L, lua_upvalueindex(1), SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u == NULL || u->peer.connection == NULL || u->read_closed) { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_http_lua_socket_check_busy_connecting(r, u, L); + ngx_http_lua_socket_check_busy_reading(r, u, L); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket receiveuntil iterator"); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket read timeout: %M", u->read_timeout); + + u->input_filter = ngx_http_lua_socket_read_until; + + cp = lua_touserdata(L, lua_upvalueindex(3)); + + dd("checking existing state: %d", cp->state); + + if (cp->state == -1) { + cp->state = 0; + + lua_pushnil(L); + lua_pushnil(L); + lua_pushnil(L); + return 3; + } + + cp->upstream = u; + + cp->pattern.data = + (u_char *) lua_tolstring(L, lua_upvalueindex(2), + &cp->pattern.len); + + u->input_filter_ctx = cp; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + if (u->bufs_in == NULL) { + u->bufs_in = + ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_recv_bufs, + u->conf->buffer_size); + + if (u->bufs_in == NULL) { + return luaL_error(L, "no memory"); + } + + u->buf_in = u->bufs_in; + u->buffer = *u->buf_in->buf; + } + + u->length = (size_t) bytes; + u->rest = u->length; + + if (u->raw_downstream || u->body_downstream) { + r->read_event_handler = ngx_http_lua_req_socket_rev_handler; + } + + u->read_waiting = 0; + u->read_co_ctx = NULL; + + rc = ngx_http_lua_socket_tcp_read(r, u); + + if (rc == NGX_ERROR) { + dd("read failed: %d", (int) u->ft_type); + rc = ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); + dd("tcp receive retval returned: %d", (int) rc); + return rc; + } + + if (rc == NGX_OK) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket receive done in a single run"); + + return ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); + } + + /* rc == NGX_AGAIN */ + + coctx = ctx->cur_co_ctx; + + u->read_event_handler = ngx_http_lua_socket_read_handler; + + ngx_http_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_http_lua_coctx_cleanup; + coctx->data = u; + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_http_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_http_core_run_phases; + } + + u->read_co_ctx = coctx; + u->read_waiting = 1; + u->read_prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler; + + dd("setting data to %p", u); + + if (u->raw_downstream || u->body_downstream) { + ctx->downstream = u; + } + + return lua_yield(L, 0); +} + + +static ngx_int_t +ngx_http_lua_socket_compile_pattern(u_char *data, size_t len, + ngx_http_lua_socket_compiled_pattern_t *cp, ngx_log_t *log) +{ + size_t i; + size_t prefix_len; + size_t size; + unsigned found; + int cur_state, new_state; + + ngx_http_lua_dfa_edge_t *edge; + ngx_http_lua_dfa_edge_t **last = NULL; + + cp->pattern.len = len; + + if (len <= 2) { + return NGX_OK; + } + + for (i = 1; i < len; i++) { + prefix_len = 1; + + while (prefix_len <= len - i - 1) { + + if (ngx_memcmp(data, &data[i], prefix_len) == 0) { + if (data[prefix_len] == data[i + prefix_len]) { + prefix_len++; + continue; + } + + cur_state = i + prefix_len; + new_state = prefix_len + 1; + + if (cp->recovering == NULL) { + size = sizeof(void *) * (len - 2); + cp->recovering = ngx_alloc(size, log); + if (cp->recovering == NULL) { + return NGX_ERROR; + } + + ngx_memzero(cp->recovering, size); + } + + edge = cp->recovering[cur_state - 2]; + + found = 0; + + if (edge == NULL) { + last = &cp->recovering[cur_state - 2]; + + } else { + + for (; edge; edge = edge->next) { + last = &edge->next; + + if (edge->chr == data[prefix_len]) { + found = 1; + + if (edge->new_state < new_state) { + edge->new_state = new_state; + } + + break; + } + } + } + + if (!found) { + ngx_log_debug7(NGX_LOG_DEBUG_HTTP, log, 0, + "lua tcp socket read until recovering point:" + " on state %d (%*s), if next is '%c', then " + "recover to state %d (%*s)", cur_state, + (size_t) cur_state, data, data[prefix_len], + new_state, (size_t) new_state, data); + + edge = ngx_alloc(sizeof(ngx_http_lua_dfa_edge_t), log); + if (edge == NULL) { + return NGX_ERROR; + } + + edge->chr = data[prefix_len]; + edge->new_state = new_state; + edge->next = NULL; + + *last = edge; + } + + break; + } + + break; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_socket_read_until(void *data, ssize_t bytes) +{ + ngx_http_lua_socket_compiled_pattern_t *cp = data; + + ngx_http_lua_socket_tcp_upstream_t *u; + ngx_http_request_t *r; + ngx_buf_t *b; + u_char c; + u_char *pat; + size_t pat_len; + int i; + int state; + int old_state = 0; /* just to make old + gcc happy */ + ngx_http_lua_dfa_edge_t *edge; + unsigned matched; + ngx_int_t rc; + + u = cp->upstream; + r = u->request; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket read until"); + + if (bytes == 0) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; + return NGX_ERROR; + } + + b = &u->buffer; + + pat = cp->pattern.data; + pat_len = cp->pattern.len; + state = cp->state; + + i = 0; + while (i < bytes) { + c = b->pos[i]; + + dd("%d: read char %d, state: %d", i, c, state); + + if (c == pat[state]) { + i++; + state++; + + if (state == (int) pat_len) { + /* already matched the whole pattern */ + dd("pat len: %d", (int) pat_len); + + b->pos += i; + + if (u->length) { + cp->state = -1; + + } else { + cp->state = 0; + } + + if (cp->inclusive) { + rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, 0, + pat, state, + state); + + if (rc != NGX_OK) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; + return NGX_ERROR; + } + } + + return NGX_OK; + } + + continue; + } + + if (state == 0) { + u->buf_in->buf->last++; + + i++; + + if (u->length && --u->rest == 0) { + cp->state = state; + b->pos += i; + return NGX_OK; + } + + continue; + } + + matched = 0; + + if (cp->recovering && state >= 2) { + dd("accessing state: %d, index: %d", state, state - 2); + for (edge = cp->recovering[state - 2]; edge; edge = edge->next) { + + if (edge->chr == c) { + dd("matched '%c' and jumping to state %d", c, + edge->new_state); + + old_state = state; + state = edge->new_state; + matched = 1; + break; + } + } + } + + if (!matched) { +#if 1 + dd("adding pending data: %.*s", state, pat); + rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, i, pat, + state, state); + + if (rc != NGX_OK) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; + return NGX_ERROR; + } + +#endif + + if (u->length) { + if (u->rest <= (size_t) state) { + u->rest = 0; + cp->state = 0; + b->pos += i; + return NGX_OK; + + } else { + u->rest -= state; + } + } + + state = 0; + continue; + } + + /* matched */ + + dd("adding pending data: %.*s", (int) (old_state + 1 - state), + (char *) pat); + + rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, i, pat, + old_state + 1 - state, + old_state); + + if (rc != NGX_OK) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; + return NGX_ERROR; + } + + i++; + + if (u->length) { + if (u->rest <= (size_t) state) { + u->rest = 0; + cp->state = state; + b->pos += i; + return NGX_OK; + + } else { + u->rest -= state; + } + } + + continue; + } + + b->pos += i; + cp->state = state; + + return NGX_AGAIN; +} + + +static int +ngx_http_lua_socket_cleanup_compiled_pattern(lua_State *L) +{ + ngx_http_lua_socket_compiled_pattern_t *cp; + + ngx_http_lua_dfa_edge_t *edge, *p; + unsigned i; + + dd("cleanup compiled pattern"); + + cp = lua_touserdata(L, 1); + if (cp == NULL || cp->recovering == NULL) { + return 0; + } + + dd("pattern len: %d", (int) cp->pattern.len); + + for (i = 0; i < cp->pattern.len - 2; i++) { + edge = cp->recovering[i]; + + while (edge) { + p = edge; + edge = edge->next; + + dd("freeing edge %p", p); + + ngx_free(p); + + dd("edge: %p", edge); + } + } + +#if 1 + ngx_free(cp->recovering); + cp->recovering = NULL; +#endif + + return 0; +} + +static int +ngx_http_lua_socket_req_getfd(lua_State *L) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + + r = ngx_http_lua_get_req(L); + c = r->connection; + if(c == NULL){ + return luaL_error(L, "unknown connection"); + } + lua_pushinteger(L,(int) c->fd); + + return 1; +} + + +static int +ngx_http_lua_req_socket(lua_State *L) +{ + int n, raw; + ngx_peer_connection_t *pc; + ngx_http_lua_loc_conf_t *llcf; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + ngx_http_request_body_t *rb; + ngx_http_cleanup_t *cln; + ngx_http_lua_co_ctx_t *coctx; + + ngx_http_lua_socket_tcp_upstream_t *u; + + n = lua_gettop(L); + if (n == 0) { + raw = 0; + + } else if (n == 1) { + raw = lua_toboolean(L, 1); + lua_pop(L, 1); + + } else { + return luaL_error(L, "expecting zero arguments, but got %d", + lua_gettop(L)); + } + + r = ngx_http_lua_get_req(L); + + if (r != r->main) { + return luaL_error(L, "attempt to read the request body in a " + "subrequest"); + } + +#if (NGX_HTTP_SPDY) + if (r->spdy_stream) { + return luaL_error(L, "spdy not supported yet"); + } +#endif + +#if (NGX_HTTP_V2) + if (r->stream) { + return luaL_error(L, "http v2 not supported yet"); + } +#endif + +#if nginx_version >= 1003009 + if (!raw && r->headers_in.chunked) { + lua_pushnil(L); + lua_pushliteral(L, "chunked request bodies not supported yet"); + return 2; + } +#endif + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE + | NGX_HTTP_LUA_CONTEXT_ACCESS + | NGX_HTTP_LUA_CONTEXT_CONTENT); + + c = r->connection; + + if (raw) { +#if !defined(nginx_version) || nginx_version < 1003013 + lua_pushnil(L); + lua_pushliteral(L, "nginx version too old"); + return 2; +#else + if (r->request_body) { + if (r->request_body->rest > 0) { + lua_pushnil(L); + lua_pushliteral(L, "pending request body reading in some " + "other thread"); + return 2; + } + + } else { + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); + if (rb == NULL) { + return luaL_error(L, "no memory"); + } + r->request_body = rb; + } + + if (c->buffered & NGX_HTTP_LOWLEVEL_BUFFERED) { + lua_pushnil(L); + lua_pushliteral(L, "pending data to write"); + return 2; + } + + if (ctx->buffering) { + lua_pushnil(L); + lua_pushliteral(L, "http 1.0 buffering"); + return 2; + } + + if (!r->header_sent) { + /* prevent other parts of nginx from sending out + * the response header */ + r->header_sent = 1; + } + + ctx->header_sent = 1; + + dd("ctx acquired raw req socket: %d", ctx->acquired_raw_req_socket); + + if (ctx->acquired_raw_req_socket) { + lua_pushnil(L); + lua_pushliteral(L, "duplicate call"); + return 2; + } + + ctx->acquired_raw_req_socket = 1; + r->keepalive = 0; + r->lingering_close = 1; +#endif + + } else { + /* request body reader */ + + if (r->request_body) { + lua_pushnil(L); + lua_pushliteral(L, "request body already exists"); + return 2; + } + + if (r->discard_body) { + lua_pushnil(L); + lua_pushliteral(L, "request body discarded"); + return 2; + } + + dd("req content length: %d", (int) r->headers_in.content_length_n); + + if (r->headers_in.content_length_n <= 0) { + lua_pushnil(L); + lua_pushliteral(L, "no body"); + return 2; + } + + if (ngx_http_lua_test_expect(r) != NGX_OK) { + lua_pushnil(L); + lua_pushliteral(L, "test expect failed"); + return 2; + } + + /* prevent other request body reader from running */ + + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); + if (rb == NULL) { + return luaL_error(L, "no memory"); + } + + rb->rest = r->headers_in.content_length_n; + + r->request_body = rb; + } + + lua_createtable(L, 2 /* narr */, 3 /* nrec */); /* the object */ + + if (raw) { + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + raw_req_socket_metatable_key)); + + } else { + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + req_socket_metatable_key)); + } + + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + + u = lua_newuserdata(L, sizeof(ngx_http_lua_socket_tcp_upstream_t)); + if (u == NULL) { + return luaL_error(L, "no memory"); + } + +#if 1 + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + downstream_udata_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); +#endif + + lua_rawseti(L, 1, SOCKET_CTX_INDEX); + + ngx_memzero(u, sizeof(ngx_http_lua_socket_tcp_upstream_t)); + + if (raw) { + u->raw_downstream = 1; + + } else { + u->body_downstream = 1; + } + + coctx = ctx->cur_co_ctx; + + u->request = r; + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + u->conf = llcf; + + u->read_timeout = u->conf->read_timeout; + u->connect_timeout = u->conf->connect_timeout; + u->send_timeout = u->conf->send_timeout; + + cln = ngx_http_lua_cleanup_add(r, 0); + if (cln == NULL) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; + lua_pushnil(L); + lua_pushliteral(L, "no memory"); + return 2; + } + + cln->handler = ngx_http_lua_socket_tcp_cleanup; + cln->data = u; + u->cleanup = &cln->handler; + + pc = &u->peer; + + pc->log = c->log; + pc->log_error = NGX_ERROR_ERR; + + pc->connection = c; + + dd("setting data to %p", u); + + coctx->data = u; + ctx->downstream = u; + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (raw) { + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + } + + lua_settop(L, 1); + return 1; +} + + +static void +ngx_http_lua_req_socket_rev_handler(ngx_http_request_t *r) +{ + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_socket_tcp_upstream_t *u; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua request socket read event handler"); + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + r->read_event_handler = ngx_http_block_reading; + return; + } + + u = ctx->downstream; + if (u == NULL || u->peer.connection == NULL) { + r->read_event_handler = ngx_http_block_reading; + return; + } + + u->read_event_handler(r, u); +} + +static int +ngx_http_lua_socket_tcp_getreusedtimes(lua_State *L) +{ + ngx_http_lua_socket_tcp_upstream_t *u; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting 1 argument " + "(including the object), but got %d", lua_gettop(L)); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u == NULL + || u->peer.connection == NULL + || (u->read_closed && u->write_closed)) + { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + lua_pushinteger(L, u->reused); + return 1; +} + + +static int +ngx_http_lua_socket_tcp_setkeepalive(lua_State *L) +{ + ngx_http_lua_loc_conf_t *llcf; + ngx_http_lua_socket_tcp_upstream_t *u; + ngx_connection_t *c; + ngx_http_lua_socket_pool_t *spool; + ngx_str_t key; + ngx_queue_t *q; + ngx_peer_connection_t *pc; + ngx_http_request_t *r; + ngx_msec_t timeout; + ngx_int_t pool_size; + int n; + ngx_int_t rc; + ngx_buf_t *b; + const char *msg; + + ngx_http_lua_socket_pool_item_t *item; + + n = lua_gettop(L); + + if (n < 1 || n > 3) { + return luaL_error(L, "expecting 1 to 3 arguments " + "(including the object), but got %d", n); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (u == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + /* stack: obj timeout? size? */ + + pc = &u->peer; + c = pc->connection; + + if (c == NULL || u->read_closed || u->write_closed) { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + if (u->request != r) { + return luaL_error(L, "bad request"); + } + + ngx_http_lua_socket_check_busy_connecting(r, u, L); + ngx_http_lua_socket_check_busy_reading(r, u, L); + ngx_http_lua_socket_check_busy_writing(r, u, L); + + b = &u->buffer; + + if (b->start && ngx_buf_size(b)) { + ngx_http_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, b->pos, + b->last - b->pos); + + lua_pushnil(L); + lua_pushliteral(L, "unread data in buffer"); + return 2; + } + + if (c->read->eof + || c->read->error + || c->read->timedout + || c->write->error + || c->write->timedout) + { + lua_pushnil(L); + lua_pushliteral(L, "invalid connection"); + return 2; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + lua_pushnil(L); + lua_pushliteral(L, "failed to handle read event"); + return 2; + } + + if (ngx_terminate || ngx_exiting) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "lua tcp socket set keepalive while process exiting, " + "closing connection %p", c); + + ngx_http_lua_socket_tcp_finalize(r, u); + lua_pushinteger(L, 1); + return 1; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "lua tcp socket set keepalive: saving connection %p", c); + + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(socket_pool_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + + /* stack: obj timeout? size? pools */ + + lua_rawgeti(L, 1, SOCKET_KEY_INDEX); + key.data = (u_char *) lua_tolstring(L, -1, &key.len); + if (key.data == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "key not found"); + return 2; + } + + dd("saving connection to key %s", lua_tostring(L, -1)); + + lua_pushvalue(L, -1); + lua_rawget(L, -3); + spool = lua_touserdata(L, -1); + lua_pop(L, 1); + + /* stack: obj timeout? size? pools cache_key */ + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (spool == NULL) { + /* create a new socket pool for the current peer key */ + + if (n >= 3 && !lua_isnil(L, 3)) { + pool_size = luaL_checkinteger(L, 3); + + } else { + pool_size = llcf->pool_size; + } + + if (pool_size <= 0) { + msg = lua_pushfstring(L, "bad \"pool_size\" option value: %i", + pool_size); + return luaL_argerror(L, n, msg); + } + + ngx_http_lua_socket_tcp_create_socket_pool(L, r, key, pool_size, -1, + &spool); + } + + if (ngx_queue_empty(&spool->free)) { + + q = ngx_queue_last(&spool->cache); + ngx_queue_remove(q); + + item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue); + + ngx_http_lua_socket_tcp_close_connection(item->connection); + + /* only decrease the counter for connections which were counted */ + if (u->socket_pool != NULL) { + u->socket_pool->connections--; + } + + } else { + q = ngx_queue_head(&spool->free); + ngx_queue_remove(q); + + item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue); + + /* we should always increase connections after getting connected, + * and decrease connections after getting closed. + * however, we don't create connection pool in previous connect method. + * so we increase connections here for backward compatibility. + */ + if (u->socket_pool == NULL) { + spool->connections++; + } + } + + item->connection = c; + ngx_queue_insert_head(&spool->cache, q); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "lua tcp socket clear current socket connection"); + + pc->connection = NULL; + +#if 0 + if (u->cleanup) { + *u->cleanup = NULL; + u->cleanup = NULL; + } +#endif + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + if (n >= 2 && !lua_isnil(L, 2)) { + timeout = (ngx_msec_t) luaL_checkinteger(L, 2); + + } else { + timeout = llcf->keepalive_timeout; + } + +#if (NGX_DEBUG) + if (timeout == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket keepalive timeout: unlimited"); + } +#endif + + if (timeout) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket keepalive timeout: %M ms", timeout); + + ngx_add_timer(c->read, timeout); + } + + c->write->handler = ngx_http_lua_socket_keepalive_dummy_handler; + c->read->handler = ngx_http_lua_socket_keepalive_rev_handler; + + c->data = item; + c->idle = 1; + c->log = ngx_cycle->log; + c->pool->log = ngx_cycle->log; + c->read->log = ngx_cycle->log; + c->write->log = ngx_cycle->log; + + item->socklen = pc->socklen; + ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen); + item->reused = u->reused; + + if (c->read->ready) { + rc = ngx_http_lua_socket_keepalive_close_handler(c->read); + if (rc != NGX_OK) { + lua_pushnil(L); + lua_pushliteral(L, "connection in dubious state"); + return 2; + } + } + +#if 1 + ngx_http_lua_socket_tcp_finalize(r, u); +#endif + + /* since we set u->peer->connection to NULL previously, the connect + * operation won't be resumed in the ngx_http_lua_socket_tcp_finalize. + * Therefore we need to resume it here. + */ + ngx_http_lua_socket_tcp_resume_conn_op(spool); + + lua_pushinteger(L, 1); + return 1; +} + + +static ngx_int_t +ngx_http_lua_get_keepalive_peer(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_http_lua_socket_pool_item_t *item; + ngx_http_lua_socket_pool_t *spool; + ngx_http_cleanup_t *cln; + ngx_queue_t *q; + ngx_peer_connection_t *pc; + ngx_connection_t *c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket pool get keepalive peer"); + + pc = &u->peer; + spool = u->socket_pool; + + if (!ngx_queue_empty(&spool->cache)) { + q = ngx_queue_head(&spool->cache); + + item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue); + c = item->connection; + + ngx_queue_remove(q); + ngx_queue_insert_head(&spool->free, q); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "lua tcp socket get keepalive peer: using connection %p," + " fd:%d", c, c->fd); + + c->idle = 0; + c->log = pc->log; + c->pool->log = pc->log; + c->read->log = pc->log; + c->write->log = pc->log; + c->data = u; + +#if 1 + c->write->handler = ngx_http_lua_socket_tcp_handler; + c->read->handler = ngx_http_lua_socket_tcp_handler; +#endif + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + pc->connection = c; + pc->cached = 1; + + u->reused = item->reused + 1; + +#if 1 + u->write_event_handler = ngx_http_lua_socket_dummy_handler; + u->read_event_handler = ngx_http_lua_socket_dummy_handler; +#endif + + if (u->cleanup == NULL) { + cln = ngx_http_lua_cleanup_add(r, 0); + if (cln == NULL) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; + return NGX_ERROR; + } + + cln->handler = ngx_http_lua_socket_tcp_cleanup; + cln->data = u; + u->cleanup = &cln->handler; + } + + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "lua tcp socket keepalive: connection pool empty"); + + return NGX_DECLINED; +} + + +static void +ngx_http_lua_socket_keepalive_dummy_handler(ngx_event_t *ev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "keepalive dummy handler"); +} + + +static void +ngx_http_lua_socket_keepalive_rev_handler(ngx_event_t *ev) +{ + (void) ngx_http_lua_socket_keepalive_close_handler(ev); +} + + +static ngx_int_t +ngx_http_lua_socket_keepalive_close_handler(ngx_event_t *ev) +{ + ngx_http_lua_socket_pool_item_t *item; + ngx_http_lua_socket_pool_t *spool; + + int n; + char buf[1]; + ngx_connection_t *c; + + c = ev->data; + + if (c->close) { + goto close; + } + + if (c->read->timedout) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "lua tcp socket keepalive max idle timeout"); + + goto close; + } + + dd("read event ready: %d", (int) c->read->ready); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "lua tcp socket keepalive close handler check stale events"); + + n = recv(c->fd, buf, 1, MSG_PEEK); + + if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { + /* stale event */ + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + goto close; + } + + return NGX_OK; + } + +close: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "lua tcp socket keepalive close handler: fd:%d", c->fd); + + item = c->data; + spool = item->socket_pool; + + ngx_http_lua_socket_tcp_close_connection(c); + + ngx_queue_remove(&item->queue); + ngx_queue_insert_head(&spool->free, &item->queue); + spool->connections--; + + dd("keepalive: connections: %u", (unsigned) spool->connections); + + if (spool->connections == 0) { + ngx_http_lua_socket_free_pool(ev->log, spool); + + } else { + ngx_http_lua_socket_tcp_resume_conn_op(spool); + } + + return NGX_DECLINED; +} + + +static void +ngx_http_lua_socket_free_pool(ngx_log_t *log, ngx_http_lua_socket_pool_t *spool) +{ + lua_State *L; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "lua tcp socket keepalive: free connection pool for \"%s\"", + spool->key); + + L = spool->lua_vm; + + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(socket_pool_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushstring(L, (char *) spool->key); + lua_pushnil(L); + lua_rawset(L, -3); + lua_pop(L, 1); +} + + +static void +ngx_http_lua_socket_shutdown_pool_helper(ngx_http_lua_socket_pool_t *spool) +{ + ngx_queue_t *q; + ngx_connection_t *c; + ngx_http_lua_socket_pool_item_t *item; + ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + while (!ngx_queue_empty(&spool->cache)) { + q = ngx_queue_head(&spool->cache); + + item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue); + c = item->connection; + + ngx_http_lua_socket_tcp_close_connection(c); + + ngx_queue_remove(q); + ngx_queue_insert_head(&spool->free, q); + } + + while (!ngx_queue_empty(&spool->cache_connect_op)) { + q = ngx_queue_head(&spool->cache_connect_op); + ngx_queue_remove(q); + conn_op_ctx = ngx_queue_data(q, ngx_http_lua_socket_tcp_conn_op_ctx_t, + queue); + ngx_http_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx); + } + + while (!ngx_queue_empty(&spool->wait_connect_op)) { + q = ngx_queue_head(&spool->wait_connect_op); + ngx_queue_remove(q); + conn_op_ctx = ngx_queue_data(q, ngx_http_lua_socket_tcp_conn_op_ctx_t, + queue); + + if (conn_op_ctx->event.timer_set) { + ngx_del_timer(&conn_op_ctx->event); + } + + ngx_http_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx); + } + + /* spool->connections will be decreased down to zero in + * ngx_http_lua_socket_tcp_finalize */ +} + + +static int +ngx_http_lua_socket_shutdown_pool(lua_State *L) +{ + ngx_http_lua_socket_pool_t *spool; + + spool = lua_touserdata(L, 1); + + if (spool != NULL) { + ngx_http_lua_socket_shutdown_pool_helper(spool); + } + + return 0; +} + + +static int +ngx_http_lua_socket_tcp_upstream_destroy(lua_State *L) +{ + ngx_http_lua_socket_tcp_upstream_t *u; + + dd("upstream destroy triggered by Lua GC"); + + u = lua_touserdata(L, 1); + if (u == NULL) { + return 0; + } + + if (u->cleanup) { + ngx_http_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */ + } + + return 0; +} + + +static int +ngx_http_lua_socket_downstream_destroy(lua_State *L) +{ + ngx_http_lua_socket_tcp_upstream_t *u; + + dd("downstream destroy"); + + u = lua_touserdata(L, 1); + if (u == NULL) { + dd("u is NULL"); + return 0; + } + + if (u->cleanup) { + ngx_http_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */ + } + + return 0; +} + + +static ngx_int_t +ngx_http_lua_socket_push_input_data(ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx, ngx_http_lua_socket_tcp_upstream_t *u, + lua_State *L) +{ + ngx_chain_t *cl; + ngx_chain_t **ll; +#if (DDEBUG) || (NGX_DTRACE) + size_t size = 0; +#endif + size_t chunk_size; + ngx_buf_t *b; + size_t nbufs; + luaL_Buffer luabuf; + + dd("bufs_in: %p, buf_in: %p", u->bufs_in, u->buf_in); + + nbufs = 0; + ll = NULL; + + luaL_buffinit(L, &luabuf); + + for (cl = u->bufs_in; cl; cl = cl->next) { + b = cl->buf; + chunk_size = b->last - b->pos; + + dd("copying input data chunk from %p: \"%.*s\"", cl, + (int) chunk_size, b->pos); + + luaL_addlstring(&luabuf, (char *) b->pos, chunk_size); + + if (cl->next) { + ll = &cl->next; + } + +#if (DDEBUG) || (NGX_DTRACE) + size += chunk_size; +#endif + + nbufs++; + } + + luaL_pushresult(&luabuf); + +#if (DDEBUG) + dd("size: %d, nbufs: %d", (int) size, (int) nbufs); +#endif + +#if (NGX_DTRACE) + ngx_http_lua_probe_socket_tcp_receive_done(r, u, + (u_char *) lua_tostring(L, -1), + size); +#endif + + if (nbufs > 1 && ll) { + dd("recycle buffers: %d", (int) (nbufs - 1)); + + *ll = ctx->free_recv_bufs; + ctx->free_recv_bufs = u->bufs_in; + u->bufs_in = u->buf_in; + } + + if (u->buffer.pos == u->buffer.last) { + dd("resetting u->buffer pos & last"); + u->buffer.pos = u->buffer.start; + u->buffer.last = u->buffer.start; + } + + if (u->bufs_in) { + u->buf_in->buf->last = u->buffer.pos; + u->buf_in->buf->pos = u->buffer.pos; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_socket_add_input_buffer(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_chain_t *cl; + ngx_http_lua_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_recv_bufs, + u->conf->buffer_size); + + if (cl == NULL) { + return NGX_ERROR; + } + + u->buf_in->next = cl; + u->buf_in = cl; + u->buffer = *cl->buf; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_socket_add_pending_data(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, u_char *pos, size_t len, u_char *pat, + int prefix, int old_state) +{ + u_char *last; + ngx_buf_t *b; + + dd("resuming data: %d: [%.*s]", prefix, prefix, pat); + + last = &pos[len]; + + b = u->buf_in->buf; + + if (last - b->last == old_state) { + b->last += prefix; + return NGX_OK; + } + + dd("need more buffers because %d != %d", (int) (last - b->last), + (int) old_state); + + if (ngx_http_lua_socket_insert_buffer(r, u, pat, prefix) != NGX_OK) { + return NGX_ERROR; + } + + b->pos = last; + b->last = last; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_lua_socket_insert_buffer(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, u_char *pat, size_t prefix) +{ + ngx_chain_t *cl, *new_cl, **ll; + ngx_http_lua_ctx_t *ctx; + size_t size; + ngx_buf_t *b; + + if (prefix <= u->conf->buffer_size) { + size = u->conf->buffer_size; + + } else { + size = prefix; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + new_cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_recv_bufs, + size); + + if (new_cl == NULL) { + return NGX_ERROR; + } + + b = new_cl->buf; + + b->last = ngx_copy(b->last, pat, prefix); + + dd("copy resumed data to %p: %d: \"%.*s\"", + new_cl, (int) (b->last - b->pos), (int) (b->last - b->pos), b->pos); + + dd("before resuming data: bufs_in %p, buf_in %p, buf_in next %p", + u->bufs_in, u->buf_in, u->buf_in->next); + + ll = &u->bufs_in; + for (cl = u->bufs_in; cl->next; cl = cl->next) { + ll = &cl->next; + } + + *ll = new_cl; + new_cl->next = u->buf_in; + + dd("after resuming data: bufs_in %p, buf_in %p, buf_in next %p", + u->bufs_in, u->buf_in, u->buf_in->next); + +#if (DDEBUG) + for (cl = u->bufs_in; cl; cl = cl->next) { + b = cl->buf; + + dd("result buf after resuming data: %p: %.*s", cl, + (int) ngx_buf_size(b), b->pos); + } +#endif + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_socket_tcp_conn_op_resume(ngx_http_request_t *r) +{ + return ngx_http_lua_socket_tcp_resume_helper(r, SOCKET_OP_RESUME_CONN); +} + + +static ngx_int_t +ngx_http_lua_socket_tcp_conn_resume(ngx_http_request_t *r) +{ + return ngx_http_lua_socket_tcp_resume_helper(r, SOCKET_OP_CONNECT); +} + + +static ngx_int_t +ngx_http_lua_socket_tcp_read_resume(ngx_http_request_t *r) +{ + return ngx_http_lua_socket_tcp_resume_helper(r, SOCKET_OP_READ); +} + + +static ngx_int_t +ngx_http_lua_socket_tcp_write_resume(ngx_http_request_t *r) +{ + return ngx_http_lua_socket_tcp_resume_helper(r, SOCKET_OP_WRITE); +} + + +static ngx_int_t +ngx_http_lua_socket_tcp_resume_helper(ngx_http_request_t *r, int socket_op) +{ + int nret; + lua_State *vm; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx; + ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + ngx_http_lua_socket_tcp_retval_handler prepare_retvals; + + ngx_http_lua_socket_tcp_upstream_t *u; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ctx->resume_handler = ngx_http_lua_wev_handler; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp operation done, resuming lua thread"); + + coctx = ctx->cur_co_ctx; + + dd("coctx: %p", coctx); + + switch (socket_op) { + + case SOCKET_OP_RESUME_CONN: + conn_op_ctx = coctx->data; + u = conn_op_ctx->u; + prepare_retvals = u->write_prepare_retvals; + break; + + case SOCKET_OP_CONNECT: + case SOCKET_OP_WRITE: + u = coctx->data; + prepare_retvals = u->write_prepare_retvals; + break; + + case SOCKET_OP_READ: + u = coctx->data; + prepare_retvals = u->read_prepare_retvals; + break; + + default: + /* impossible to reach here */ + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket calling prepare retvals handler %p, " + "u:%p", prepare_retvals, u); + + nret = prepare_retvals(r, u, ctx->cur_co_ctx->co); + if (socket_op == SOCKET_OP_CONNECT + && nret > 1 + && !u->conn_closed + && u->socket_pool != NULL) + { + u->socket_pool->connections--; + ngx_http_lua_socket_tcp_resume_conn_op(u->socket_pool); + } + + if (nret == NGX_AGAIN) { + return NGX_DONE; + } + + c = r->connection; + vm = ngx_http_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + rc = ngx_http_lua_run_thread(vm, r, ctx, nret); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_http_lua_finalize_request(r, NGX_DONE); + return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (ctx->entered_content_phase) { + ngx_http_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; +} + + +static void +ngx_http_lua_tcp_queue_conn_op_cleanup(void *data) +{ + ngx_http_lua_co_ctx_t *coctx = data; + ngx_http_lua_socket_tcp_upstream_t *u; + ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; + + conn_op_ctx = coctx->data; + u = conn_op_ctx->u; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua tcp socket abort queueing, conn_op_ctx: %p, u: %p", + conn_op_ctx, u); + + if (conn_op_ctx->event.posted) { + ngx_delete_posted_event(&conn_op_ctx->event); + + } else if (conn_op_ctx->event.timer_set) { + ngx_del_timer(&conn_op_ctx->event); + } + + ngx_queue_remove(&conn_op_ctx->queue); + ngx_queue_insert_head(&u->socket_pool->cache_connect_op, + &conn_op_ctx->queue); + + u->socket_pool->connections--; + ngx_http_lua_socket_tcp_resume_conn_op(u->socket_pool); +} + + +static void +ngx_http_lua_tcp_resolve_cleanup(void *data) +{ + ngx_resolver_ctx_t *rctx; + ngx_http_lua_socket_tcp_upstream_t *u; + ngx_http_lua_co_ctx_t *coctx = data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua tcp socket abort resolver"); + + u = coctx->data; + if (u == NULL) { + return; + } + + if (u->socket_pool != NULL) { + u->socket_pool->connections--; + ngx_http_lua_socket_tcp_resume_conn_op(u->socket_pool); + } + + rctx = u->resolved->ctx; + if (rctx == NULL) { + return; + } + + /* just to be safer */ + rctx->handler = ngx_http_lua_socket_empty_resolve_handler; + + ngx_resolve_name_done(rctx); +} + + +static void +ngx_http_lua_coctx_cleanup(void *data) +{ + ngx_http_lua_socket_tcp_upstream_t *u; + ngx_http_lua_co_ctx_t *coctx = data; + + dd("running coctx cleanup"); + + u = coctx->data; + if (u == NULL) { + return; + } + + if (u->request == NULL) { + return; + } + + ngx_http_lua_socket_tcp_finalize(u->request, u); +} + + +#if (NGX_HTTP_SSL) + +static int +ngx_http_lua_ssl_free_session(lua_State *L) +{ + ngx_ssl_session_t **psession; + + psession = lua_touserdata(L, 1); + if (psession && *psession != NULL) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua ssl free session: %p", *psession); + + ngx_ssl_free_session(*psession); + } + + return 0; +} + +#endif /* NGX_HTTP_SSL */ + + +void +ngx_http_lua_cleanup_conn_pools(lua_State *L) +{ + ngx_http_lua_socket_pool_t *spool; + + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + socket_pool_key)); + lua_rawget(L, LUA_REGISTRYINDEX); /* table */ + + lua_pushnil(L); /* first key */ + while (lua_next(L, -2) != 0) { + /* tb key val */ + spool = lua_touserdata(L, -1); + + if (spool != NULL) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua tcp socket keepalive: free connection pool %p " + "for \"%s\"", spool, spool->key); + + ngx_http_lua_socket_shutdown_pool_helper(spool); + } + + lua_pop(L, 1); + } + + lua_pop(L, 1); +} + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_ssl.h b/src/ngx_http_lua_ssl.h index acb8c4b16c..b85df98fa4 100644 --- a/src/ngx_http_lua_ssl.h +++ b/src/ngx_http_lua_ssl.h @@ -25,11 +25,13 @@ typedef struct { ngx_str_t session_id; int exit_code; /* exit code for openssl's + set_client_hello_cb or set_cert_cb callback */ unsigned done:1; unsigned aborted:1; + unsigned entered_client_hello_handler:1; unsigned entered_cert_handler:1; unsigned entered_sess_fetch_handler:1; } ngx_http_lua_ssl_ctx_t; diff --git a/src/ngx_http_lua_ssl_client_helloby.c b/src/ngx_http_lua_ssl_client_helloby.c new file mode 100644 index 0000000000..2413928ffc --- /dev/null +++ b/src/ngx_http_lua_ssl_client_helloby.c @@ -0,0 +1,724 @@ + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#if (NGX_HTTP_SSL) + + +#include "ngx_http_lua_cache.h" +#include "ngx_http_lua_initworkerby.h" +#include "ngx_http_lua_util.h" +#include "ngx_http_ssl_module.h" +#include "ngx_http_lua_contentby.h" +#include "ngx_http_lua_ssl_client_helloby.h" +#include "ngx_http_lua_directive.h" +#include "ngx_http_lua_ssl.h" + + +static void ngx_http_lua_ssl_client_hello_done(void *data); +static void ngx_http_lua_ssl_client_hello_aborted(void *data); +static u_char *ngx_http_lua_log_ssl_client_hello_error(ngx_log_t *log, + u_char *buf, size_t len); +static ngx_int_t ngx_http_lua_ssl_client_hello_by_chunk(lua_State *L, + ngx_http_request_t *r); + + +ngx_int_t +ngx_http_lua_ssl_client_hello_handler_file(ngx_http_request_t *r, + ngx_http_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_http_lua_cache_loadfile(r->connection->log, L, + lscf->srv.ssl_client_hello_src.data, + lscf->srv.ssl_client_hello_src_key); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_http_lua_assert(lua_isfunction(L, -1)); + + return ngx_http_lua_ssl_client_hello_by_chunk(L, r); +} + + +ngx_int_t +ngx_http_lua_ssl_client_hello_handler_inline(ngx_http_request_t *r, + ngx_http_lua_srv_conf_t *lscf, lua_State *L) +{ + ngx_int_t rc; + + rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L, + lscf->srv.ssl_client_hello_src.data, + lscf->srv.ssl_client_hello_src.len, + lscf->srv.ssl_client_hello_src_key, + "=ssl_client_hello_by_lua"); + if (rc != NGX_OK) { + return rc; + } + + /* make sure we have a valid code chunk */ + ngx_http_lua_assert(lua_isfunction(L, -1)); + + return ngx_http_lua_ssl_client_hello_by_chunk(L, r); +} + + +char * +ngx_http_lua_ssl_client_hello_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_http_lua_ssl_client_hello_by_lua; + cf->handler_conf = conf; + + rv = ngx_http_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_http_lua_ssl_client_hello_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ +#ifndef SSL_ERROR_WANT_CLIENT_HELLO_CB + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "at least OpenSSL 1.1.1 required but found " + OPENSSL_VERSION_TEXT); + + return NGX_CONF_ERROR; + +#else + + u_char *p; + u_char *name; + ngx_str_t *value; + ngx_http_lua_srv_conf_t *lscf = conf; + + /* must specify a concrete handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (lscf->srv.ssl_client_hello_handler) { + return "is duplicate"; + } + + if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + lscf->srv.ssl_client_hello_handler = + (ngx_http_lua_srv_conf_handler_pt) cmd->post; + + if (cmd->post == ngx_http_lua_ssl_client_hello_handler_file) { + /* Lua code in an external file */ + + name = ngx_http_lua_rebase_path(cf->pool, value[1].data, + value[1].len); + if (name == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_client_hello_src.data = name; + lscf->srv.ssl_client_hello_src.len = ngx_strlen(name); + + p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_client_hello_src_key = p; + + p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN); + p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + + } else { + /* inlined Lua code */ + + lscf->srv.ssl_client_hello_src = value[1]; + + p = ngx_palloc(cf->pool, + sizeof("ssl_client_hello_by_lua") + + NGX_HTTP_LUA_INLINE_KEY_LEN); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + lscf->srv.ssl_client_hello_src_key = p; + + p = ngx_copy(p, "ssl_client_hello_by_lua", + sizeof("ssl_client_hello_by_lua") - 1); + p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN); + p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); + *p = '\0'; + } + + return NGX_CONF_OK; + +#endif /* NO SSL_ERROR_WANT_CLIENT_HELLO_CB */ +} + + +int +ngx_http_lua_ssl_client_hello_handler(ngx_ssl_conn_t *ssl_conn, + int *al, void *arg) +{ + lua_State *L; + ngx_int_t rc; + ngx_connection_t *c, *fc; + ngx_http_request_t *r = NULL; + ngx_pool_cleanup_t *cln; + ngx_http_connection_t *hc; + ngx_http_lua_srv_conf_t *lscf; + ngx_http_core_loc_conf_t *clcf; + ngx_http_lua_ssl_ctx_t *cctx; + ngx_http_core_srv_conf_t *cscf; + + c = ngx_ssl_get_connection(ssl_conn); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "ssl client hello: connection reusable: %ud", c->reusable); + + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + + dd("ssl client_hello handler, client_hello-ctx=%p", cctx); + + if (cctx && cctx->entered_client_hello_handler) { + /* not the first time */ + + if (cctx->done) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua_client_hello_by_lua: " + "client_hello cb exit code: %d", + cctx->exit_code); + + dd("lua ssl client_hello done, finally"); + return cctx->exit_code; + } + + return -1; + } + + dd("first time"); + + ngx_reusable_connection(c, 0); + + hc = c->data; + + fc = ngx_http_lua_create_fake_connection(NULL); + if (fc == NULL) { + goto failed; + } + + fc->log->handler = ngx_http_lua_log_ssl_client_hello_error; + fc->log->data = fc; + + fc->addr_text = c->addr_text; + fc->listening = c->listening; + + r = ngx_http_lua_create_fake_request(fc); + if (r == NULL) { + goto failed; + } + + r->main_conf = hc->conf_ctx->main_conf; + r->srv_conf = hc->conf_ctx->srv_conf; + r->loc_conf = hc->conf_ctx->loc_conf; + + fc->log->file = c->log->file; + fc->log->log_level = c->log->log_level; + fc->ssl = c->ssl; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + +#if defined(nginx_version) && nginx_version >= 1003014 + +# if nginx_version >= 1009000 + + ngx_set_connection_log(fc, clcf->error_log); + +# else + + ngx_http_set_connection_log(fc, clcf->error_log); + +# endif + +#else + + fc->log->file = clcf->error_log->file; + + if (!(fc->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { + fc->log->log_level = clcf->error_log->log_level; + } + +#endif + + if (cctx == NULL) { + cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t)); + if (cctx == NULL) { + goto failed; /* error */ + } + } + + cctx->exit_code = 1; /* successful by default */ + cctx->connection = c; + cctx->request = r; + cctx->entered_client_hello_handler = 1; + cctx->done = 0; + + dd("setting cctx"); + + if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, cctx) + == 0) + { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed"); + goto failed; + } + + lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module); + + /* TODO honor lua_code_cache off */ + L = ngx_http_lua_get_lua_vm(r, NULL); + + c->log->action = "loading SSL client_hello by lua"; + + if (lscf->srv.ssl_client_hello_handler == NULL) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "no ssl_client_hello_by_lua* defined in " + "server %V", &cscf->server_name); + + goto failed; + } + + rc = lscf->srv.ssl_client_hello_handler(r, lscf, L); + + if (rc >= NGX_OK || rc == NGX_ERROR) { + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua_client_hello_by_lua: handler return value: %i, " + "client_hello cb exit code: %d", rc, cctx->exit_code); + + c->log->action = "SSL handshaking"; + return cctx->exit_code; + } + + /* rc == NGX_DONE */ + + cln = ngx_pool_cleanup_add(fc->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->handler = ngx_http_lua_ssl_client_hello_done; + cln->data = cctx; + + if (cctx->cleanup == NULL) { + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->data = cctx; + cctx->cleanup = &cln->handler; + } + + *cctx->cleanup = ngx_http_lua_ssl_client_hello_aborted; + + return -1; + +#if 1 +failed: + + if (r && r->pool) { + ngx_http_lua_free_fake_request(r); + } + + if (fc) { + ngx_http_lua_close_fake_connection(fc); + } + + return 0; +#endif +} + + +static void +ngx_http_lua_ssl_client_hello_done(void *data) +{ + ngx_connection_t *c; + ngx_http_lua_ssl_ctx_t *cctx = data; + + dd("lua ssl client_hello done"); + + if (cctx->aborted) { + return; + } + + ngx_http_lua_assert(cctx->done == 0); + + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + c = cctx->connection; + + c->log->action = "SSL handshaking"; + + ngx_post_event(c->write, &ngx_posted_events); +} + + +static void +ngx_http_lua_ssl_client_hello_aborted(void *data) +{ + ngx_http_lua_ssl_ctx_t *cctx = data; + + dd("lua ssl client_hello done"); + + if (cctx->done) { + /* completed successfully already */ + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0, + "lua_client_hello_by_lua: client_hello cb aborted"); + + cctx->aborted = 1; + cctx->request->connection->ssl = NULL; + + ngx_http_lua_finalize_fake_request(cctx->request, NGX_ERROR); +} + + +static u_char * +ngx_http_lua_log_ssl_client_hello_error(ngx_log_t *log, + u_char *buf, size_t len) +{ + u_char *p; + ngx_connection_t *c; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + buf = p; + } + + p = ngx_snprintf(buf, len, ", context: ssl_client_hello_by_lua*"); + len -= p - buf; + buf = p; + + c = log->data; + + if (c->addr_text.len) { + p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); + len -= p - buf; + buf = p; + } + + if (c && c->listening && c->listening->addr_text.len) { + p = ngx_snprintf(buf, len, ", server: %V", &c->listening->addr_text); + /* len -= p - buf; */ + buf = p; + } + + return buf; +} + + +static ngx_int_t +ngx_http_lua_ssl_client_hello_by_chunk(lua_State *L, ngx_http_request_t *r) +{ + int co_ref; + ngx_int_t rc; + lua_State *co; + ngx_http_lua_ctx_t *ctx; + ngx_http_cleanup_t *cln; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + if (ctx == NULL) { + ctx = ngx_http_lua_create_ctx(r); + if (ctx == NULL) { + rc = NGX_ERROR; + ngx_http_lua_finalize_request(r, rc); + return rc; + } + + } else { + dd("reset ctx"); + ngx_http_lua_reset_ctx(r, L, ctx); + } + + ctx->entered_content_phase = 1; + + /* {{{ new coroutine to handle request */ + co = ngx_http_lua_new_thread(r, L, &co_ref); + + if (co == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua: failed to create new coroutine to handle request"); + + rc = NGX_ERROR; + ngx_http_lua_finalize_request(r, rc); + return rc; + } + + /* move code closure to new coroutine */ + lua_xmove(L, co, 1); + +#ifndef OPENRESTY_LUAJIT + /* set closure's env table to new coroutine's globals table */ + ngx_http_lua_get_globals_table(co); + lua_setfenv(co, -2); +#endif + + /* save nginx request in coroutine globals table */ + ngx_http_lua_set_req(co, r); + + ctx->cur_co_ctx = &ctx->entry_co_ctx; + ctx->cur_co_ctx->co = co; + ctx->cur_co_ctx->co_ref = co_ref; +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 1; +#endif + + /* register request cleanup hooks */ + if (ctx->cleanup == NULL) { + cln = ngx_http_cleanup_add(r, 0); + if (cln == NULL) { + rc = NGX_ERROR; + ngx_http_lua_finalize_request(r, rc); + return rc; + } + + cln->handler = ngx_http_lua_request_cleanup_handler; + cln->data = ctx; + ctx->cleanup = &cln->handler; + } + + ctx->context = NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO; + + rc = ngx_http_lua_run_thread(L, r, ctx, 0); + + if (rc == NGX_ERROR || rc >= NGX_OK) { + /* do nothing */ + + } else if (rc == NGX_AGAIN) { + rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0); + + } else if (rc == NGX_DONE) { + rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1); + + } else { + rc = NGX_OK; + } + + ngx_http_lua_finalize_request(r, rc); + return rc; +} + + +#ifndef NGX_LUA_NO_FFI_API + +int +ngx_http_lua_ffi_ssl_client_server_name(ngx_http_request_t *r, char **name, + size_t *namelen, char **err) +{ + ngx_ssl_conn_t *ssl_conn; +#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + const unsigned char *p; + size_t remaining, len; +#endif + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + +# ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB + remaining = 0; + + if (!SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_server_name, &p, + &remaining) || + remaining <= 2) { + *err = "SSL_AD_UNRECOGNIZED_NAME"; + return NGX_ERROR; + } + + len = (*(p++) << 8); + len += *(p++); + if (len + 2 != remaining) { + *err = "SSL_AD_UNRECOGNIZED_NAME"; + return NGX_ERROR; + } + remaining = len; + + if (remaining == 0 || *p++ != TLSEXT_NAMETYPE_host_name) { + *err = "SSL_AD_UNRECOGNIZED_NAME"; + return NGX_ERROR; + } + remaining--; + + if (remaining <= 2) { + *err = "SSL_AD_UNRECOGNIZED_NAME"; + return NGX_ERROR; + } + len = (*(p++) << 8); + len += *(p++); + if (len + 2 > remaining) { + *err = "SSL_AD_UNRECOGNIZED_NAME"; + return NGX_ERROR; + } + remaining = len; + + *name = (char *) p; + *namelen = len; + + return NGX_OK; + +# else + *err = "OpenSSL too old to"; + return NGX_ERROR; + +# endif + +#else + + *err = "no TLS extension support"; + return NGX_ERROR; + +#endif +} + + +int +ngx_http_lua_ffi_ssl_set_protocols(ngx_http_request_t *r, + int protocols, char **err) +{ + + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + +#if OPENSSL_VERSION_NUMBER >= 0x009080dfL + /* only in 0.9.8m+ */ + SSL_clear_options(ssl_conn, + SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1); +#endif + + if (!(protocols & NGX_SSL_SSLv2)) { + SSL_set_options(ssl_conn, SSL_OP_NO_SSLv2); + } + if (!(protocols & NGX_SSL_SSLv3)) { + SSL_set_options(ssl_conn, SSL_OP_NO_SSLv3); + } + if (!(protocols & NGX_SSL_TLSv1)) { + SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1); + } +#ifdef SSL_OP_NO_TLSv1_1 + SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_1); + if (!(protocols & NGX_SSL_TLSv1_1)) { + SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_1); + } +#endif +#ifdef SSL_OP_NO_TLSv1_2 + SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_2); + if (!(protocols & NGX_SSL_TLSv1_2)) { + SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_2); + } +#endif +#ifdef SSL_OP_NO_TLSv1_3 + SSL_clear_options(ssl_conn, SSL_OP_NO_TLSv1_3); + if (!(protocols & NGX_SSL_TLSv1_3)) { + SSL_set_options(ssl_conn, SSL_OP_NO_TLSv1_3); + } +#endif + + return NGX_OK; +} + + +int +ngx_http_lua_ffi_ssl_set_ciphers(ngx_http_request_t *r, + u_char *cdata, char **err) +{ + char *ciphers = NULL; + ngx_ssl_conn_t *ssl_conn; + + if (r->connection == NULL || r->connection->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = r->connection->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + ciphers = (char *)cdata; + if (ciphers == NULL) { + *err = "invalid ciphers failed"; + goto failed; + } + + if (SSL_set_cipher_list(ssl_conn, ciphers) == 0) { + *err = "SSL_set_cipher_list() failed"; + goto failed; + } + + return NGX_OK; + +failed: + + ERR_clear_error(); + + return NGX_ERROR; +} + + +#endif /* NGX_LUA_NO_FFI_API */ + + +#endif /* NGX_HTTP_SSL */ diff --git a/src/ngx_http_lua_ssl_client_helloby.h b/src/ngx_http_lua_ssl_client_helloby.h new file mode 100644 index 0000000000..2bb0d5d138 --- /dev/null +++ b/src/ngx_http_lua_ssl_client_helloby.h @@ -0,0 +1,32 @@ + +#ifndef _NGX_HTTP_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_ +#define _NGX_HTTP_LUA_SSL_CLIENT_HELLOBY_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +#if (NGX_HTTP_SSL) + +ngx_int_t ngx_http_lua_ssl_client_hello_handler_inline(ngx_http_request_t *r, + ngx_http_lua_srv_conf_t *lscf, lua_State *L); + +ngx_int_t ngx_http_lua_ssl_client_hello_handler_file(ngx_http_request_t *r, + ngx_http_lua_srv_conf_t *lscf, lua_State *L); + +char *ngx_http_lua_ssl_client_hello_by_lua_block(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +char *ngx_http_lua_ssl_client_hello_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +int ngx_http_lua_ssl_client_hello_handler(ngx_ssl_conn_t *ssl_conn, + int *al, void *arg); + + +#endif /* NGX_HTTP_SSL */ + + +#endif /* _NGX_HTTP_LUA_SSL_CERTBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h index f08f4815db..e37765c209 100644 --- a/src/ngx_http_lua_util.h +++ b/src/ngx_http_lua_util.h @@ -85,6 +85,8 @@ extern char ngx_http_lua_headers_metatable_key; : (c) == NGX_HTTP_LUA_CONTEXT_TIMER ? "ngx.timer" \ : (c) == NGX_HTTP_LUA_CONTEXT_INIT_WORKER ? "init_worker_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_BALANCER ? "balancer_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO ? \ + "ssl_client_hello_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CERT ? "ssl_certificate_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE ? \ "ssl_session_store_by_lua*" \ diff --git a/t/162-ssl-client-hello.t b/t/162-ssl-client-hello.t new file mode 100644 index 0000000000..e8b075870c --- /dev/null +++ b/t/162-ssl-client-hello.t @@ -0,0 +1,1826 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +repeat_each(3); + +# All these tests need to have new openssl +my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $openssl_version = eval { `$NginxBinary -V 2>&1` }; + +if ($openssl_version =~ m/built with OpenSSL 1\.1\.1/) { + plan tests => repeat_each() * (blocks() * 6 + 6); +} else { + plan(skip_all => "too old OpenSSL, need 1.1.1, was $1"); +} + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); +$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; + +#log_level 'warn'; +log_level 'debug'; + +no_long_string(); +#no_diff(); + +run_tests(); + +__DATA__ + +=== TEST 1: simple logging +--- http_config + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] +--- grep_error_log eval: qr/ssl_client_hello_by_lua:.*?,|\bssl client hello: connection reusable: \d+|\breusable connection: \d+/ +--- grep_error_log_out eval +qr/reusable connection: 1 +ssl client hello: connection reusable: 1 +reusable connection: 0 +ssl_client_hello_by_lua:1: ssl client hello by lua is running!, +/ + + + +=== TEST 2: sleep +--- http_config + ssl_client_hello_by_lua_block { + local begin = ngx.now() + ngx.sleep(0.1) + print("elapsed in ssl client hello by lua: ", ngx.now() - begin) + } + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log eval +[ +'lua ssl server name: "test.com"', +qr/elapsed in ssl client hello by lua: 0.(?:09|1[01])\d+,/, +] + +--- no_error_log +[error] +[alert] + + + +=== TEST 3: timer +--- http_config + ssl_client_hello_by_lua_block { + local function f() + print("my timer run!") + end + local ok, err = ngx.timer.at(0, f) + if not ok then + ngx.log(ngx.ERR, "failed to create timer: ", err) + return + end + } + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +my timer run! + +--- no_error_log +[error] +[alert] + + + +=== TEST 4: cosocket +--- http_config + ssl_client_hello_by_lua_block { + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT) + if not ok then + ngx.log(ngx.ERR, "failed to connect to memc: ", err) + return + end + + local bytes, err = sock:send("flush_all\r\n") + if not bytes then + ngx.log(ngx.ERR, "failed to send flush_all command: ", err) + return + end + + local res, err = sock:receive() + if not res then + ngx.log(ngx.ERR, "failed to receive memc reply: ", err) + return + end + + print("received memc reply: ", res) + } + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +received memc reply: OK + +--- no_error_log +[error] +[alert] + + + +=== TEST 5: ngx.exit(0) - no yield +--- http_config + ssl_client_hello_by_lua_block { + ngx.exit(0) + ngx.log(ngx.ERR, "should never reached here...") + } + server { + listen 127.0.0.2:8080 ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", 8080) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: boolean + +--- error_log +lua exit with code 0 + +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 6: ngx.exit(ngx.ERROR) - no yield +--- http_config + ssl_client_hello_by_lua_block { + ngx.exit(ngx.ERROR) + ngx.log(ngx.ERR, "should never reached here...") + } + server { + listen 127.0.0.2:8080 ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", 8080) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'lua_client_hello_by_lua: handler return value: -1, client_hello cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/, +'lua exit with code -1', +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 7: ngx.exit(0) - yield +--- http_config + ssl_client_hello_by_lua_block { + ngx.sleep(0.001) + ngx.exit(0) + + ngx.log(ngx.ERR, "should never reached here...") + } + server { + listen 127.0.0.2:8080 ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", 8080) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: boolean + +--- error_log +lua exit with code 0 + +--- no_error_log +should never reached here +[error] +[alert] +[emerg] + + + +=== TEST 8: ngx.exit(ngx.ERROR) - yield +--- http_config + ssl_client_hello_by_lua_block { + ngx.sleep(0.001) + ngx.exit(ngx.ERROR) + + ngx.log(ngx.ERR, "should never reached here...") + } + server { + listen 127.0.0.2:8080 ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", 8080) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'lua_client_hello_by_lua: client_hello cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/, +'lua exit with code -1', +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 9: lua exception - no yield +--- http_config + ssl_client_hello_by_lua_block { + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + server { + listen 127.0.0.2:8080 ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", 8080) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'runtime error: ssl_client_hello_by_lua:2: bad bad bad', +'lua_client_hello_by_lua: handler return value: 500, client_hello cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/, +qr/context: ssl_client_hello_by_lua\*, client: \d+\.\d+\.\d+\.\d+, server: \d+\.\d+\.\d+\.\d+:\d+/, +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 10: lua exception - yield +--- http_config + ssl_client_hello_by_lua_block { + ngx.sleep(0.001) + error("bad bad bad") + ngx.log(ngx.ERR, "should never reached here...") + } + server { + listen 127.0.0.2:8080 ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.2", 8080) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } + +--- request +GET /t +--- response_body +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'runtime error: ssl_client_hello_by_lua:3: bad bad bad', +'lua_client_hello_by_lua: client_hello cb exit code: 0', +qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/, +] + +--- no_error_log +should never reached here +[alert] +[emerg] + + + +=== TEST 11: get phase +--- http_config + ssl_client_hello_by_lua_block {print("get_phase: ", ngx.get_phase())} + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end + collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata + +--- error_log +lua ssl server name: "test.com" +get_phase: ssl_client_hello + +--- no_error_log +[error] +[alert] + + + +=== TEST 12: connection aborted prematurely +--- http_config + ssl_client_hello_by_lua_block { + ngx.sleep(0.3) + print("ssl-client-hello-by-lua: after sleeping") + } + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(150) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t + +--- response_body +connected: 1 +failed to do SSL handshake: timeout + +--- error_log +lua ssl server name: "test.com" +ssl-client-hello-by-lua: after sleeping + +--- no_error_log +[error] +[alert] +--- wait: 0.6 + + + +=== TEST 13: subrequests disabled +--- http_config + ssl_client_hello_by_lua_block {ngx.location.capture("/foo")} + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +failed to do SSL handshake: handshake failed + +--- error_log eval +[ +'lua ssl server name: "test.com"', +'ssl_client_hello_by_lua:1: API disabled in the context of ssl_client_hello_by_lua*', +qr/\[info\] .*?callback failed/, +] + +--- no_error_log +[alert] + + + +=== TEST 14: simple logging (by_lua_file) +--- http_config + ssl_client_hello_by_lua_file html/a.lua; + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block {ngx.status = 201 ngx.say("foo") ngx.exit(201)} + more_clear_headers Date; + } + } + +--- user_files +>>> a.lua +print("ssl client hello by lua is running!") + +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" +a.lua:1: ssl client hello by lua is running! + +--- no_error_log +[error] +[alert] + + + +=== TEST 15: coroutine API +--- http_config + ssl_client_hello_by_lua_block { + local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield + + local function f() + local cnt = 0 + for i = 1, 20 do + print("co yield: ", cnt) + cy() + cnt = cnt + 1 + end + end + + local c = cc(f) + for i = 1, 3 do + print("co resume, status: ", coroutine.status(c)) + cr(c) + end + } + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- grep_error_log eval: qr/co (?:yield: \d+|resume, status: \w+)/ +--- grep_error_log_out +co resume, status: suspended +co yield: 0 +co resume, status: suspended +co yield: 1 +co resume, status: suspended +co yield: 2 + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert] + + + +=== TEST 16: will crash when ssl_client_hello_by_lua* is allowed in server context +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + error_log syslog:server=127.0.0.1:12345 debug; + + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +close: 1 nil + +--- no_error_log +[error] +--- must_die +--- error_log eval +qr/\[emerg\] .*? "ssl_client_hello_by_lua_block" directive is not allowed here .*?\bnginx\.conf/ + + + +=== TEST 17: simple logging (syslog) +--- http_config + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + error_log syslog:server=127.0.0.1:12345 debug; + + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log eval +[ +qr/\[error\] .*? send\(\) failed/, +'lua ssl server name: "test.com"', +] +--- no_error_log +[alert] +ssl_client_hello_by_lua:1: ssl client hello by lua is running! + + + +=== TEST 18: check the count of running timers +--- http_config + ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") } + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /timers { + default_type 'text/plain'; + content_by_lua_block { + ngx.timer.at(0.1, function() ngx.sleep(0.3) end) + ngx.timer.at(0.11, function() ngx.sleep(0.3) end) + ngx.timer.at(0.09, function() ngx.sleep(0.3) end) + ngx.sleep(0.2) + ngx.say(ngx.timer.running_count()) + } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /timers HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 59 bytes. +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 2 +received: Connection: close +received: +received: 3 +close: 1 nil + +--- error_log eval +[ +'ssl_client_hello_by_lua:1: ssl client hello by lua is running!', +'lua ssl server name: "test.com"', +] +--- no_error_log +[error] +[alert] + + + +=== TEST 19: get raw_client_addr - IPv4 +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + ssl_client_hello_by_lua_block { + local ssl = require "ngx.ssl" + local byte = string.byte + local addr, addrtype, err = ssl.raw_client_addr() + local ip = string.format("%d.%d.%d.%d", byte(addr, 1), byte(addr, 2), + byte(addr, 3), byte(addr, 4)) + print("client ip: ", ip) + } + + server { + listen 127.0.0.1:12345 ssl; + server_name test.com; + + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("127.0.0.1", 12345) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +client ip: 127.0.0.1 + +--- no_error_log +[error] +[alert] + + + +=== TEST 20: get raw_client_addr - unix domain socket +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;;"; + ssl_client_hello_by_lua_block { + local ssl = require "ngx.ssl" + local addr, addrtyp, err = ssl.raw_client_addr() + print("client socket file: ", addr) + } + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", true) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to receive response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +client socket file: + +--- no_error_log +[error] +[alert] + + + +=== TEST 21: ssl_certificate_by_lua* can yield when reading early data +--- skip_openssl: 6: < 1.1.1 +--- http_config + ssl_client_hello_by_lua_block { + local begin = ngx.now() + ngx.sleep(0.1) + print("elapsed in ssl_client_hello_by_lua*: ", ngx.now() - begin) + } + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + ssl_early_data on; + server_tokens off; + } +--- config + server_tokens off; + lua_ssl_trusted_certificate ../../cert/test.crt; + lua_ssl_verify_depth 3; + + location /t { + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(false, nil, true, false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + end -- do + } + } +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: boolean +--- grep_error_log eval +qr/elapsed in ssl_client_hello_by_lua\*: 0\.(?:09|1[01])\d+,/, +--- grep_error_log_out eval +[ +qr/elapsed in ssl_client_hello_by_lua\*: 0\.(?:09|1[01])\d+,/, +qr/elapsed in ssl_client_hello_by_lua\*: 0\.(?:09|1[01])\d+,/, +qr/elapsed in ssl_client_hello_by_lua\*: 0\.(?:09|1[01])\d+,/, +] +--- no_error_log +[error] +[alert] +[emerg] From 2464e6ce1b478741e265d7d9695cb13c7c8c0554 Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Mon, 6 Apr 2020 15:21:49 +1000 Subject: [PATCH 09/16] skip merging of lua ssl directives on http only servers don't error with http level ssl directives --- src/ngx_http_lua_module.c | 74 +++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index 9a1f3415e6..49b73b0d13 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -1020,6 +1020,42 @@ ngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) dd("merge srv conf"); + if (conf->srv.ssl_cert_src.len == 0) { + conf->srv.ssl_cert_src = prev->srv.ssl_cert_src; + conf->srv.ssl_cert_src_key = prev->srv.ssl_cert_src_key; + conf->srv.ssl_cert_handler = prev->srv.ssl_cert_handler; + } + + if (conf->srv.ssl_cert_src.len) { + sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); + if (sscf == NULL || sscf->ssl.ctx == NULL) { + return NGX_CONF_OK; + } + +#ifdef LIBRESSL_VERSION_NUMBER + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "LibreSSL does not support ssl_certificate_by_lua*"); + return NGX_CONF_ERROR; + +#else + +# if OPENSSL_VERSION_NUMBER >= 0x1000205fL + + SSL_CTX_set_cert_cb(sscf->ssl.ctx, ngx_http_lua_ssl_cert_handler, NULL); + +# else + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "OpenSSL too old to support ssl_certificate_by_lua*"); + return NGX_CONF_ERROR; + +# endif + +#endif + } + + if (conf->srv.ssl_client_hello_src.len == 0) { conf->srv.ssl_client_hello_src = prev->srv.ssl_client_hello_src; conf->srv.ssl_client_hello_src_key = prev->srv.ssl_client_hello_src_key; @@ -1052,44 +1088,6 @@ ngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) # endif } -#endif - } - - if (conf->srv.ssl_cert_src.len == 0) { - conf->srv.ssl_cert_src = prev->srv.ssl_cert_src; - conf->srv.ssl_cert_src_key = prev->srv.ssl_cert_src_key; - conf->srv.ssl_cert_handler = prev->srv.ssl_cert_handler; - } - - if (conf->srv.ssl_cert_src.len) { - sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); - if (sscf == NULL || sscf->ssl.ctx == NULL) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no ssl configured for the server"); - - return NGX_CONF_ERROR; - } - -#ifdef LIBRESSL_VERSION_NUMBER - - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "LibreSSL does not support ssl_certificate_by_lua*"); - return NGX_CONF_ERROR; - -#else - -# if OPENSSL_VERSION_NUMBER >= 0x1000205fL - - SSL_CTX_set_cert_cb(sscf->ssl.ctx, ngx_http_lua_ssl_cert_handler, NULL); - -# else - - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "OpenSSL too old to support ssl_certificate_by_lua*"); - return NGX_CONF_ERROR; - -# endif - #endif } From f0a8bfcfa275050cb8f0001afbcaa0ca31a06ae4 Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Fri, 10 Apr 2020 20:27:51 +1000 Subject: [PATCH 10/16] feature/slow-read --- src/ngx_http_lua_req_body.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index e6bf3c192e..2a1f4cf778 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -64,6 +64,23 @@ ngx_http_lua_inject_req_body_api(lua_State *L) lua_setfield(L, -2, "finish_body"); } +unsigned int +ngx_http_lua_ffi_bytes_sent(ngx_http_request_t *r) +{ + return r->connection->sent; +} + +unsigned int +ngx_http_lua_ffi_bytes_buffered(ngx_http_request_t *r) +{ + unsigned total = 0; + ngx_chain_t* chain = r->out; + while(chain){ + total += ngx_buf_size(chain->buf); + chain = chain->next; + } + return total; +} static int ngx_http_lua_ngx_req_read_body(lua_State *L) From c5c206f915bbdb5df1bba968b943e22aa2bb9309 Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Sat, 11 Apr 2020 14:19:29 +1000 Subject: [PATCH 11/16] add nbusy --- src/ngx_http_lua_req_body.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index 2a1f4cf778..4c3b9e2c7d 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -70,10 +70,16 @@ ngx_http_lua_ffi_bytes_sent(ngx_http_request_t *r) return r->connection->sent; } +unsigned int +ngx_http_lua_ffi_con_nbusy(ngx_http_request_t *r) +{ + return r->connection->nbusy; +} + unsigned int ngx_http_lua_ffi_bytes_buffered(ngx_http_request_t *r) { - unsigned total = 0; + unsigned int total = 0; ngx_chain_t* chain = r->out; while(chain){ total += ngx_buf_size(chain->buf); From 49c23854381877d28d6f54abb7346ceb5440832d Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Sat, 11 Apr 2020 14:22:49 +1000 Subject: [PATCH 12/16] add ngx_http_lua_ffi_con_bytes_busy --- src/ngx_http_lua_req_body.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index 4c3b9e2c7d..9874183c5f 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -76,6 +76,18 @@ ngx_http_lua_ffi_con_nbusy(ngx_http_request_t *r) return r->connection->nbusy; } +unsigned int +ngx_http_lua_ffi_con_bytes_busy(ngx_http_request_t *r) +{ + unsigned int total = 0; + ngx_chain_t* chain = r->connection->busy; + while(chain){ + total += ngx_buf_size(chain->buf); + chain = chain->next; + } + return total; +} + unsigned int ngx_http_lua_ffi_bytes_buffered(ngx_http_request_t *r) { From 34ce3e990170121f0d2249eaf337834de99a89c3 Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Sat, 11 Apr 2020 15:00:32 +1000 Subject: [PATCH 13/16] use http_connection --- src/ngx_http_lua_req_body.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index 9874183c5f..3778d6af80 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -67,13 +67,13 @@ ngx_http_lua_inject_req_body_api(lua_State *L) unsigned int ngx_http_lua_ffi_bytes_sent(ngx_http_request_t *r) { - return r->connection->sent; + return r->http_connection->sent; } unsigned int ngx_http_lua_ffi_con_nbusy(ngx_http_request_t *r) { - return r->connection->nbusy; + return r->http_connection->nbusy; } unsigned int From 5ca2d2dc93253d5c6bab73a3dc7437045780d73f Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Sat, 11 Apr 2020 15:05:49 +1000 Subject: [PATCH 14/16] fix --- src/ngx_http_lua_req_body.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index 3778d6af80..8d5dd03289 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -67,7 +67,7 @@ ngx_http_lua_inject_req_body_api(lua_State *L) unsigned int ngx_http_lua_ffi_bytes_sent(ngx_http_request_t *r) { - return r->http_connection->sent; + return r->connection->sent; } unsigned int @@ -80,7 +80,7 @@ unsigned int ngx_http_lua_ffi_con_bytes_busy(ngx_http_request_t *r) { unsigned int total = 0; - ngx_chain_t* chain = r->connection->busy; + ngx_chain_t* chain = r->http_connection->busy; while(chain){ total += ngx_buf_size(chain->buf); chain = chain->next; From 19318ce50dd5694b16ab634c4047546c13be4ac3 Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Sat, 25 Apr 2020 08:11:11 +1000 Subject: [PATCH 15/16] cleanup --- src/ngx_http_lua_socket_tcp.c.orig | 6221 ---------------------------- 1 file changed, 6221 deletions(-) delete mode 100644 src/ngx_http_lua_socket_tcp.c.orig diff --git a/src/ngx_http_lua_socket_tcp.c.orig b/src/ngx_http_lua_socket_tcp.c.orig deleted file mode 100644 index 9e1752f01c..0000000000 --- a/src/ngx_http_lua_socket_tcp.c.orig +++ /dev/null @@ -1,6221 +0,0 @@ - -/* - * Copyright (C) Yichun Zhang (agentzh) - */ - - -#ifndef DDEBUG -#define DDEBUG 0 -#endif -#include "ddebug.h" - - -#include "ngx_http_lua_socket_tcp.h" -#include "ngx_http_lua_input_filters.h" -#include "ngx_http_lua_util.h" -#include "ngx_http_lua_uthread.h" -#include "ngx_http_lua_output.h" -#include "ngx_http_lua_contentby.h" -#include "ngx_http_lua_probe.h" - - -static int ngx_http_lua_socket_tcp(lua_State *L); -static int ngx_http_lua_socket_tcp_getfd(lua_State *L); -static int ngx_http_lua_socket_req_getfd(lua_State *L); -static int ngx_http_lua_socket_tcp_connect(lua_State *L); -#if (NGX_HTTP_SSL) -static int ngx_http_lua_socket_tcp_sslhandshake(lua_State *L); -#endif -static int ngx_http_lua_socket_tcp_receive(lua_State *L); -static int ngx_http_lua_socket_tcp_receiveany(lua_State *L); -static int ngx_http_lua_socket_tcp_send(lua_State *L); -static int ngx_http_lua_socket_tcp_close(lua_State *L); -static int ngx_http_lua_socket_tcp_setoption(lua_State *L); -static int ngx_http_lua_socket_tcp_settimeout(lua_State *L); -static int ngx_http_lua_socket_tcp_settimeouts(lua_State *L); -static void ngx_http_lua_socket_tcp_handler(ngx_event_t *ev); -static ngx_int_t ngx_http_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, - void *data); -static void ngx_http_lua_socket_init_peer_connection_addr_text( - ngx_peer_connection_t *pc); -static void ngx_http_lua_socket_read_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static void ngx_http_lua_socket_send_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static void ngx_http_lua_socket_connected_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static void ngx_http_lua_socket_tcp_cleanup(void *data); -static void ngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static void ngx_http_lua_socket_tcp_finalize_read_part(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static void ngx_http_lua_socket_tcp_finalize_write_part(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static ngx_int_t ngx_http_lua_socket_send(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static ngx_int_t ngx_http_lua_socket_test_connect(ngx_http_request_t *r, - ngx_connection_t *c); -static void ngx_http_lua_socket_handle_conn_error(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type); -static void ngx_http_lua_socket_handle_read_error(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type); -static void ngx_http_lua_socket_handle_write_error(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type); -static void ngx_http_lua_socket_handle_conn_success(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static void ngx_http_lua_socket_handle_read_success(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static void ngx_http_lua_socket_handle_write_success(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static int ngx_http_lua_socket_tcp_send_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); -static int ngx_http_lua_socket_tcp_conn_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); -static void ngx_http_lua_socket_dummy_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static int ngx_http_lua_socket_tcp_receive_helper(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); -static ngx_int_t ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static int ngx_http_lua_socket_tcp_receive_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); -static ngx_int_t ngx_http_lua_socket_read_line(void *data, ssize_t bytes); -static void ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx); -static int ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); -static int ngx_http_lua_socket_conn_error_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); -static int ngx_http_lua_socket_read_error_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); -static int ngx_http_lua_socket_write_error_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); -static ngx_int_t ngx_http_lua_socket_read_all(void *data, ssize_t bytes); -static ngx_int_t ngx_http_lua_socket_read_until(void *data, ssize_t bytes); -static ngx_int_t ngx_http_lua_socket_read_chunk(void *data, ssize_t bytes); -static ngx_int_t ngx_http_lua_socket_read_any(void *data, ssize_t bytes); -static int ngx_http_lua_socket_tcp_receiveuntil(lua_State *L); -static int ngx_http_lua_socket_receiveuntil_iterator(lua_State *L); -static ngx_int_t ngx_http_lua_socket_compile_pattern(u_char *data, size_t len, - ngx_http_lua_socket_compiled_pattern_t *cp, ngx_log_t *log); -static int ngx_http_lua_socket_cleanup_compiled_pattern(lua_State *L); -static int ngx_http_lua_req_socket(lua_State *L); -static void ngx_http_lua_req_socket_rev_handler(ngx_http_request_t *r); -static int ngx_http_lua_socket_tcp_getreusedtimes(lua_State *L); -static int ngx_http_lua_socket_tcp_setkeepalive(lua_State *L); -static void ngx_http_lua_socket_tcp_create_socket_pool(lua_State *L, - ngx_http_request_t *r, ngx_str_t key, ngx_int_t pool_size, - ngx_int_t backlog, ngx_http_lua_socket_pool_t **spool); -static ngx_int_t ngx_http_lua_get_keepalive_peer(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static void ngx_http_lua_socket_keepalive_dummy_handler(ngx_event_t *ev); -static int ngx_http_lua_socket_tcp_connect_helper(lua_State *L, - ngx_http_lua_socket_tcp_upstream_t *u, ngx_http_request_t *r, - ngx_http_lua_ctx_t *ctx, u_char *host_ref, size_t host_len, in_port_t port, - unsigned resuming); -static void ngx_http_lua_socket_tcp_conn_op_timeout_handler( - ngx_event_t *ev); -static int ngx_http_lua_socket_tcp_conn_op_timeout_retval_handler( - ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); -static void ngx_http_lua_socket_tcp_resume_conn_op( - ngx_http_lua_socket_pool_t *spool); -static void ngx_http_lua_socket_tcp_conn_op_ctx_cleanup(void *data); -static void ngx_http_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev); -static ngx_int_t ngx_http_lua_socket_keepalive_close_handler(ngx_event_t *ev); -static void ngx_http_lua_socket_keepalive_rev_handler(ngx_event_t *ev); -static int ngx_http_lua_socket_tcp_conn_op_resume_retval_handler( - ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); -static int ngx_http_lua_socket_tcp_upstream_destroy(lua_State *L); -static int ngx_http_lua_socket_downstream_destroy(lua_State *L); -static ngx_int_t ngx_http_lua_socket_push_input_data(ngx_http_request_t *r, - ngx_http_lua_ctx_t *ctx, ngx_http_lua_socket_tcp_upstream_t *u, - lua_State *L); -static ngx_int_t ngx_http_lua_socket_add_pending_data(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, u_char *pos, size_t len, u_char *pat, - int prefix, int old_state); -static ngx_int_t ngx_http_lua_socket_add_input_buffer(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); -static ngx_int_t ngx_http_lua_socket_insert_buffer(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, u_char *pat, size_t prefix); -static ngx_int_t ngx_http_lua_socket_tcp_conn_op_resume(ngx_http_request_t *r); -static ngx_int_t ngx_http_lua_socket_tcp_conn_resume(ngx_http_request_t *r); -static ngx_int_t ngx_http_lua_socket_tcp_read_resume(ngx_http_request_t *r); -static ngx_int_t ngx_http_lua_socket_tcp_write_resume(ngx_http_request_t *r); -static ngx_int_t ngx_http_lua_socket_tcp_resume_helper(ngx_http_request_t *r, - int socket_op); -static void ngx_http_lua_tcp_queue_conn_op_cleanup(void *data); -static void ngx_http_lua_tcp_resolve_cleanup(void *data); -static void ngx_http_lua_coctx_cleanup(void *data); -static void ngx_http_lua_socket_free_pool(ngx_log_t *log, - ngx_http_lua_socket_pool_t *spool); -static int ngx_http_lua_socket_shutdown_pool(lua_State *L); -static void ngx_http_lua_socket_shutdown_pool_helper( - ngx_http_lua_socket_pool_t *spool); -static void - ngx_http_lua_socket_empty_resolve_handler(ngx_resolver_ctx_t *ctx); -static int ngx_http_lua_socket_prepare_error_retvals(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L, ngx_uint_t ft_type); -#if (NGX_HTTP_SSL) -static int ngx_http_lua_ssl_handshake_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); -static void ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c); -static int ngx_http_lua_ssl_free_session(lua_State *L); -#endif -static void ngx_http_lua_socket_tcp_close_connection(ngx_connection_t *c); - - -enum { - SOCKET_CTX_INDEX = 1, - SOCKET_KEY_INDEX = 3, - SOCKET_CONNECT_TIMEOUT_INDEX = 2, - SOCKET_SEND_TIMEOUT_INDEX = 4, - SOCKET_READ_TIMEOUT_INDEX = 5, -}; - - -enum { - SOCKET_OP_CONNECT, - SOCKET_OP_READ, - SOCKET_OP_WRITE, - SOCKET_OP_RESUME_CONN -}; - - -#define ngx_http_lua_socket_check_busy_connecting(r, u, L) \ - if ((u)->conn_waiting) { \ - lua_pushnil(L); \ - lua_pushliteral(L, "socket busy connecting"); \ - return 2; \ - } - - -#define ngx_http_lua_socket_check_busy_reading(r, u, L) \ - if ((u)->read_waiting) { \ - lua_pushnil(L); \ - lua_pushliteral(L, "socket busy reading"); \ - return 2; \ - } - - -#define ngx_http_lua_socket_check_busy_writing(r, u, L) \ - if ((u)->write_waiting) { \ - lua_pushnil(L); \ - lua_pushliteral(L, "socket busy writing"); \ - return 2; \ - } \ - if ((u)->raw_downstream \ - && ((r)->connection->buffered & NGX_HTTP_LOWLEVEL_BUFFERED)) \ - { \ - lua_pushnil(L); \ - lua_pushliteral(L, "socket busy writing"); \ - return 2; \ - } - - -static char ngx_http_lua_req_socket_metatable_key; -static char ngx_http_lua_raw_req_socket_metatable_key; -static char ngx_http_lua_tcp_socket_metatable_key; -static char ngx_http_lua_upstream_udata_metatable_key; -static char ngx_http_lua_downstream_udata_metatable_key; -static char ngx_http_lua_pool_udata_metatable_key; -static char ngx_http_lua_pattern_udata_metatable_key; -#if (NGX_HTTP_SSL) -static char ngx_http_lua_ssl_session_metatable_key; -#endif - - -void -ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) -{ - ngx_int_t rc; - - lua_createtable(L, 0, 4 /* nrec */); /* ngx.socket */ - - lua_pushcfunction(L, ngx_http_lua_socket_tcp); - lua_pushvalue(L, -1); - lua_setfield(L, -3, "tcp"); - lua_setfield(L, -2, "stream"); - - { - const char buf[] = "local sock = ngx.socket.tcp()" - " local ok, err = sock:connect(...)" - " if ok then return sock else return nil, err end"; - - rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=ngx.socket.connect"); - } - - if (rc != NGX_OK) { - ngx_log_error(NGX_LOG_CRIT, log, 0, - "failed to load Lua code for ngx.socket.connect(): %i", - rc); - - } else { - lua_setfield(L, -2, "connect"); - } - - lua_setfield(L, -2, "socket"); - - /* {{{req socket object metatable */ - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - req_socket_metatable_key)); - lua_createtable(L, 0 /* narr */, 5 /* nrec */); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); - lua_setfield(L, -2, "receive"); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil); - lua_setfield(L, -2, "receiveuntil"); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeout); - lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeouts); - lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */ - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_getfd); - lua_setfield(L, -2, "getfd"); /* ngx socket mt */ - - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - - lua_rawset(L, LUA_REGISTRYINDEX); - /* }}} */ - - /* {{{raw req socket object metatable */ - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - raw_req_socket_metatable_key)); - lua_createtable(L, 0 /* narr */, 6 /* nrec */); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); - lua_setfield(L, -2, "receive"); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil); - lua_setfield(L, -2, "receiveuntil"); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_send); - lua_setfield(L, -2, "send"); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeout); - lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeouts); - lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */ - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_getfd); - lua_setfield(L, -2, "getfd"); /* ngx socket mt */ - - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - - lua_rawset(L, LUA_REGISTRYINDEX); - /* }}} */ - - /* {{{tcp object metatable */ - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - tcp_socket_metatable_key)); - lua_createtable(L, 0 /* narr */, 12 /* nrec */); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_connect); - lua_setfield(L, -2, "connect"); - -#if (NGX_HTTP_SSL) - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_sslhandshake); - lua_setfield(L, -2, "sslhandshake"); - -#endif - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); - lua_setfield(L, -2, "receive"); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveany); - lua_setfield(L, -2, "receiveany"); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil); - lua_setfield(L, -2, "receiveuntil"); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_send); - lua_setfield(L, -2, "send"); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_close); - lua_setfield(L, -2, "close"); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_setoption); - lua_setfield(L, -2, "setoption"); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeout); - lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeouts); - lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */ - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_getreusedtimes); - lua_setfield(L, -2, "getreusedtimes"); - - lua_pushcfunction(L, ngx_http_lua_socket_tcp_setkeepalive); - lua_setfield(L, -2, "setkeepalive"); - - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - lua_rawset(L, LUA_REGISTRYINDEX); - /* }}} */ - - /* {{{upstream userdata metatable */ - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - upstream_udata_metatable_key)); - lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ - lua_pushcfunction(L, ngx_http_lua_socket_tcp_upstream_destroy); - lua_setfield(L, -2, "__gc"); - lua_rawset(L, LUA_REGISTRYINDEX); - /* }}} */ - - /* {{{downstream userdata metatable */ - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - downstream_udata_metatable_key)); - lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ - lua_pushcfunction(L, ngx_http_lua_socket_downstream_destroy); - lua_setfield(L, -2, "__gc"); - lua_rawset(L, LUA_REGISTRYINDEX); - /* }}} */ - - /* {{{socket pool userdata metatable */ - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - pool_udata_metatable_key)); - lua_createtable(L, 0, 1); /* metatable */ - lua_pushcfunction(L, ngx_http_lua_socket_shutdown_pool); - lua_setfield(L, -2, "__gc"); - lua_rawset(L, LUA_REGISTRYINDEX); - /* }}} */ - - /* {{{socket compiled pattern userdata metatable */ - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - pattern_udata_metatable_key)); - lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ - lua_pushcfunction(L, ngx_http_lua_socket_cleanup_compiled_pattern); - lua_setfield(L, -2, "__gc"); - lua_rawset(L, LUA_REGISTRYINDEX); - /* }}} */ - -#if (NGX_HTTP_SSL) - - /* {{{ssl session userdata metatable */ - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - ssl_session_metatable_key)); - lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ - lua_pushcfunction(L, ngx_http_lua_ssl_free_session); - lua_setfield(L, -2, "__gc"); - lua_rawset(L, LUA_REGISTRYINDEX); - /* }}} */ - -#endif -} - - -void -ngx_http_lua_inject_req_socket_api(lua_State *L) -{ - lua_pushcfunction(L, ngx_http_lua_req_socket); - lua_setfield(L, -2, "socket"); - - lua_pushcfunction(L, ngx_http_lua_socket_req_getfd); - lua_setfield(L, -2, "getfd"); /* ngx socket mt */ -} - -static int -ngx_http_lua_socket_tcp_getfd(lua_State *L) -{ - ngx_connection_t *c; - ngx_http_lua_socket_tcp_upstream_t *u; - - luaL_checktype(L, 1, LUA_TTABLE); - - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); - lua_pop(L, 1); - - if (u == NULL || u->peer.connection == NULL) { - lua_pushnil(L); - lua_pushliteral(L, "closed"); - return 2; - } - - c = u->peer.connection; - lua_pushinteger(L,(int) c->fd); - - return 1; -} - -static int -ngx_http_lua_socket_tcp(lua_State *L) -{ - ngx_http_request_t *r; - ngx_http_lua_ctx_t *ctx; - - if (lua_gettop(L) != 0) { - return luaL_error(L, "expecting zero arguments, but got %d", - lua_gettop(L)); - } - - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - return luaL_error(L, "no ctx found"); - } - - ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE - | NGX_HTTP_LUA_CONTEXT_ACCESS - | NGX_HTTP_LUA_CONTEXT_CONTENT - | NGX_HTTP_LUA_CONTEXT_TIMER - | NGX_HTTP_LUA_CONTEXT_SSL_CERT - | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); - - lua_createtable(L, 5 /* narr */, 1 /* nrec */); - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - tcp_socket_metatable_key)); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_setmetatable(L, -2); - - dd("top: %d", lua_gettop(L)); - - return 1; -} - - -static void -ngx_http_lua_socket_tcp_create_socket_pool(lua_State *L, ngx_http_request_t *r, - ngx_str_t key, ngx_int_t pool_size, ngx_int_t backlog, - ngx_http_lua_socket_pool_t **spool) -{ - u_char *p; - size_t size, key_len; - ngx_int_t i; - ngx_http_lua_socket_pool_t *sp; - ngx_http_lua_socket_pool_item_t *items; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket connection pool size: %i, backlog: %i", - pool_size, backlog); - - key_len = ngx_align(key.len + 1, sizeof(void *)); - - size = sizeof(ngx_http_lua_socket_pool_t) - 1 + key_len - + sizeof(ngx_http_lua_socket_pool_item_t) * pool_size; - - /* before calling this function, the Lua stack is: - * -1 key - * -2 pools - */ - sp = lua_newuserdata(L, size); - if (sp == NULL) { - luaL_error(L, "no memory"); - return; - } - - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - pool_udata_metatable_key)); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_setmetatable(L, -2); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket keepalive create connection pool for key" - " \"%V\"", &key); - - /* a new socket pool with metatable is push to the stack, so now we have: - * -1 sp - * -2 key - * -3 pools - * - * it is time to set pools[key] to sp. - */ - lua_rawset(L, -3); - - /* clean up the stack for consistency's sake */ - lua_pop(L, 1); - - sp->backlog = backlog; - sp->size = pool_size; - sp->connections = 0; - sp->lua_vm = ngx_http_lua_get_lua_vm(r, NULL); - - ngx_queue_init(&sp->cache_connect_op); - ngx_queue_init(&sp->wait_connect_op); - ngx_queue_init(&sp->cache); - ngx_queue_init(&sp->free); - - p = ngx_copy(sp->key, key.data, key.len); - *p++ = '\0'; - - items = (ngx_http_lua_socket_pool_item_t *) (sp->key + key_len); - - dd("items: %p", items); - - ngx_http_lua_assert((void *) items == ngx_align_ptr(items, sizeof(void *))); - - for (i = 0; i < pool_size; i++) { - ngx_queue_insert_head(&sp->free, &items[i].queue); - items[i].socket_pool = sp; - } - - *spool = sp; -} - - -static int -ngx_http_lua_socket_tcp_connect_helper(lua_State *L, - ngx_http_lua_socket_tcp_upstream_t *u, ngx_http_request_t *r, - ngx_http_lua_ctx_t *ctx, u_char *host_ref, size_t host_len, in_port_t port, - unsigned resuming) -{ - int n; - int host_size; - int saved_top; - ngx_int_t rc; - ngx_str_t host; - ngx_str_t *conn_op_host; - ngx_url_t url; - ngx_queue_t *q; - ngx_resolver_ctx_t *rctx, temp; - ngx_http_lua_co_ctx_t *coctx; - ngx_http_core_loc_conf_t *clcf; - ngx_http_lua_socket_pool_t *spool; - ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; - - spool = u->socket_pool; - if (spool != NULL) { - rc = ngx_http_lua_get_keepalive_peer(r, u); - - if (rc == NGX_OK) { - lua_pushinteger(L, 1); - return 1; - } - - /* rc == NGX_DECLINED */ - - spool->connections++; - - /* check if backlog is enabled and - * don't queue resuming connection operation */ - if (spool->backlog >= 0 && !resuming) { - - dd("lua tcp socket %s connections %ld", - spool->key, spool->connections); - - if (spool->connections > spool->size + spool->backlog) { - spool->connections--; - lua_pushnil(L); - lua_pushliteral(L, "too many waiting connect operations"); - return 2; - } - - if (spool->connections > spool->size) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, u->peer.log, 0, - "lua tcp socket queue connect operation for " - "connection pool \"%s\", connections: %i", - spool->key, spool->connections); - - host_size = sizeof(u_char) * - (ngx_max(host_len, NGX_INET_ADDRSTRLEN) + 1); - - if (!ngx_queue_empty(&spool->cache_connect_op)) { - q = ngx_queue_last(&spool->cache_connect_op); - ngx_queue_remove(q); - conn_op_ctx = ngx_queue_data( - q, ngx_http_lua_socket_tcp_conn_op_ctx_t, queue); - - conn_op_host = &conn_op_ctx->host; - if (host_len > conn_op_host->len - && host_len > NGX_INET_ADDRSTRLEN) - { - ngx_free(conn_op_host->data); - conn_op_host->data = ngx_alloc(host_size, - ngx_cycle->log); - if (conn_op_host->data == NULL) { - ngx_free(conn_op_ctx); - goto no_memory_and_not_resuming; - } - } - - } else { - conn_op_ctx = ngx_alloc( - sizeof(ngx_http_lua_socket_tcp_conn_op_ctx_t), - ngx_cycle->log); - if (conn_op_ctx == NULL) { - goto no_memory_and_not_resuming; - } - - conn_op_host = &conn_op_ctx->host; - conn_op_host->data = ngx_alloc(host_size, ngx_cycle->log); - if (conn_op_host->data == NULL) { - ngx_free(conn_op_ctx); - goto no_memory_and_not_resuming; - } - } - - conn_op_ctx->cleanup = NULL; - - ngx_memcpy(conn_op_host->data, host_ref, host_len); - conn_op_host->data[host_len] = '\0'; - conn_op_host->len = host_len; - - conn_op_ctx->port = port; - - u->write_co_ctx = ctx->cur_co_ctx; - - conn_op_ctx->u = u; - ctx->cur_co_ctx->cleanup = - ngx_http_lua_tcp_queue_conn_op_cleanup; - ctx->cur_co_ctx->data = conn_op_ctx; - - ngx_memzero(&conn_op_ctx->event, sizeof(ngx_event_t)); - conn_op_ctx->event.handler = - ngx_http_lua_socket_tcp_conn_op_timeout_handler; - conn_op_ctx->event.data = conn_op_ctx; - conn_op_ctx->event.log = ngx_cycle->log; - - ngx_add_timer(&conn_op_ctx->event, u->connect_timeout); - - ngx_queue_insert_tail(&spool->wait_connect_op, - &conn_op_ctx->queue); - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, - "lua tcp socket queued connect operation for " - "%d(ms), u: %p, ctx: %p", - u->connect_timeout, conn_op_ctx->u, conn_op_ctx); - - return lua_yield(L, 0); - } - } - - } /* end spool != NULL */ - - host.data = ngx_palloc(r->pool, host_len + 1); - if (host.data == NULL) { - return luaL_error(L, "no memory"); - } - - host.len = host_len; - - ngx_memcpy(host.data, host_ref, host_len); - host.data[host_len] = '\0'; - - ngx_memzero(&url, sizeof(ngx_url_t)); - url.url = host; - url.default_port = port; - url.no_resolve = 1; - - coctx = ctx->cur_co_ctx; - - if (ngx_parse_url(r->pool, &url) != NGX_OK) { - lua_pushnil(L); - - if (url.err) { - lua_pushfstring(L, "failed to parse host name \"%s\": %s", - url.url.data, url.err); - - } else { - lua_pushfstring(L, "failed to parse host name \"%s\"", - url.url.data); - } - - goto failed; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket connect timeout: %M", u->connect_timeout); - - u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); - if (u->resolved == NULL) { - if (resuming) { - lua_pushnil(L); - lua_pushliteral(L, "no memory"); - goto failed; - } - - goto no_memory_and_not_resuming; - } - - if (url.addrs && url.addrs[0].sockaddr) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket network address given directly"); - - u->resolved->sockaddr = url.addrs[0].sockaddr; - u->resolved->socklen = url.addrs[0].socklen; - u->resolved->naddrs = 1; - u->resolved->host = url.addrs[0].name; - - } else { - u->resolved->host = host; - u->resolved->port = url.default_port; - } - - if (u->resolved->sockaddr) { - rc = ngx_http_lua_socket_resolve_retval_handler(r, u, L); - if (rc == NGX_AGAIN && !resuming) { - return lua_yield(L, 0); - } - - if (rc > 1) { - goto failed; - } - - return rc; - } - - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - temp.name = host; - rctx = ngx_resolve_start(clcf->resolver, &temp); - if (rctx == NULL) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; - lua_pushnil(L); - lua_pushliteral(L, "failed to start the resolver"); - goto failed; - } - - if (rctx == NGX_NO_RESOLVER) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; - lua_pushnil(L); - lua_pushfstring(L, "no resolver defined to resolve \"%s\"", host.data); - goto failed; - } - - rctx->name = host; -#if !defined(nginx_version) || nginx_version < 1005008 - rctx->type = NGX_RESOLVE_A; -#endif - rctx->handler = ngx_http_lua_socket_resolve_handler; - rctx->data = u; - rctx->timeout = clcf->resolver_timeout; - - u->resolved->ctx = rctx; - u->write_co_ctx = ctx->cur_co_ctx; - - ngx_http_lua_cleanup_pending_operation(coctx); - coctx->cleanup = ngx_http_lua_tcp_resolve_cleanup; - coctx->data = u; - - saved_top = lua_gettop(L); - - if (ngx_resolve_name(rctx) != NGX_OK) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket fail to run resolver immediately"); - - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; - - coctx->cleanup = NULL; - coctx->data = NULL; - - u->resolved->ctx = NULL; - lua_pushnil(L); - lua_pushfstring(L, "%s could not be resolved", host.data); - goto failed; - } - - if (u->conn_waiting) { - dd("resolved and already connecting"); - - if (resuming) { - return NGX_AGAIN; - } - - return lua_yield(L, 0); - } - - n = lua_gettop(L) - saved_top; - if (n) { - dd("errors occurred during resolving or connecting" - "or already connected"); - - if (n > 1) { - goto failed; - } - - return n; - } - - /* still resolving */ - - u->conn_waiting = 1; - u->write_prepare_retvals = ngx_http_lua_socket_resolve_retval_handler; - - dd("setting data to %p", u); - - if (ctx->entered_content_phase) { - r->write_event_handler = ngx_http_lua_content_wev_handler; - - } else { - r->write_event_handler = ngx_http_core_run_phases; - } - - if (resuming) { - return NGX_AGAIN; - } - - return lua_yield(L, 0); - -failed: - - if (spool != NULL) { - spool->connections--; - ngx_http_lua_socket_tcp_resume_conn_op(spool); - } - - return 2; - -no_memory_and_not_resuming: - - if (spool != NULL) { - spool->connections--; - ngx_http_lua_socket_tcp_resume_conn_op(spool); - } - - return luaL_error(L, "no memory"); -} - - -static int -ngx_http_lua_socket_tcp_connect(lua_State *L) -{ - ngx_http_request_t *r; - ngx_http_lua_ctx_t *ctx; - int port; - int n; - u_char *p; - size_t len; - ngx_http_lua_loc_conf_t *llcf; - ngx_peer_connection_t *pc; - int connect_timeout, send_timeout, read_timeout; - unsigned custom_pool; - int key_index; - ngx_int_t backlog; - ngx_int_t pool_size; - ngx_str_t key; - const char *msg; - - ngx_http_lua_socket_tcp_upstream_t *u; - - ngx_http_lua_socket_pool_t *spool; - - n = lua_gettop(L); - if (n != 2 && n != 3 && n != 4) { - return luaL_error(L, "ngx.socket connect: expecting 2, 3, or 4 " - "arguments (including the object), but seen %d", n); - } - - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - return luaL_error(L, "no ctx found"); - } - - ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE - | NGX_HTTP_LUA_CONTEXT_ACCESS - | NGX_HTTP_LUA_CONTEXT_CONTENT - | NGX_HTTP_LUA_CONTEXT_TIMER - | NGX_HTTP_LUA_CONTEXT_SSL_CERT - | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); - - luaL_checktype(L, 1, LUA_TTABLE); - - p = (u_char *) luaL_checklstring(L, 2, &len); - - backlog = -1; - key_index = 2; - pool_size = 0; - custom_pool = 0; - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (lua_type(L, n) == LUA_TTABLE) { - - /* found the last optional option table */ - - lua_getfield(L, n, "pool_size"); - - if (lua_isnumber(L, -1)) { - pool_size = (ngx_int_t) lua_tointeger(L, -1); - - if (pool_size <= 0) { - msg = lua_pushfstring(L, "bad \"pool_size\" option value: %i", - pool_size); - return luaL_argerror(L, n, msg); - } - - } else if (!lua_isnil(L, -1)) { - msg = lua_pushfstring(L, "bad \"pool_size\" option type: %s", - lua_typename(L, lua_type(L, -1))); - return luaL_argerror(L, n, msg); - } - - lua_pop(L, 1); - - lua_getfield(L, n, "backlog"); - - if (lua_isnumber(L, -1)) { - backlog = (ngx_int_t) lua_tointeger(L, -1); - - if (backlog < 0) { - msg = lua_pushfstring(L, "bad \"backlog\" option value: %i", - backlog); - return luaL_argerror(L, n, msg); - } - - /* use default value for pool size if only backlog specified */ - if (pool_size == 0) { - pool_size = llcf->pool_size; - } - } - - lua_pop(L, 1); - - lua_getfield(L, n, "pool"); - - switch (lua_type(L, -1)) { - case LUA_TNUMBER: - lua_tostring(L, -1); - /* FALLTHROUGH */ - - case LUA_TSTRING: - custom_pool = 1; - - lua_pushvalue(L, -1); - lua_rawseti(L, 1, SOCKET_KEY_INDEX); - - key_index = n + 1; - - break; - - case LUA_TNIL: - lua_pop(L, 2); - break; - - default: - msg = lua_pushfstring(L, "bad \"pool\" option type: %s", - luaL_typename(L, -1)); - luaL_argerror(L, n, msg); - break; - } - - n--; - } - - /* the fourth argument is not a table */ - if (n == 4) { - lua_pop(L, 1); - n--; - } - - if (n == 3) { - port = luaL_checkinteger(L, 3); - - if (port < 0 || port > 65535) { - lua_pushnil(L); - lua_pushfstring(L, "bad port number: %d", port); - return 2; - } - - if (!custom_pool) { - lua_pushliteral(L, ":"); - lua_insert(L, 3); - lua_concat(L, 3); - } - - dd("socket key: %s", lua_tostring(L, -1)); - - } else { /* n == 2 */ - port = 0; - } - - if (!custom_pool) { - /* the key's index is 2 */ - - lua_pushvalue(L, 2); - lua_rawseti(L, 1, SOCKET_KEY_INDEX); - } - - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); - lua_pop(L, 1); - - if (u) { - if (u->request && u->request != r) { - return luaL_error(L, "bad request"); - } - - ngx_http_lua_socket_check_busy_connecting(r, u, L); - ngx_http_lua_socket_check_busy_reading(r, u, L); - ngx_http_lua_socket_check_busy_writing(r, u, L); - - if (u->body_downstream || u->raw_downstream) { - return luaL_error(L, "attempt to re-connect a request socket"); - } - - if (u->peer.connection) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket reconnect without shutting down"); - - ngx_http_lua_socket_tcp_finalize(r, u); - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua reuse socket upstream ctx"); - - } else { - u = lua_newuserdata(L, sizeof(ngx_http_lua_socket_tcp_upstream_t)); - if (u == NULL) { - return luaL_error(L, "no memory"); - } - -#if 1 - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - upstream_udata_metatable_key)); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_setmetatable(L, -2); -#endif - - lua_rawseti(L, 1, SOCKET_CTX_INDEX); - } - - ngx_memzero(u, sizeof(ngx_http_lua_socket_tcp_upstream_t)); - - u->request = r; /* set the controlling request */ - - u->conf = llcf; - - pc = &u->peer; - - pc->log = r->connection->log; - pc->log_error = NGX_ERROR_ERR; - - dd("lua peer connection log: %p", pc->log); - - lua_rawgeti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX); - lua_rawgeti(L, 1, SOCKET_SEND_TIMEOUT_INDEX); - lua_rawgeti(L, 1, SOCKET_READ_TIMEOUT_INDEX); - - read_timeout = (ngx_int_t) lua_tointeger(L, -1); - send_timeout = (ngx_int_t) lua_tointeger(L, -2); - connect_timeout = (ngx_int_t) lua_tointeger(L, -3); - - lua_pop(L, 3); - - if (connect_timeout > 0) { - u->connect_timeout = (ngx_msec_t) connect_timeout; - - } else { - u->connect_timeout = u->conf->connect_timeout; - } - - if (send_timeout > 0) { - u->send_timeout = (ngx_msec_t) send_timeout; - - } else { - u->send_timeout = u->conf->send_timeout; - } - - if (read_timeout > 0) { - u->read_timeout = (ngx_msec_t) read_timeout; - - } else { - u->read_timeout = u->conf->read_timeout; - } - - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(socket_pool_key)); - lua_rawget(L, LUA_REGISTRYINDEX); /* table */ - lua_pushvalue(L, key_index); /* key */ - - lua_rawget(L, -2); - spool = lua_touserdata(L, -1); - lua_pop(L, 1); - - if (spool != NULL) { - u->socket_pool = spool; - - } else if (pool_size > 0) { - lua_pushvalue(L, key_index); - key.data = (u_char *) lua_tolstring(L, -1, &key.len); - - ngx_http_lua_socket_tcp_create_socket_pool(L, r, key, pool_size, - backlog, &spool); - u->socket_pool = spool; - } - - return ngx_http_lua_socket_tcp_connect_helper(L, u, r, ctx, p, - len, port, 0); -} - - -static void -ngx_http_lua_socket_empty_resolve_handler(ngx_resolver_ctx_t *ctx) -{ - /* do nothing */ -} - - -static void -ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) -{ - ngx_http_request_t *r; - ngx_connection_t *c; - ngx_http_upstream_resolved_t *ur; - ngx_http_lua_ctx_t *lctx; - lua_State *L; - ngx_http_lua_socket_tcp_upstream_t *u; - u_char *p; - size_t len; -#if defined(nginx_version) && nginx_version >= 1005008 - socklen_t socklen; - struct sockaddr *sockaddr; -#else - struct sockaddr_in *sin; -#endif - ngx_uint_t i; - unsigned waiting; - - u = ctx->data; - r = u->request; - c = r->connection; - ur = u->resolved; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua tcp socket resolve handler"); - - lctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (lctx == NULL) { - return; - } - - lctx->cur_co_ctx = u->write_co_ctx; - - u->write_co_ctx->cleanup = NULL; - - L = lctx->cur_co_ctx->co; - - waiting = u->conn_waiting; - - if (ctx->state) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua tcp socket resolver error: %s " - "(connect waiting: %d)", - ngx_resolver_strerror(ctx->state), (int) waiting); - - lua_pushnil(L); - lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len); - lua_pushfstring(L, " could not be resolved (%d: %s)", - (int) ctx->state, - ngx_resolver_strerror(ctx->state)); - lua_concat(L, 2); - - u->write_prepare_retvals = - ngx_http_lua_socket_conn_error_retval_handler; - ngx_http_lua_socket_handle_conn_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_RESOLVER); - - if (waiting) { - ngx_http_run_posted_requests(c); - } - - return; - } - - ur->naddrs = ctx->naddrs; - ur->addrs = ctx->addrs; - -#if (NGX_DEBUG) - { -# if defined(nginx_version) && nginx_version >= 1005008 - u_char text[NGX_SOCKADDR_STRLEN]; - ngx_str_t addr; -# else - in_addr_t addr; -# endif - ngx_uint_t i; - -# if defined(nginx_version) && nginx_version >= 1005008 - addr.data = text; - - for (i = 0; i < ctx->naddrs; i++) { - addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, - text, NGX_SOCKADDR_STRLEN, 0); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "name was resolved to %V", &addr); - } -# else - for (i = 0; i < ctx->naddrs; i++) { - dd("addr i: %d %p", (int) i, &ctx->addrs[i]); - - addr = ntohl(ctx->addrs[i]); - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, - "name was resolved to %ud.%ud.%ud.%ud", - (addr >> 24) & 0xff, (addr >> 16) & 0xff, - (addr >> 8) & 0xff, addr & 0xff); - } -# endif - } -#endif - - ngx_http_lua_assert(ur->naddrs > 0); - - if (ur->naddrs == 1) { - i = 0; - - } else { - i = ngx_random() % ur->naddrs; - } - - dd("selected addr index: %d", (int) i); - -#if defined(nginx_version) && nginx_version >= 1005008 - socklen = ur->addrs[i].socklen; - - sockaddr = ngx_palloc(r->pool, socklen); - if (sockaddr == NULL) { - goto nomem; - } - - ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen); - - switch (sockaddr->sa_family) { -#if (NGX_HAVE_INET6) - case AF_INET6: - ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port); - break; -#endif - default: /* AF_INET */ - ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port); - } - - p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN); - if (p == NULL) { - goto nomem; - } - - len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); - ur->sockaddr = sockaddr; - ur->socklen = socklen; - -#else - /* for nginx older than 1.5.8 */ - - len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; - - p = ngx_pnalloc(r->pool, len + sizeof(struct sockaddr_in)); - if (p == NULL) { - goto nomem; - } - - sin = (struct sockaddr_in *) &p[len]; - ngx_memzero(sin, sizeof(struct sockaddr_in)); - - len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN); - len = ngx_sprintf(&p[len], ":%d", ur->port) - p; - - sin->sin_family = AF_INET; - sin->sin_port = htons(ur->port); - sin->sin_addr.s_addr = ur->addrs[i]; - - ur->sockaddr = (struct sockaddr *) sin; - ur->socklen = sizeof(struct sockaddr_in); -#endif - - ur->host.data = p; - ur->host.len = len; - ur->naddrs = 1; - - ngx_resolve_name_done(ctx); - ur->ctx = NULL; - - u->conn_waiting = 0; - u->write_co_ctx = NULL; - - if (waiting) { - lctx->resume_handler = ngx_http_lua_socket_tcp_conn_resume; - r->write_event_handler(r); - ngx_http_run_posted_requests(c); - - } else { - (void) ngx_http_lua_socket_resolve_retval_handler(r, u, L); - } - - return; - -nomem: - - if (ur->ctx) { - ngx_resolve_name_done(ctx); - ur->ctx = NULL; - } - - u->write_prepare_retvals = ngx_http_lua_socket_conn_error_retval_handler; - ngx_http_lua_socket_handle_conn_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_NOMEM); - - if (waiting) { - dd("run posted requests"); - ngx_http_run_posted_requests(c); - - } else { - lua_pushnil(L); - lua_pushliteral(L, "no memory"); - } -} - - -static void -ngx_http_lua_socket_init_peer_connection_addr_text(ngx_peer_connection_t *pc) -{ - ngx_connection_t *c; - size_t addr_text_max_len; - - c = pc->connection; - - switch (pc->sockaddr->sa_family) { - -#if (NGX_HAVE_INET6) - case AF_INET6: - addr_text_max_len = NGX_INET6_ADDRSTRLEN; - break; -#endif - -#if (NGX_HAVE_UNIX_DOMAIN) - case AF_UNIX: - addr_text_max_len = NGX_UNIX_ADDRSTRLEN; - break; -#endif - - case AF_INET: - addr_text_max_len = NGX_INET_ADDRSTRLEN; - break; - - default: - addr_text_max_len = NGX_SOCKADDR_STRLEN; - break; - } - - c->addr_text.data = ngx_pnalloc(c->pool, addr_text_max_len); - if (c->addr_text.data == NULL) { - ngx_log_error(NGX_LOG_ERR, pc->log, 0, - "init peer connection addr_text failed: no memory"); - return; - } - - c->addr_text.len = ngx_sock_ntop(pc->sockaddr, pc->socklen, - c->addr_text.data, - addr_text_max_len, 0); -} - - -static int -ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) -{ - ngx_http_lua_ctx_t *ctx; - ngx_peer_connection_t *pc; - ngx_connection_t *c; - ngx_http_cleanup_t *cln; - ngx_http_upstream_resolved_t *ur; - ngx_int_t rc; - ngx_http_lua_co_ctx_t *coctx; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket resolve retval handler"); - - if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_RESOLVER) { - return 2; - } - - pc = &u->peer; - - ur = u->resolved; - - if (ur->sockaddr) { - pc->sockaddr = ur->sockaddr; - pc->socklen = ur->socklen; - pc->name = &ur->host; - - } else { - lua_pushnil(L); - lua_pushliteral(L, "resolver not working"); - return 2; - } - - pc->get = ngx_http_lua_socket_tcp_get_peer; - - rc = ngx_event_connect_peer(pc); - - if (rc == NGX_ERROR) { - u->socket_errno = ngx_socket_errno; - } - - if (u->cleanup == NULL) { - cln = ngx_http_lua_cleanup_add(r, 0); - if (cln == NULL) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; - lua_pushnil(L); - lua_pushliteral(L, "no memory"); - return 2; - } - - cln->handler = ngx_http_lua_socket_tcp_cleanup; - cln->data = u; - u->cleanup = &cln->handler; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket connect: %i", rc); - - if (rc == NGX_ERROR) { - return ngx_http_lua_socket_conn_error_retval_handler(r, u, L); - } - - if (rc == NGX_BUSY) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; - lua_pushnil(L); - lua_pushliteral(L, "no live connection"); - return 2; - } - - if (rc == NGX_DECLINED) { - dd("socket errno: %d", (int) ngx_socket_errno); - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; - u->socket_errno = ngx_socket_errno; - return ngx_http_lua_socket_conn_error_retval_handler(r, u, L); - } - - /* rc == NGX_OK || rc == NGX_AGAIN */ - - c = pc->connection; - - c->data = u; - - c->write->handler = ngx_http_lua_socket_tcp_handler; - c->read->handler = ngx_http_lua_socket_tcp_handler; - - u->write_event_handler = ngx_http_lua_socket_connected_handler; - u->read_event_handler = ngx_http_lua_socket_connected_handler; - - c->sendfile &= r->connection->sendfile; - - if (c->pool == NULL) { - - /* we need separate pool here to be able to cache SSL connections */ - - c->pool = ngx_create_pool(128, r->connection->log); - if (c->pool == NULL) { - return ngx_http_lua_socket_prepare_error_retvals(r, u, L, - NGX_HTTP_LUA_SOCKET_FT_NOMEM); - } - } - - c->log = r->connection->log; - c->pool->log = c->log; - c->read->log = c->log; - c->write->log = c->log; - - /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */ - -#if 0 - u->writer.out = NULL; - u->writer.last = &u->writer.out; -#endif - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - coctx = ctx->cur_co_ctx; - - dd("setting data to %p", u); - - if (rc == NGX_OK) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket connected: fd:%d", (int) c->fd); - - /* We should delete the current write/read event - * here because the socket object may not be used immediately - * on the Lua land, thus causing hot spin around level triggered - * event poll and wasting CPU cycles. */ - - if (ngx_handle_write_event(c->write, 0) != NGX_OK) { - ngx_http_lua_socket_handle_conn_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - lua_pushnil(L); - lua_pushliteral(L, "failed to handle write event"); - return 2; - } - - if (ngx_handle_read_event(c->read, 0) != NGX_OK) { - ngx_http_lua_socket_handle_conn_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - lua_pushnil(L); - lua_pushliteral(L, "failed to handle read event"); - return 2; - } - - u->read_event_handler = ngx_http_lua_socket_dummy_handler; - u->write_event_handler = ngx_http_lua_socket_dummy_handler; - - lua_pushinteger(L, 1); - return 1; - } - - /* rc == NGX_AGAIN */ - - ngx_http_lua_cleanup_pending_operation(coctx); - coctx->cleanup = ngx_http_lua_coctx_cleanup; - coctx->data = u; - - ngx_add_timer(c->write, u->connect_timeout); - - u->write_co_ctx = ctx->cur_co_ctx; - u->conn_waiting = 1; - u->write_prepare_retvals = ngx_http_lua_socket_tcp_conn_retval_handler; - - dd("setting data to %p", u); - - if (ctx->entered_content_phase) { - r->write_event_handler = ngx_http_lua_content_wev_handler; - - } else { - r->write_event_handler = ngx_http_core_run_phases; - } - - return NGX_AGAIN; -} - - -static int -ngx_http_lua_socket_conn_error_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) -{ - ngx_uint_t ft_type; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket error retval handler"); - - if (u->write_co_ctx) { - u->write_co_ctx->cleanup = NULL; - } - - ngx_http_lua_socket_tcp_finalize(r, u); - - ft_type = u->ft_type; - u->ft_type = 0; - return ngx_http_lua_socket_prepare_error_retvals(r, u, L, ft_type); -} - - -#if (NGX_HTTP_SSL) - -static int -ngx_http_lua_socket_tcp_sslhandshake(lua_State *L) -{ - int n, top; - ngx_int_t rc; - ngx_str_t name = ngx_null_string; - ngx_connection_t *c; - ngx_ssl_session_t **psession; - ngx_http_request_t *r; - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_co_ctx_t *coctx; - - ngx_http_lua_socket_tcp_upstream_t *u; - - /* Lua function arguments: self [,session] [,host] [,verify] - [,send_status_req] */ - - n = lua_gettop(L); - if (n < 1 || n > 5) { - return luaL_error(L, "ngx.socket sslhandshake: expecting 1 ~ 5 " - "arguments (including the object), but seen %d", n); - } - - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket ssl handshake"); - - luaL_checktype(L, 1, LUA_TTABLE); - - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); - - if (u == NULL - || u->peer.connection == NULL - || u->read_closed - || u->write_closed) - { - lua_pushnil(L); - lua_pushliteral(L, "closed"); - return 2; - } - - if (u->request != r) { - return luaL_error(L, "bad request"); - } - - ngx_http_lua_socket_check_busy_connecting(r, u, L); - ngx_http_lua_socket_check_busy_reading(r, u, L); - ngx_http_lua_socket_check_busy_writing(r, u, L); - - if (u->raw_downstream || u->body_downstream) { - lua_pushnil(L); - lua_pushliteral(L, "not supported for downstream"); - return 2; - } - - c = u->peer.connection; - - u->ssl_session_reuse = 1; - - if (c->ssl && c->ssl->handshaked) { - switch (lua_type(L, 2)) { - case LUA_TUSERDATA: - lua_pushvalue(L, 2); - break; - - case LUA_TBOOLEAN: - if (!lua_toboolean(L, 2)) { - /* avoid generating the ssl session */ - lua_pushboolean(L, 1); - break; - } - /* fall through */ - - default: - ngx_http_lua_ssl_handshake_retval_handler(r, u, L); - break; - } - - return 1; - } - - if (ngx_ssl_create_connection(u->conf->ssl, c, - NGX_SSL_BUFFER|NGX_SSL_CLIENT) - != NGX_OK) - { - lua_pushnil(L); - lua_pushliteral(L, "failed to create ssl connection"); - return 2; - } - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - return luaL_error(L, "no ctx found"); - } - - coctx = ctx->cur_co_ctx; - - c->sendfile = 0; - - if (n >= 2) { - if (lua_type(L, 2) == LUA_TBOOLEAN) { - u->ssl_session_reuse = lua_toboolean(L, 2); - - } else { - psession = lua_touserdata(L, 2); - - if (psession != NULL && *psession != NULL) { - if (ngx_ssl_set_session(c, *psession) != NGX_OK) { - lua_pushnil(L); - lua_pushliteral(L, "lua ssl set session failed"); - return 2; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua ssl set session: %p", *psession); - } - } - - if (n >= 3) { - name.data = (u_char *) lua_tolstring(L, 3, &name.len); - - if (name.data) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua ssl server name: \"%*s\"", name.len, - name.data); - -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - - if (SSL_set_tlsext_host_name(c->ssl->connection, - (char *) name.data) - == 0) - { - lua_pushnil(L); - lua_pushliteral(L, "SSL_set_tlsext_host_name failed"); - return 2; - } - -#else - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua socket SNI disabled because the current " - "version of OpenSSL lacks the support"); - -#endif - } - - if (n >= 4) { - u->ssl_verify = lua_toboolean(L, 4); - - if (n >= 5) { - if (lua_toboolean(L, 5)) { -#ifdef NGX_HTTP_LUA_USE_OCSP - SSL_set_tlsext_status_type(c->ssl->connection, - TLSEXT_STATUSTYPE_ocsp); -#else - return luaL_error(L, "no OCSP support"); -#endif - } - } - } - } - } - - dd("found sni name: %.*s %p", (int) name.len, name.data, name.data); - - if (name.len == 0) { - u->ssl_name.len = 0; - - } else { - if (u->ssl_name.data) { - /* buffer already allocated */ - - if (u->ssl_name.len >= name.len) { - /* reuse it */ - ngx_memcpy(u->ssl_name.data, name.data, name.len); - u->ssl_name.len = name.len; - - } else { - ngx_free(u->ssl_name.data); - goto new_ssl_name; - } - - } else { - -new_ssl_name: - - u->ssl_name.data = ngx_alloc(name.len, ngx_cycle->log); - if (u->ssl_name.data == NULL) { - u->ssl_name.len = 0; - - lua_pushnil(L); - lua_pushliteral(L, "no memory"); - return 2; - } - - ngx_memcpy(u->ssl_name.data, name.data, name.len); - u->ssl_name.len = name.len; - } - } - - u->write_co_ctx = coctx; - -#if 0 -#ifdef NGX_HTTP_LUA_USE_OCSP - SSL_set_tlsext_status_type(c->ssl->connection, TLSEXT_STATUSTYPE_ocsp); -#endif -#endif - - rc = ngx_ssl_handshake(c); - - dd("ngx_ssl_handshake returned %d", (int) rc); - - if (rc == NGX_AGAIN) { - if (c->write->timer_set) { - ngx_del_timer(c->write); - } - - ngx_add_timer(c->read, u->connect_timeout); - - u->conn_waiting = 1; - u->write_prepare_retvals = ngx_http_lua_ssl_handshake_retval_handler; - - ngx_http_lua_cleanup_pending_operation(coctx); - coctx->cleanup = ngx_http_lua_coctx_cleanup; - coctx->data = u; - - c->ssl->handler = ngx_http_lua_ssl_handshake_handler; - - if (ctx->entered_content_phase) { - r->write_event_handler = ngx_http_lua_content_wev_handler; - - } else { - r->write_event_handler = ngx_http_core_run_phases; - } - - return lua_yield(L, 0); - } - - top = lua_gettop(L); - ngx_http_lua_ssl_handshake_handler(c); - return lua_gettop(L) - top; -} - - -static void -ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c) -{ - const char *err; - int waiting; - lua_State *L; - ngx_int_t rc; - ngx_connection_t *dc; /* downstream connection */ - ngx_http_request_t *r; - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_loc_conf_t *llcf; - - ngx_http_lua_socket_tcp_upstream_t *u; - - u = c->data; - r = u->request; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - return; - } - - c->write->handler = ngx_http_lua_socket_tcp_handler; - c->read->handler = ngx_http_lua_socket_tcp_handler; - - waiting = u->conn_waiting; - - dc = r->connection; - L = u->write_co_ctx->co; - - if (c->read->timedout) { - lua_pushnil(L); - lua_pushliteral(L, "timeout"); - goto failed; - } - - if (c->read->timer_set) { - ngx_del_timer(c->read); - } - - if (c->ssl->handshaked) { - - if (u->ssl_verify) { - rc = SSL_get_verify_result(c->ssl->connection); - - if (rc != X509_V_OK) { - lua_pushnil(L); - err = lua_pushfstring(L, "%d: %s", (int) rc, - X509_verify_cert_error_string(rc)); - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - if (llcf->log_socket_errors) { - ngx_log_error(NGX_LOG_ERR, dc->log, 0, "lua ssl " - "certificate verify error: (%s)", err); - } - - goto failed; - } - -#if defined(nginx_version) && nginx_version >= 1007000 - - if (u->ssl_name.len - && ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) - { - lua_pushnil(L); - lua_pushliteral(L, "certificate host mismatch"); - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - if (llcf->log_socket_errors) { - ngx_log_error(NGX_LOG_ERR, dc->log, 0, "lua ssl " - "certificate does not match host \"%V\"", - &u->ssl_name); - } - - goto failed; - } - -#endif - } - - if (waiting) { - ngx_http_lua_socket_handle_conn_success(r, u); - - } else { - (void) ngx_http_lua_ssl_handshake_retval_handler(r, u, L); - } - - if (waiting) { - ngx_http_run_posted_requests(dc); - } - - return; - } - - lua_pushnil(L); - lua_pushliteral(L, "handshake failed"); - -failed: - - if (waiting) { - u->write_prepare_retvals = - ngx_http_lua_socket_conn_error_retval_handler; - ngx_http_lua_socket_handle_conn_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_SSL); - ngx_http_run_posted_requests(dc); - - } else { - (void) ngx_http_lua_socket_conn_error_retval_handler(r, u, L); - } -} - - -static int -ngx_http_lua_ssl_handshake_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) -{ - ngx_connection_t *c; - ngx_ssl_session_t *ssl_session, **ud; - - if (!u->ssl_session_reuse) { - lua_pushboolean(L, 1); - return 1; - } - - ud = lua_newuserdata(L, sizeof(ngx_ssl_session_t *)); - - c = u->peer.connection; - - ssl_session = ngx_ssl_get_session(c); - if (ssl_session == NULL) { - *ud = NULL; - - } else { - *ud = ssl_session; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua ssl save session: %p", ssl_session); - - /* set up the __gc metamethod */ - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - ssl_session_metatable_key)); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_setmetatable(L, -2); - } - - return 1; -} - -#endif /* NGX_HTTP_SSL */ - - -static int -ngx_http_lua_socket_read_error_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) -{ - ngx_uint_t ft_type; - - if (u->read_co_ctx) { - u->read_co_ctx->cleanup = NULL; - } - - ft_type = u->ft_type; - u->ft_type = 0; - - if (u->no_close) { - u->no_close = 0; - - } else { - ngx_http_lua_socket_tcp_finalize_read_part(r, u); - } - - return ngx_http_lua_socket_prepare_error_retvals(r, u, L, ft_type); -} - - -static int -ngx_http_lua_socket_write_error_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) -{ - ngx_uint_t ft_type; - - if (u->write_co_ctx) { - u->write_co_ctx->cleanup = NULL; - } - - ngx_http_lua_socket_tcp_finalize_write_part(r, u); - - ft_type = u->ft_type; - u->ft_type = 0; - return ngx_http_lua_socket_prepare_error_retvals(r, u, L, ft_type); -} - - -static int -ngx_http_lua_socket_prepare_error_retvals(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L, ngx_uint_t ft_type) -{ - u_char errstr[NGX_MAX_ERROR_STR]; - u_char *p; - - if (ft_type & (NGX_HTTP_LUA_SOCKET_FT_RESOLVER - | NGX_HTTP_LUA_SOCKET_FT_SSL)) - { - return 2; - } - - lua_pushnil(L); - - if (ft_type & NGX_HTTP_LUA_SOCKET_FT_TIMEOUT) { - lua_pushliteral(L, "timeout"); - - } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_CLOSED) { - lua_pushliteral(L, "closed"); - - } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_BUFTOOSMALL) { - lua_pushliteral(L, "buffer too small"); - - } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_NOMEM) { - lua_pushliteral(L, "no memory"); - - } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT) { - lua_pushliteral(L, "client aborted"); - - } else { - - if (u->socket_errno) { -#if defined(nginx_version) && nginx_version >= 9000 - p = ngx_strerror(u->socket_errno, errstr, sizeof(errstr)); -#else - p = ngx_strerror_r(u->socket_errno, errstr, sizeof(errstr)); -#endif - /* for compatibility with LuaSocket */ - ngx_strlow(errstr, errstr, p - errstr); - lua_pushlstring(L, (char *) errstr, p - errstr); - - } else { - lua_pushliteral(L, "error"); - } - } - - return 2; -} - - -static int -ngx_http_lua_socket_tcp_conn_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) -{ - if (u->ft_type) { - return ngx_http_lua_socket_conn_error_retval_handler(r, u, L); - } - - lua_pushinteger(L, 1); - return 1; -} - - -static int -ngx_http_lua_socket_tcp_receive_helper(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) -{ - ngx_int_t rc; - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_co_ctx_t *coctx; - - u->input_filter_ctx = u; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - if (u->bufs_in == NULL) { - u->bufs_in = - ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, - &ctx->free_recv_bufs, - u->conf->buffer_size); - - if (u->bufs_in == NULL) { - return luaL_error(L, "no memory"); - } - - u->buf_in = u->bufs_in; - u->buffer = *u->buf_in->buf; - } - - dd("tcp receive: buf_in: %p, bufs_in: %p", u->buf_in, u->bufs_in); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket read timeout: %M", u->read_timeout); - - if (u->raw_downstream || u->body_downstream) { - r->read_event_handler = ngx_http_lua_req_socket_rev_handler; - } - - u->read_waiting = 0; - u->read_co_ctx = NULL; - - rc = ngx_http_lua_socket_tcp_read(r, u); - - if (rc == NGX_ERROR) { - dd("read failed: %d", (int) u->ft_type); - rc = ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); - dd("tcp receive retval returned: %d", (int) rc); - return rc; - } - - if (rc == NGX_OK) { - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket receive done in a single run"); - - return ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); - } - - /* rc == NGX_AGAIN */ - - u->read_event_handler = ngx_http_lua_socket_read_handler; - - coctx = ctx->cur_co_ctx; - - ngx_http_lua_cleanup_pending_operation(coctx); - coctx->cleanup = ngx_http_lua_coctx_cleanup; - coctx->data = u; - - if (ctx->entered_content_phase) { - r->write_event_handler = ngx_http_lua_content_wev_handler; - - } else { - r->write_event_handler = ngx_http_core_run_phases; - } - - u->read_co_ctx = coctx; - u->read_waiting = 1; - u->read_prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler; - - dd("setting data to %p, coctx:%p", u, coctx); - - if (u->raw_downstream || u->body_downstream) { - ctx->downstream = u; - } - - return lua_yield(L, 0); -} - - -static int -ngx_http_lua_socket_tcp_receiveany(lua_State *L) -{ - int n; - lua_Integer bytes; - ngx_http_request_t *r; - ngx_http_lua_loc_conf_t *llcf; - ngx_http_lua_socket_tcp_upstream_t *u; - - n = lua_gettop(L); - if (n != 2) { - return luaL_error(L, "expecting 2 arguments " - "(including the object), but got %d", n); - } - - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - luaL_checktype(L, 1, LUA_TTABLE); - - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); - - if (u == NULL || u->peer.connection == NULL || u->read_closed) { - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (llcf->log_socket_errors) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "attempt to receive data on a closed socket: u:%p, " - "c:%p, ft:%d eof:%d", - u, u ? u->peer.connection : NULL, - u ? (int) u->ft_type : 0, u ? (int) u->eof : 0); - } - - lua_pushnil(L); - lua_pushliteral(L, "closed"); - return 2; - } - - if (u->request != r) { - return luaL_error(L, "bad request"); - } - - ngx_http_lua_socket_check_busy_connecting(r, u, L); - ngx_http_lua_socket_check_busy_reading(r, u, L); - - if (!lua_isnumber(L, 2)) { - return luaL_argerror(L, 2, "bad max argument"); - } - - bytes = lua_tointeger(L, 2); - if (bytes <= 0) { - return luaL_argerror(L, 2, "bad max argument"); - } - - u->input_filter = ngx_http_lua_socket_read_any; - u->rest = (size_t) bytes; - u->length = u->rest; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket calling receiveany() method to read at " - "most %uz bytes", u->rest); - - return ngx_http_lua_socket_tcp_receive_helper(r, u, L); -} - - -static int -ngx_http_lua_socket_tcp_receive(lua_State *L) -{ - ngx_http_request_t *r; - ngx_http_lua_socket_tcp_upstream_t *u; - int n; - ngx_str_t pat; - lua_Integer bytes; - char *p; - int typ; - ngx_http_lua_loc_conf_t *llcf; - - n = lua_gettop(L); - if (n != 1 && n != 2) { - return luaL_error(L, "expecting 1 or 2 arguments " - "(including the object), but got %d", n); - } - - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket calling receive() method"); - - luaL_checktype(L, 1, LUA_TTABLE); - - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); - - if (u == NULL || u->peer.connection == NULL || u->read_closed) { - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (llcf->log_socket_errors) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "attempt to receive data on a closed socket: u:%p, " - "c:%p, ft:%d eof:%d", - u, u ? u->peer.connection : NULL, - u ? (int) u->ft_type : 0, u ? (int) u->eof : 0); - } - - lua_pushnil(L); - lua_pushliteral(L, "closed"); - return 2; - } - - if (u->request != r) { - return luaL_error(L, "bad request"); - } - - ngx_http_lua_socket_check_busy_connecting(r, u, L); - ngx_http_lua_socket_check_busy_reading(r, u, L); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket read timeout: %M", u->read_timeout); - - if (n > 1) { - if (lua_isnumber(L, 2)) { - typ = LUA_TNUMBER; - - } else { - typ = lua_type(L, 2); - } - - switch (typ) { - case LUA_TSTRING: - pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); - if (pat.len != 2 || pat.data[0] != '*') { - p = (char *) lua_pushfstring(L, "bad pattern argument: %s", - (char *) pat.data); - - return luaL_argerror(L, 2, p); - } - - switch (pat.data[1]) { - case 'l': - u->input_filter = ngx_http_lua_socket_read_line; - break; - - case 'a': - u->input_filter = ngx_http_lua_socket_read_all; - break; - - default: - return luaL_argerror(L, 2, "bad pattern argument"); - break; - } - - u->length = 0; - u->rest = 0; - - break; - - case LUA_TNUMBER: - bytes = lua_tointeger(L, 2); - if (bytes < 0) { - return luaL_argerror(L, 2, "bad pattern argument"); - } - -#if 1 - if (bytes == 0) { - lua_pushliteral(L, ""); - return 1; - } -#endif - - u->input_filter = ngx_http_lua_socket_read_chunk; - u->length = (size_t) bytes; - u->rest = u->length; - - break; - - default: - return luaL_argerror(L, 2, "bad pattern argument"); - break; - } - - } else { - u->input_filter = ngx_http_lua_socket_read_line; - u->length = 0; - u->rest = 0; - } - - return ngx_http_lua_socket_tcp_receive_helper(r, u, L); -} - - -static ngx_int_t -ngx_http_lua_socket_read_chunk(void *data, ssize_t bytes) -{ - ngx_int_t rc; - ngx_http_lua_socket_tcp_upstream_t *u = data; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, - "lua tcp socket read chunk %z", bytes); - - rc = ngx_http_lua_read_bytes(&u->buffer, u->buf_in, &u->rest, - bytes, u->request->connection->log); - if (rc == NGX_ERROR) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; - return NGX_ERROR; - } - - return rc; -} - - -static ngx_int_t -ngx_http_lua_socket_read_all(void *data, ssize_t bytes) -{ - ngx_http_lua_socket_tcp_upstream_t *u = data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, - "lua tcp socket read all"); - return ngx_http_lua_read_all(&u->buffer, u->buf_in, bytes, - u->request->connection->log); -} - - -static ngx_int_t -ngx_http_lua_socket_read_line(void *data, ssize_t bytes) -{ - ngx_http_lua_socket_tcp_upstream_t *u = data; - - ngx_int_t rc; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, - "lua tcp socket read line"); - - rc = ngx_http_lua_read_line(&u->buffer, u->buf_in, bytes, - u->request->connection->log); - if (rc == NGX_ERROR) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; - return NGX_ERROR; - } - - return rc; -} - - -static ngx_int_t -ngx_http_lua_socket_read_any(void *data, ssize_t bytes) -{ - ngx_http_lua_socket_tcp_upstream_t *u = data; - - ngx_int_t rc; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, - "lua tcp socket read any"); - - rc = ngx_http_lua_read_any(&u->buffer, u->buf_in, &u->rest, bytes, - u->request->connection->log); - if (rc == NGX_ERROR) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; - return NGX_ERROR; - } - - return rc; -} - - -static ngx_int_t -ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_int_t rc; - ngx_connection_t *c; - ngx_buf_t *b; - ngx_event_t *rev; - size_t size; - ssize_t n; - unsigned read; - off_t preread = 0; - ngx_http_lua_loc_conf_t *llcf; - - c = u->peer.connection; - rev = c->read; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua tcp socket read data: wait:%d", - (int) u->read_waiting); - - b = &u->buffer; - read = 0; - - for ( ;; ) { - - size = b->last - b->pos; - - if (size || u->eof) { - - rc = u->input_filter(u->input_filter_ctx, size); - - if (rc == NGX_OK) { - - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket receive done: wait:%d, eof:%d, " - "uri:\"%V?%V\"", (int) u->read_waiting, - (int) u->eof, &r->uri, &r->args); - - if (u->body_downstream - && b->last == b->pos - && r->request_body->rest == 0) - { - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (llcf->check_client_abort) { - rc = ngx_http_lua_check_broken_connection(r, rev); - - if (rc == NGX_OK) { - goto success; - } - - if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST) { - ngx_http_lua_socket_handle_read_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); - - } else { - ngx_http_lua_socket_handle_read_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - } - - return NGX_ERROR; - } - } - -#if 1 - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_http_lua_socket_handle_read_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - return NGX_ERROR; - } -#endif - -success: - - ngx_http_lua_socket_handle_read_success(r, u); - return NGX_OK; - } - - if (rc == NGX_ERROR) { - dd("input filter error: ft_type:%d wait:%d", - (int) u->ft_type, (int) u->read_waiting); - - ngx_http_lua_socket_handle_read_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - return NGX_ERROR; - } - - /* rc == NGX_AGAIN */ - - if (u->body_downstream && r->request_body->rest == 0) { - u->eof = 1; - } - - continue; - } - - if (read && !rev->ready) { - rc = NGX_AGAIN; - break; - } - - size = b->end - b->last; - - if (size == 0) { - rc = ngx_http_lua_socket_add_input_buffer(r, u); - if (rc == NGX_ERROR) { - ngx_http_lua_socket_handle_read_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_NOMEM); - - return NGX_ERROR; - } - - b = &u->buffer; - size = (size_t) (b->end - b->last); - } - - if (u->raw_downstream) { - preread = r->header_in->last - r->header_in->pos; - - if (preread) { - - if ((off_t) size > preread) { - size = (size_t) preread; - } - - ngx_http_lua_probe_req_socket_consume_preread(r, - r->header_in->pos, - size); - - b->last = ngx_copy(b->last, r->header_in->pos, size); - r->header_in->pos += size; - continue; - } - - } else if (u->body_downstream) { - - if (r->request_body->rest == 0) { - - dd("request body rest is zero"); - - u->eof = 1; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua request body exhausted"); - - continue; - } - - /* try to process the preread body */ - - preread = r->header_in->last - r->header_in->pos; - - if (preread) { - - /* there is the pre-read part of the request body */ - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http client request body preread %O", preread); - - if (preread >= r->request_body->rest) { - preread = r->request_body->rest; - } - - if ((off_t) size > preread) { - size = (size_t) preread; - } - - ngx_http_lua_probe_req_socket_consume_preread(r, - r->header_in->pos, - size); - - b->last = ngx_copy(b->last, r->header_in->pos, size); - - r->header_in->pos += size; - r->request_length += size; - - if (r->request_body->rest) { - r->request_body->rest -= size; - } - - continue; - } - - if (size > (size_t) r->request_body->rest) { - size = (size_t) r->request_body->rest; - } - } - -#if 1 - if (rev->active && !rev->ready) { - rc = NGX_AGAIN; - break; - } -#endif - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket try to recv data %uz: \"%V?%V\"", - size, &r->uri, &r->args); - - n = c->recv(c, b->last, size); - - dd("read event ready: %d", (int) c->read->ready); - - read = 1; - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket recv returned %d: \"%V?%V\"", - (int) n, &r->uri, &r->args); - - if (n == NGX_AGAIN) { - rc = NGX_AGAIN; - dd("socket recv busy"); - break; - } - - if (n == 0) { - - if (u->raw_downstream || u->body_downstream) { - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (llcf->check_client_abort) { - - ngx_http_lua_socket_handle_read_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); - return NGX_ERROR; - } - - /* llcf->check_client_abort == 0 */ - - if (u->body_downstream && r->request_body->rest) { - ngx_http_lua_socket_handle_read_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); - return NGX_ERROR; - } - } - - u->eof = 1; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket closed"); - - continue; - } - - if (n == NGX_ERROR) { - u->socket_errno = ngx_socket_errno; - ngx_http_lua_socket_handle_read_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - return NGX_ERROR; - } - - b->last += n; - - if (u->body_downstream) { - r->request_length += n; - r->request_body->rest -= n; - } - } - -#if 1 - if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_http_lua_socket_handle_read_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - return NGX_ERROR; - } -#endif - - if (rev->active) { - ngx_add_timer(rev, u->read_timeout); - - } else if (rev->timer_set) { - ngx_del_timer(rev); - } - - return rc; -} - - -static int -ngx_http_lua_socket_tcp_send(lua_State *L) -{ - ngx_int_t rc; - ngx_http_request_t *r; - u_char *p; - size_t len; - ngx_chain_t *cl; - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_socket_tcp_upstream_t *u; - int type; - int tcp_nodelay; - const char *msg; - ngx_buf_t *b; - ngx_connection_t *c; - ngx_http_lua_loc_conf_t *llcf; - ngx_http_core_loc_conf_t *clcf; - ngx_http_lua_co_ctx_t *coctx; - - /* TODO: add support for the optional "i" and "j" arguments */ - - if (lua_gettop(L) != 2) { - return luaL_error(L, "expecting 2 arguments (including the object), " - "but got %d", lua_gettop(L)); - } - - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - luaL_checktype(L, 1, LUA_TTABLE); - - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); - lua_pop(L, 1); - - dd("tcp send: u=%p, u->write_closed=%d", u, (unsigned) u->write_closed); - - if (u == NULL || u->peer.connection == NULL || u->write_closed) { - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (llcf->log_socket_errors) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "attempt to send data on a closed socket: u:%p, " - "c:%p, ft:%d eof:%d", - u, u ? u->peer.connection : NULL, - u ? (int) u->ft_type : 0, u ? (int) u->eof : 0); - } - - lua_pushnil(L); - lua_pushliteral(L, "closed"); - return 2; - } - - if (u->request != r) { - return luaL_error(L, "bad request"); - } - - ngx_http_lua_socket_check_busy_connecting(r, u, L); - ngx_http_lua_socket_check_busy_writing(r, u, L); - - if (u->body_downstream) { - return luaL_error(L, "attempt to write to request sockets"); - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket send timeout: %M", u->send_timeout); - - type = lua_type(L, 2); - switch (type) { - case LUA_TNUMBER: - case LUA_TSTRING: - lua_tolstring(L, 2, &len); - break; - - case LUA_TTABLE: - len = ngx_http_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */); - break; - - case LUA_TNIL: - len = sizeof("nil") - 1; - break; - - case LUA_TBOOLEAN: - if (lua_toboolean(L, 2)) { - len = sizeof("true") - 1; - - } else { - len = sizeof("false") - 1; - } - - break; - - default: - msg = lua_pushfstring(L, "string, number, boolean, nil, " - "or array table expected, got %s", - lua_typename(L, type)); - - return luaL_argerror(L, 2, msg); - } - - if (len == 0) { - lua_pushinteger(L, 0); - return 1; - } - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, - &ctx->free_bufs, len); - - if (cl == NULL) { - return luaL_error(L, "no memory"); - } - - b = cl->buf; - - switch (type) { - case LUA_TNUMBER: - case LUA_TSTRING: - p = (u_char *) lua_tolstring(L, -1, &len); - b->last = ngx_copy(b->last, (u_char *) p, len); - break; - - case LUA_TTABLE: - b->last = ngx_http_lua_copy_str_in_table(L, -1, b->last); - break; - - case LUA_TNIL: - *b->last++ = 'n'; - *b->last++ = 'i'; - *b->last++ = 'l'; - break; - - case LUA_TBOOLEAN: - if (lua_toboolean(L, 2)) { - *b->last++ = 't'; - *b->last++ = 'r'; - *b->last++ = 'u'; - *b->last++ = 'e'; - - } else { - *b->last++ = 'f'; - *b->last++ = 'a'; - *b->last++ = 'l'; - *b->last++ = 's'; - *b->last++ = 'e'; - } - - break; - - default: - return luaL_error(L, "impossible to reach here"); - } - - u->request_bufs = cl; - - u->request_len = len; - - /* mimic ngx_http_upstream_init_request here */ - - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - c = u->peer.connection; - - if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua socket tcp_nodelay"); - - tcp_nodelay = 1; - - if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, - (const void *) &tcp_nodelay, sizeof(int)) - == -1) - { - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - if (llcf->log_socket_errors) { - ngx_connection_error(c, ngx_socket_errno, - "setsockopt(TCP_NODELAY) " - "failed"); - } - - lua_pushnil(L); - lua_pushliteral(L, "setsocketopt tcp_nodelay failed"); - return 2; - } - - c->tcp_nodelay = NGX_TCP_NODELAY_SET; - } - -#if 1 - u->write_waiting = 0; - u->write_co_ctx = NULL; -#endif - - ngx_http_lua_probe_socket_tcp_send_start(r, u, b->pos, len); - - rc = ngx_http_lua_socket_send(r, u); - - dd("socket send returned %d", (int) rc); - - if (rc == NGX_ERROR) { - return ngx_http_lua_socket_write_error_retval_handler(r, u, L); - } - - if (rc == NGX_OK) { - lua_pushinteger(L, len); - return 1; - } - - /* rc == NGX_AGAIN */ - - coctx = ctx->cur_co_ctx; - - ngx_http_lua_cleanup_pending_operation(coctx); - coctx->cleanup = ngx_http_lua_coctx_cleanup; - coctx->data = u; - - if (u->raw_downstream) { - ctx->writing_raw_req_socket = 1; - } - - if (ctx->entered_content_phase) { - r->write_event_handler = ngx_http_lua_content_wev_handler; - - } else { - r->write_event_handler = ngx_http_core_run_phases; - } - - u->write_co_ctx = coctx; - u->write_waiting = 1; - u->write_prepare_retvals = ngx_http_lua_socket_tcp_send_retval_handler; - - dd("setting data to %p", u); - - return lua_yield(L, 0); -} - - -static int -ngx_http_lua_socket_tcp_send_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) -{ - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket send return value handler"); - - if (u->ft_type) { - return ngx_http_lua_socket_write_error_retval_handler(r, u, L); - } - - lua_pushinteger(L, u->request_len); - return 1; -} - - -static int -ngx_http_lua_socket_tcp_receive_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) -{ - int n; - ngx_int_t rc; - ngx_http_lua_ctx_t *ctx; - ngx_event_t *ev; - - ngx_http_lua_loc_conf_t *llcf; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket receive return value handler"); - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - -#if 1 - if (u->raw_downstream || u->body_downstream) { - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (llcf->check_client_abort) { - - r->read_event_handler = ngx_http_lua_rd_check_broken_connection; - - ev = r->connection->read; - - dd("rev active: %d", ev->active); - - if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && !ev->active) { - if (ngx_add_event(ev, NGX_READ_EVENT, 0) != NGX_OK) { - lua_pushnil(L); - lua_pushliteral(L, "failed to add event"); - return 2; - } - } - - } else { - /* llcf->check_client_abort == 0 */ - r->read_event_handler = ngx_http_block_reading; - } - } -#endif - - if (u->ft_type) { - - if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_TIMEOUT) { - u->no_close = 1; - } - - dd("u->bufs_in: %p", u->bufs_in); - - if (u->bufs_in) { - rc = ngx_http_lua_socket_push_input_data(r, ctx, u, L); - if (rc == NGX_ERROR) { - lua_pushnil(L); - lua_pushliteral(L, "no memory"); - return 2; - } - - (void) ngx_http_lua_socket_read_error_retval_handler(r, u, L); - - lua_pushvalue(L, -3); - lua_remove(L, -4); - return 3; - } - - n = ngx_http_lua_socket_read_error_retval_handler(r, u, L); - lua_pushliteral(L, ""); - return n + 1; - } - - rc = ngx_http_lua_socket_push_input_data(r, ctx, u, L); - if (rc == NGX_ERROR) { - lua_pushnil(L); - lua_pushliteral(L, "no memory"); - return 2; - } - - return 1; -} - - -static int -ngx_http_lua_socket_tcp_close(lua_State *L) -{ - ngx_http_request_t *r; - ngx_http_lua_socket_tcp_upstream_t *u; - - if (lua_gettop(L) != 1) { - return luaL_error(L, "expecting 1 argument " - "(including the object) but seen %d", lua_gettop(L)); - } - - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - luaL_checktype(L, 1, LUA_TTABLE); - - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); - lua_pop(L, 1); - - if (u == NULL - || u->peer.connection == NULL - || (u->read_closed && u->write_closed)) - { - lua_pushnil(L); - lua_pushliteral(L, "closed"); - return 2; - } - - if (u->request != r) { - return luaL_error(L, "bad request"); - } - - ngx_http_lua_socket_check_busy_connecting(r, u, L); - ngx_http_lua_socket_check_busy_reading(r, u, L); - ngx_http_lua_socket_check_busy_writing(r, u, L); - - if (u->raw_downstream || u->body_downstream) { - lua_pushnil(L); - lua_pushliteral(L, "attempt to close a request socket"); - return 2; - } - - ngx_http_lua_socket_tcp_finalize(r, u); - - lua_pushinteger(L, 1); - return 1; -} - - -static int -ngx_http_lua_socket_tcp_setoption(lua_State *L) -{ - /* TODO */ - return 0; -} - - -static int -ngx_http_lua_socket_tcp_settimeout(lua_State *L) -{ - int n; - ngx_int_t timeout; - - ngx_http_lua_socket_tcp_upstream_t *u; - - n = lua_gettop(L); - - if (n != 2) { - return luaL_error(L, "ngx.socket settimout: expecting 2 arguments " - "(including the object) but seen %d", lua_gettop(L)); - } - - timeout = (ngx_int_t) lua_tonumber(L, 2); - if (timeout >> 31) { - return luaL_error(L, "bad timeout value"); - } - - lua_pushinteger(L, timeout); - lua_pushinteger(L, timeout); - - lua_rawseti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX); - lua_rawseti(L, 1, SOCKET_SEND_TIMEOUT_INDEX); - lua_rawseti(L, 1, SOCKET_READ_TIMEOUT_INDEX); - - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); - - if (u) { - if (timeout > 0) { - u->read_timeout = (ngx_msec_t) timeout; - u->send_timeout = (ngx_msec_t) timeout; - u->connect_timeout = (ngx_msec_t) timeout; - - } else { - u->read_timeout = u->conf->read_timeout; - u->send_timeout = u->conf->send_timeout; - u->connect_timeout = u->conf->connect_timeout; - } - } - - return 0; -} - - -static int -ngx_http_lua_socket_tcp_settimeouts(lua_State *L) -{ - int n; - ngx_int_t connect_timeout, send_timeout, read_timeout; - - ngx_http_lua_socket_tcp_upstream_t *u; - - n = lua_gettop(L); - - if (n != 4) { - return luaL_error(L, "ngx.socket settimout: expecting 4 arguments " - "(including the object) but seen %d", lua_gettop(L)); - } - - connect_timeout = (ngx_int_t) lua_tonumber(L, 2); - if (connect_timeout >> 31) { - return luaL_error(L, "bad timeout value"); - } - - send_timeout = (ngx_int_t) lua_tonumber(L, 3); - if (send_timeout >> 31) { - return luaL_error(L, "bad timeout value"); - } - - read_timeout = (ngx_int_t) lua_tonumber(L, 4); - if (read_timeout >> 31) { - return luaL_error(L, "bad timeout value"); - } - - lua_rawseti(L, 1, SOCKET_READ_TIMEOUT_INDEX); - lua_rawseti(L, 1, SOCKET_SEND_TIMEOUT_INDEX); - lua_rawseti(L, 1, SOCKET_CONNECT_TIMEOUT_INDEX); - - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); - - if (u) { - if (connect_timeout > 0) { - u->connect_timeout = (ngx_msec_t) connect_timeout; - - } else { - u->connect_timeout = u->conf->connect_timeout; - } - - if (send_timeout > 0) { - u->send_timeout = (ngx_msec_t) send_timeout; - - } else { - u->send_timeout = u->conf->send_timeout; - } - - if (read_timeout > 0) { - u->read_timeout = (ngx_msec_t) read_timeout; - - } else { - u->read_timeout = u->conf->read_timeout; - } - } - - return 0; -} - - -static void -ngx_http_lua_socket_tcp_handler(ngx_event_t *ev) -{ - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_log_ctx_t *ctx; - - ngx_http_lua_socket_tcp_upstream_t *u; - - c = ev->data; - u = c->data; - r = u->request; - c = r->connection; - - if (c->fd != (ngx_socket_t) -1) { /* not a fake connection */ - ctx = c->log->data; - ctx->current_request = r; - } - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua tcp socket handler for \"%V?%V\", wev %d", &r->uri, - &r->args, (int) ev->write); - - if (ev->write) { - u->write_event_handler(r, u); - - } else { - u->read_event_handler(r, u); - } - - ngx_http_run_posted_requests(c); -} - - -static ngx_int_t -ngx_http_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, void *data) -{ - /* empty */ - return NGX_OK; -} - - -static void -ngx_http_lua_socket_read_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_connection_t *c; - ngx_http_lua_loc_conf_t *llcf; - - c = u->peer.connection; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket read handler"); - - if (c->read->timedout) { - c->read->timedout = 0; - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (llcf->log_socket_errors) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "lua tcp socket read timed out"); - } - - ngx_http_lua_socket_handle_read_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); - return; - } - -#if 1 - if (c->read->timer_set) { - ngx_del_timer(c->read); - } -#endif - - if (u->buffer.start != NULL) { - (void) ngx_http_lua_socket_tcp_read(r, u); - } -} - - -static void -ngx_http_lua_socket_send_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_connection_t *c; - ngx_http_lua_loc_conf_t *llcf; - - c = u->peer.connection; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket send handler"); - - if (c->write->timedout) { - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (llcf->log_socket_errors) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "lua tcp socket write timed out"); - } - - ngx_http_lua_socket_handle_write_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); - return; - } - - if (u->request_bufs) { - (void) ngx_http_lua_socket_send(r, u); - } -} - - -static ngx_int_t -ngx_http_lua_socket_send(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_int_t n; - ngx_connection_t *c; - ngx_http_lua_ctx_t *ctx; - ngx_buf_t *b; - - c = u->peer.connection; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket send data"); - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - ngx_http_lua_socket_handle_write_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - return NGX_ERROR; - } - - b = u->request_bufs->buf; - - for (;;) { - n = c->send(c, b->pos, b->last - b->pos); - - if (n >= 0) { - b->pos += n; - - if (b->pos == b->last) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua tcp socket sent all the data"); - - if (c->write->timer_set) { - ngx_del_timer(c->write); - } - - -#if defined(nginx_version) && nginx_version >= 1001004 - ngx_chain_update_chains(r->pool, -#else - ngx_chain_update_chains( -#endif - &ctx->free_bufs, &ctx->busy_bufs, - &u->request_bufs, - (ngx_buf_tag_t) &ngx_http_lua_module); - - u->write_event_handler = ngx_http_lua_socket_dummy_handler; - - if (ngx_handle_write_event(c->write, 0) != NGX_OK) { - ngx_http_lua_socket_handle_write_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - return NGX_ERROR; - } - - ngx_http_lua_socket_handle_write_success(r, u); - return NGX_OK; - } - - /* keep sending more data */ - continue; - } - - /* NGX_ERROR || NGX_AGAIN */ - break; - } - - if (n == NGX_ERROR) { - c->error = 1; - u->socket_errno = ngx_socket_errno; - ngx_http_lua_socket_handle_write_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - return NGX_ERROR; - } - - /* n == NGX_AGAIN */ - - if (u->raw_downstream) { - ctx->writing_raw_req_socket = 1; - } - - u->write_event_handler = ngx_http_lua_socket_send_handler; - - ngx_add_timer(c->write, u->send_timeout); - - if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) { - ngx_http_lua_socket_handle_write_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - return NGX_ERROR; - } - - return NGX_AGAIN; -} - - -static void -ngx_http_lua_socket_handle_conn_success(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_co_ctx_t *coctx; - -#if 1 - u->read_event_handler = ngx_http_lua_socket_dummy_handler; - u->write_event_handler = ngx_http_lua_socket_dummy_handler; -#endif - - if (u->conn_waiting) { - u->conn_waiting = 0; - - coctx = u->write_co_ctx; - coctx->cleanup = NULL; - u->write_co_ctx = NULL; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - return; - } - - ctx->resume_handler = ngx_http_lua_socket_tcp_conn_resume; - ctx->cur_co_ctx = coctx; - - ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) - || coctx->co_ref >= 0)); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket waking up the current request (conn)"); - - r->write_event_handler(r); - } -} - - -static void -ngx_http_lua_socket_handle_read_success(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_co_ctx_t *coctx; - -#if 1 - u->read_event_handler = ngx_http_lua_socket_dummy_handler; -#endif - - if (u->read_waiting) { - u->read_waiting = 0; - - coctx = u->read_co_ctx; - coctx->cleanup = NULL; - u->read_co_ctx = NULL; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - return; - } - - ctx->resume_handler = ngx_http_lua_socket_tcp_read_resume; - ctx->cur_co_ctx = coctx; - - ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) - || coctx->co_ref >= 0)); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket waking up the current request (read)"); - - r->write_event_handler(r); - } -} - - -static void -ngx_http_lua_socket_handle_write_success(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_co_ctx_t *coctx; - -#if 1 - u->write_event_handler = ngx_http_lua_socket_dummy_handler; -#endif - - if (u->write_waiting) { - u->write_waiting = 0; - - coctx = u->write_co_ctx; - coctx->cleanup = NULL; - u->write_co_ctx = NULL; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - return; - } - - ctx->resume_handler = ngx_http_lua_socket_tcp_write_resume; - ctx->cur_co_ctx = coctx; - - ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) - || coctx->co_ref >= 0)); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket waking up the current request (read)"); - - r->write_event_handler(r); - } -} - - -static void -ngx_http_lua_socket_handle_conn_error(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type) -{ - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_co_ctx_t *coctx; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket handle connect error"); - - u->ft_type |= ft_type; - -#if 1 - ngx_http_lua_socket_tcp_finalize(r, u); -#endif - - u->read_event_handler = ngx_http_lua_socket_dummy_handler; - u->write_event_handler = ngx_http_lua_socket_dummy_handler; - - dd("connection waiting: %d", (int) u->conn_waiting); - - coctx = u->write_co_ctx; - - if (u->conn_waiting) { - u->conn_waiting = 0; - - coctx->cleanup = NULL; - u->write_co_ctx = NULL; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - ctx->resume_handler = ngx_http_lua_socket_tcp_conn_resume; - ctx->cur_co_ctx = coctx; - - ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) - || coctx->co_ref >= 0)); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket waking up the current request"); - - r->write_event_handler(r); - } -} - - -static void -ngx_http_lua_socket_handle_read_error(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type) -{ - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_co_ctx_t *coctx; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket handle read error"); - - u->ft_type |= ft_type; - -#if 0 - ngx_http_lua_socket_tcp_finalize(r, u); -#endif - - u->read_event_handler = ngx_http_lua_socket_dummy_handler; - - if (u->read_waiting) { - u->read_waiting = 0; - - coctx = u->read_co_ctx; - coctx->cleanup = NULL; - u->read_co_ctx = NULL; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - ctx->resume_handler = ngx_http_lua_socket_tcp_read_resume; - ctx->cur_co_ctx = coctx; - - ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) - || coctx->co_ref >= 0)); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket waking up the current request"); - - r->write_event_handler(r); - } -} - - -static void -ngx_http_lua_socket_handle_write_error(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type) -{ - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_co_ctx_t *coctx; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket handle write error"); - - u->ft_type |= ft_type; - -#if 0 - ngx_http_lua_socket_tcp_finalize(r, u); -#endif - - u->write_event_handler = ngx_http_lua_socket_dummy_handler; - - if (u->write_waiting) { - u->write_waiting = 0; - - coctx = u->write_co_ctx; - coctx->cleanup = NULL; - u->write_co_ctx = NULL; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - ctx->resume_handler = ngx_http_lua_socket_tcp_write_resume; - ctx->cur_co_ctx = coctx; - - ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) - || coctx->co_ref >= 0)); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket waking up the current request"); - - r->write_event_handler(r); - } -} - - -static void -ngx_http_lua_socket_connected_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_int_t rc; - ngx_connection_t *c; - ngx_http_lua_loc_conf_t *llcf; - - c = u->peer.connection; - - if (c->write->timedout) { - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (llcf->log_socket_errors) { - ngx_http_lua_socket_init_peer_connection_addr_text(&u->peer); - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "lua tcp socket connect timed out," - " when connecting to %V:%ud", - &c->addr_text, ngx_inet_get_port(u->peer.sockaddr)); - } - - ngx_http_lua_socket_handle_conn_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); - return; - } - - if (c->write->timer_set) { - ngx_del_timer(c->write); - } - - rc = ngx_http_lua_socket_test_connect(r, c); - if (rc != NGX_OK) { - if (rc > 0) { - u->socket_errno = (ngx_err_t) rc; - } - - ngx_http_lua_socket_handle_conn_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - return; - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket connected"); - - /* We should delete the current write/read event - * here because the socket object may not be used immediately - * on the Lua land, thus causing hot spin around level triggered - * event poll and wasting CPU cycles. */ - - if (ngx_handle_write_event(c->write, 0) != NGX_OK) { - ngx_http_lua_socket_handle_conn_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - return; - } - - if (ngx_handle_read_event(c->read, 0) != NGX_OK) { - ngx_http_lua_socket_handle_conn_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); - return; - } - - ngx_http_lua_socket_handle_conn_success(r, u); -} - - -static void -ngx_http_lua_socket_tcp_cleanup(void *data) -{ - ngx_http_lua_socket_tcp_upstream_t *u = data; - - ngx_http_request_t *r; - - r = u->request; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "cleanup lua tcp socket request: \"%V\"", &r->uri); - - ngx_http_lua_socket_tcp_finalize(r, u); -} - - -static void -ngx_http_lua_socket_tcp_finalize_read_part(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_chain_t *cl; - ngx_chain_t **ll; - ngx_connection_t *c; - ngx_http_lua_ctx_t *ctx; - - if (u->read_closed) { - return; - } - - u->read_closed = 1; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - if (ctx && u->bufs_in) { - - ll = &u->bufs_in; - for (cl = u->bufs_in; cl; cl = cl->next) { - dd("bufs_in chain: %p, next %p", cl, cl->next); - cl->buf->pos = cl->buf->last; - ll = &cl->next; - } - - dd("ctx: %p", ctx); - dd("free recv bufs: %p", ctx->free_recv_bufs); - *ll = ctx->free_recv_bufs; - ctx->free_recv_bufs = u->bufs_in; - u->bufs_in = NULL; - u->buf_in = NULL; - ngx_memzero(&u->buffer, sizeof(ngx_buf_t)); - } - - if (u->raw_downstream || u->body_downstream) { - if (r->connection->read->timer_set) { - ngx_del_timer(r->connection->read); - } - return; - } - - c = u->peer.connection; - - if (c) { - if (c->read->timer_set) { - ngx_del_timer(c->read); - } - - if (c->read->active || c->read->disabled) { - ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); - } - -#if defined(nginx_version) && nginx_version >= 1007005 - if (c->read->posted) { -#else - if (c->read->prev) { -#endif - ngx_delete_posted_event(c->read); - } - - c->read->closed = 1; - - /* TODO: shutdown the reading part of the connection */ - } -} - - -static void -ngx_http_lua_socket_tcp_finalize_write_part(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_connection_t *c; - ngx_http_lua_ctx_t *ctx; - - if (u->write_closed) { - return; - } - - u->write_closed = 1; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - if (u->raw_downstream || u->body_downstream) { - if (ctx && ctx->writing_raw_req_socket) { - ctx->writing_raw_req_socket = 0; - if (r->connection->write->timer_set) { - ngx_del_timer(r->connection->write); - } - - r->connection->write->error = 1; - } - return; - } - - c = u->peer.connection; - - if (c) { - if (c->write->timer_set) { - ngx_del_timer(c->write); - } - - if (c->write->active || c->write->disabled) { - ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT); - } - -#if defined(nginx_version) && nginx_version >= 1007005 - if (c->write->posted) { -#else - if (c->write->prev) { -#endif - ngx_delete_posted_event(c->write); - } - - c->write->closed = 1; - - /* TODO: shutdown the writing part of the connection */ - } -} - - -static void -ngx_http_lua_socket_tcp_conn_op_timeout_handler(ngx_event_t *ev) -{ - ngx_http_lua_socket_tcp_upstream_t *u; - ngx_http_lua_ctx_t *ctx; - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_lua_co_ctx_t *coctx; - ngx_http_lua_loc_conf_t *llcf; - ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; - - conn_op_ctx = ev->data; - ngx_queue_remove(&conn_op_ctx->queue); - - u = conn_op_ctx->u; - r = u->request; - - coctx = u->write_co_ctx; - coctx->cleanup = NULL; - /* note that we store conn_op_ctx in coctx->data instead of u */ - coctx->data = conn_op_ctx; - u->write_co_ctx = NULL; - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (llcf->log_socket_errors) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "lua tcp socket queued connect timed out," - " when trying to connect to %V:%ud", - &conn_op_ctx->host, conn_op_ctx->port); - } - - ngx_queue_insert_head(&u->socket_pool->cache_connect_op, - &conn_op_ctx->queue); - u->socket_pool->connections--; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - return; - } - - ctx->cur_co_ctx = coctx; - - ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) - || coctx->co_ref >= 0)); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket waking up the current request"); - - u->write_prepare_retvals = - ngx_http_lua_socket_tcp_conn_op_timeout_retval_handler; - - c = r->connection; - - if (ctx->entered_content_phase) { - (void) ngx_http_lua_socket_tcp_conn_op_resume(r); - - } else { - ctx->resume_handler = ngx_http_lua_socket_tcp_conn_op_resume; - ngx_http_core_run_phases(r); - } - - ngx_http_run_posted_requests(c); -} - - -static int -ngx_http_lua_socket_tcp_conn_op_timeout_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) -{ - lua_pushnil(L); - lua_pushliteral(L, "timeout"); - return 2; -} - - -static void -ngx_http_lua_socket_tcp_resume_conn_op(ngx_http_lua_socket_pool_t *spool) -{ - ngx_queue_t *q; - ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; - -#if (NGX_DEBUG) - ngx_http_lua_assert(spool->connections >= 0); - -#else - if (spool->connections < 0) { - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, - "lua tcp socket connections count mismatched for " - "connection pool \"%s\", connections: %i, size: %i", - spool->key, spool->connections, spool->size); - spool->connections = 0; - } -#endif - - /* we manually destroy wait_connect_op before triggering connect - * operation resumption, so that there is no resumption happens when Nginx - * is exiting. - */ - if (ngx_queue_empty(&spool->wait_connect_op)) { - return; - } - - q = ngx_queue_head(&spool->wait_connect_op); - conn_op_ctx = ngx_queue_data(q, ngx_http_lua_socket_tcp_conn_op_ctx_t, - queue); - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, - "lua tcp socket post connect operation resumption " - "u: %p, ctx: %p for connection pool \"%s\", " - "connections: %i", - conn_op_ctx->u, conn_op_ctx, spool->key, spool->connections); - - if (conn_op_ctx->event.timer_set) { - ngx_del_timer(&conn_op_ctx->event); - } - - conn_op_ctx->event.handler = - ngx_http_lua_socket_tcp_conn_op_resume_handler; - - ngx_post_event((&conn_op_ctx->event), &ngx_posted_events); -} - - -static void -ngx_http_lua_socket_tcp_conn_op_ctx_cleanup(void *data) -{ - ngx_http_lua_socket_tcp_upstream_t *u; - ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx = data; - - u = conn_op_ctx->u; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, - "cleanup lua tcp socket conn_op_ctx: \"%V\"", - &u->request->uri); - - ngx_queue_insert_head(&u->socket_pool->cache_connect_op, - &conn_op_ctx->queue); -} - - -static void -ngx_http_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev) -{ - ngx_queue_t *q; - ngx_connection_t *c; - ngx_http_lua_ctx_t *ctx; - ngx_http_request_t *r; - ngx_http_cleanup_t *cln; - ngx_http_lua_co_ctx_t *coctx; - ngx_http_lua_socket_pool_t *spool; - ngx_http_lua_socket_tcp_upstream_t *u; - ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; - - conn_op_ctx = ev->data; - u = conn_op_ctx->u; - r = u->request; - spool = u->socket_pool; - - if (ngx_queue_empty(&spool->wait_connect_op)) { -#if (NGX_DEBUG) - ngx_http_lua_assert(!(spool->backlog >= 0 - && spool->connections > spool->size)); - -#else - if (spool->backlog >= 0 && spool->connections > spool->size) { - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, - "lua tcp socket connections count mismatched for " - "connection pool \"%s\", connections: %i, size: %i", - spool->key, spool->connections, spool->size); - spool->connections = spool->size; - } -#endif - - return; - } - - q = ngx_queue_head(&spool->wait_connect_op); - ngx_queue_remove(q); - - coctx = u->write_co_ctx; - coctx->cleanup = NULL; - /* note that we store conn_op_ctx in coctx->data instead of u */ - coctx->data = conn_op_ctx; - /* clear ngx_http_lua_tcp_queue_conn_op_cleanup */ - u->write_co_ctx = NULL; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - ngx_queue_insert_head(&spool->cache_connect_op, - &conn_op_ctx->queue); - return; - } - - ctx->cur_co_ctx = coctx; - - ngx_http_lua_assert(coctx && (!ngx_http_lua_is_thread(ctx) - || coctx->co_ref >= 0)); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket waking up the current request"); - - u->write_prepare_retvals = - ngx_http_lua_socket_tcp_conn_op_resume_retval_handler; - - c = r->connection; - - if (ctx->entered_content_phase) { - (void) ngx_http_lua_socket_tcp_conn_op_resume(r); - - } else { - cln = ngx_http_lua_cleanup_add(r, 0); - if (cln != NULL) { - cln->handler = ngx_http_lua_socket_tcp_conn_op_ctx_cleanup; - cln->data = conn_op_ctx; - conn_op_ctx->cleanup = &cln->handler; - } - - ctx->resume_handler = ngx_http_lua_socket_tcp_conn_op_resume; - ngx_http_core_run_phases(r); - } - - ngx_http_run_posted_requests(c); -} - - -static int -ngx_http_lua_socket_tcp_conn_op_resume_retval_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) -{ - int nret; - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_co_ctx_t *coctx; - ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - return NGX_ERROR; - } - - coctx = ctx->cur_co_ctx; - dd("coctx: %p", coctx); - conn_op_ctx = coctx->data; - if (conn_op_ctx->cleanup != NULL) { - *conn_op_ctx->cleanup = NULL; - ngx_http_lua_cleanup_free(r, conn_op_ctx->cleanup); - conn_op_ctx->cleanup = NULL; - } - - /* decrease pending connect operation counter */ - u->socket_pool->connections--; - - nret = ngx_http_lua_socket_tcp_connect_helper(L, u, r, ctx, - conn_op_ctx->host.data, - conn_op_ctx->host.len, - conn_op_ctx->port, 1); - ngx_queue_insert_head(&u->socket_pool->cache_connect_op, - &conn_op_ctx->queue); - - return nret; -} - - -static void -ngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_connection_t *c; - ngx_http_lua_socket_pool_t *spool; - - dd("request: %p, u: %p, u->cleanup: %p", r, u, u->cleanup); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua finalize socket"); - - if (u->cleanup) { - *u->cleanup = NULL; - ngx_http_lua_cleanup_free(r, u->cleanup); - u->cleanup = NULL; - } - - ngx_http_lua_socket_tcp_finalize_read_part(r, u); - ngx_http_lua_socket_tcp_finalize_write_part(r, u); - - if (u->raw_downstream || u->body_downstream) { - u->peer.connection = NULL; - return; - } - - if (u->resolved && u->resolved->ctx) { - ngx_resolve_name_done(u->resolved->ctx); - u->resolved->ctx = NULL; - } - - if (u->peer.free) { - u->peer.free(&u->peer, u->peer.data, 0); - } - -#if (NGX_HTTP_SSL) - if (u->ssl_name.data) { - ngx_free(u->ssl_name.data); - u->ssl_name.data = NULL; - u->ssl_name.len = 0; - } -#endif - - c = u->peer.connection; - if (c) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua close socket connection"); - - ngx_http_lua_socket_tcp_close_connection(c); - u->peer.connection = NULL; - u->conn_closed = 1; - - spool = u->socket_pool; - if (spool == NULL) { - return; - } - - spool->connections--; - - if (spool->connections == 0) { - ngx_http_lua_socket_free_pool(r->connection->log, spool); - return; - } - - ngx_http_lua_socket_tcp_resume_conn_op(spool); - } -} - - -static void -ngx_http_lua_socket_tcp_close_connection(ngx_connection_t *c) -{ -#if (NGX_HTTP_SSL) - - if (c->ssl) { - c->ssl->no_wait_shutdown = 1; - c->ssl->no_send_shutdown = 1; - - (void) ngx_ssl_shutdown(c); - } - -#endif - - if (c->pool) { - ngx_destroy_pool(c->pool); - c->pool = NULL; - } - - ngx_close_connection(c); -} - - -static ngx_int_t -ngx_http_lua_socket_test_connect(ngx_http_request_t *r, ngx_connection_t *c) -{ - int err; - socklen_t len; - - ngx_http_lua_loc_conf_t *llcf; - -#if (NGX_HAVE_KQUEUE) - - ngx_event_t *ev; - - if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { - dd("pending eof: (%p)%d (%p)%d", c->write, c->write->pending_eof, - c->read, c->read->pending_eof); - - if (c->write->pending_eof) { - ev = c->write; - - } else if (c->read->pending_eof) { - ev = c->read; - - } else { - ev = NULL; - } - - if (ev) { - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - if (llcf->log_socket_errors) { - (void) ngx_connection_error(c, ev->kq_errno, - "kevent() reported that " - "connect() failed"); - } - return ev->kq_errno; - } - - } else -#endif - { - err = 0; - len = sizeof(int); - - /* - * BSDs and Linux return 0 and set a pending error in err - * Solaris returns -1 and sets errno - */ - - if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) - == -1) - { - err = ngx_errno; - } - - if (err) { - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - if (llcf->log_socket_errors) { - (void) ngx_connection_error(c, err, "connect() failed"); - } - return err; - } - } - - return NGX_OK; -} - - -static void -ngx_http_lua_socket_dummy_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket dummy handler"); -} - - -static int -ngx_http_lua_socket_tcp_receiveuntil(lua_State *L) -{ - ngx_http_request_t *r; - int n; - ngx_str_t pat; - ngx_int_t rc; - size_t size; - unsigned inclusive = 0; - - ngx_http_lua_socket_compiled_pattern_t *cp; - - n = lua_gettop(L); - if (n != 2 && n != 3) { - return luaL_error(L, "expecting 2 or 3 arguments " - "(including the object), but got %d", n); - } - - if (n == 3) { - /* check out the options table */ - - luaL_checktype(L, 3, LUA_TTABLE); - - lua_getfield(L, 3, "inclusive"); - - switch (lua_type(L, -1)) { - case LUA_TNIL: - /* do nothing */ - break; - - case LUA_TBOOLEAN: - if (lua_toboolean(L, -1)) { - inclusive = 1; - } - break; - - default: - return luaL_error(L, "bad \"inclusive\" option value type: %s", - luaL_typename(L, -1)); - - } - - lua_pop(L, 2); - } - - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket calling receiveuntil() method"); - - luaL_checktype(L, 1, LUA_TTABLE); - - pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); - if (pat.len == 0) { - lua_pushnil(L); - lua_pushliteral(L, "pattern is empty"); - return 2; - } - - size = sizeof(ngx_http_lua_socket_compiled_pattern_t); - - cp = lua_newuserdata(L, size); - if (cp == NULL) { - return luaL_error(L, "no memory"); - } - - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - pattern_udata_metatable_key)); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_setmetatable(L, -2); - - ngx_memzero(cp, size); - - cp->inclusive = inclusive; - - rc = ngx_http_lua_socket_compile_pattern(pat.data, pat.len, cp, - r->connection->log); - - if (rc != NGX_OK) { - lua_pushnil(L); - lua_pushliteral(L, "failed to compile pattern"); - return 2; - } - - lua_pushcclosure(L, ngx_http_lua_socket_receiveuntil_iterator, 3); - return 1; -} - - -static int -ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) -{ - ngx_http_request_t *r; - ngx_http_lua_socket_tcp_upstream_t *u; - ngx_int_t rc; - ngx_http_lua_ctx_t *ctx; - lua_Integer bytes; - int n; - ngx_http_lua_co_ctx_t *coctx; - - ngx_http_lua_socket_compiled_pattern_t *cp; - - n = lua_gettop(L); - if (n > 1) { - return luaL_error(L, "expecting 0 or 1 arguments, " - "but seen %d", n); - } - - if (n >= 1) { - bytes = luaL_checkinteger(L, 1); - if (bytes < 0) { - bytes = 0; - } - - } else { - bytes = 0; - } - - lua_rawgeti(L, lua_upvalueindex(1), SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); - lua_pop(L, 1); - - if (u == NULL || u->peer.connection == NULL || u->read_closed) { - lua_pushnil(L); - lua_pushliteral(L, "closed"); - return 2; - } - - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - if (u->request != r) { - return luaL_error(L, "bad request"); - } - - ngx_http_lua_socket_check_busy_connecting(r, u, L); - ngx_http_lua_socket_check_busy_reading(r, u, L); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket receiveuntil iterator"); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket read timeout: %M", u->read_timeout); - - u->input_filter = ngx_http_lua_socket_read_until; - - cp = lua_touserdata(L, lua_upvalueindex(3)); - - dd("checking existing state: %d", cp->state); - - if (cp->state == -1) { - cp->state = 0; - - lua_pushnil(L); - lua_pushnil(L); - lua_pushnil(L); - return 3; - } - - cp->upstream = u; - - cp->pattern.data = - (u_char *) lua_tolstring(L, lua_upvalueindex(2), - &cp->pattern.len); - - u->input_filter_ctx = cp; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - if (u->bufs_in == NULL) { - u->bufs_in = - ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, - &ctx->free_recv_bufs, - u->conf->buffer_size); - - if (u->bufs_in == NULL) { - return luaL_error(L, "no memory"); - } - - u->buf_in = u->bufs_in; - u->buffer = *u->buf_in->buf; - } - - u->length = (size_t) bytes; - u->rest = u->length; - - if (u->raw_downstream || u->body_downstream) { - r->read_event_handler = ngx_http_lua_req_socket_rev_handler; - } - - u->read_waiting = 0; - u->read_co_ctx = NULL; - - rc = ngx_http_lua_socket_tcp_read(r, u); - - if (rc == NGX_ERROR) { - dd("read failed: %d", (int) u->ft_type); - rc = ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); - dd("tcp receive retval returned: %d", (int) rc); - return rc; - } - - if (rc == NGX_OK) { - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket receive done in a single run"); - - return ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); - } - - /* rc == NGX_AGAIN */ - - coctx = ctx->cur_co_ctx; - - u->read_event_handler = ngx_http_lua_socket_read_handler; - - ngx_http_lua_cleanup_pending_operation(coctx); - coctx->cleanup = ngx_http_lua_coctx_cleanup; - coctx->data = u; - - if (ctx->entered_content_phase) { - r->write_event_handler = ngx_http_lua_content_wev_handler; - - } else { - r->write_event_handler = ngx_http_core_run_phases; - } - - u->read_co_ctx = coctx; - u->read_waiting = 1; - u->read_prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler; - - dd("setting data to %p", u); - - if (u->raw_downstream || u->body_downstream) { - ctx->downstream = u; - } - - return lua_yield(L, 0); -} - - -static ngx_int_t -ngx_http_lua_socket_compile_pattern(u_char *data, size_t len, - ngx_http_lua_socket_compiled_pattern_t *cp, ngx_log_t *log) -{ - size_t i; - size_t prefix_len; - size_t size; - unsigned found; - int cur_state, new_state; - - ngx_http_lua_dfa_edge_t *edge; - ngx_http_lua_dfa_edge_t **last = NULL; - - cp->pattern.len = len; - - if (len <= 2) { - return NGX_OK; - } - - for (i = 1; i < len; i++) { - prefix_len = 1; - - while (prefix_len <= len - i - 1) { - - if (ngx_memcmp(data, &data[i], prefix_len) == 0) { - if (data[prefix_len] == data[i + prefix_len]) { - prefix_len++; - continue; - } - - cur_state = i + prefix_len; - new_state = prefix_len + 1; - - if (cp->recovering == NULL) { - size = sizeof(void *) * (len - 2); - cp->recovering = ngx_alloc(size, log); - if (cp->recovering == NULL) { - return NGX_ERROR; - } - - ngx_memzero(cp->recovering, size); - } - - edge = cp->recovering[cur_state - 2]; - - found = 0; - - if (edge == NULL) { - last = &cp->recovering[cur_state - 2]; - - } else { - - for (; edge; edge = edge->next) { - last = &edge->next; - - if (edge->chr == data[prefix_len]) { - found = 1; - - if (edge->new_state < new_state) { - edge->new_state = new_state; - } - - break; - } - } - } - - if (!found) { - ngx_log_debug7(NGX_LOG_DEBUG_HTTP, log, 0, - "lua tcp socket read until recovering point:" - " on state %d (%*s), if next is '%c', then " - "recover to state %d (%*s)", cur_state, - (size_t) cur_state, data, data[prefix_len], - new_state, (size_t) new_state, data); - - edge = ngx_alloc(sizeof(ngx_http_lua_dfa_edge_t), log); - if (edge == NULL) { - return NGX_ERROR; - } - - edge->chr = data[prefix_len]; - edge->new_state = new_state; - edge->next = NULL; - - *last = edge; - } - - break; - } - - break; - } - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_lua_socket_read_until(void *data, ssize_t bytes) -{ - ngx_http_lua_socket_compiled_pattern_t *cp = data; - - ngx_http_lua_socket_tcp_upstream_t *u; - ngx_http_request_t *r; - ngx_buf_t *b; - u_char c; - u_char *pat; - size_t pat_len; - int i; - int state; - int old_state = 0; /* just to make old - gcc happy */ - ngx_http_lua_dfa_edge_t *edge; - unsigned matched; - ngx_int_t rc; - - u = cp->upstream; - r = u->request; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket read until"); - - if (bytes == 0) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; - return NGX_ERROR; - } - - b = &u->buffer; - - pat = cp->pattern.data; - pat_len = cp->pattern.len; - state = cp->state; - - i = 0; - while (i < bytes) { - c = b->pos[i]; - - dd("%d: read char %d, state: %d", i, c, state); - - if (c == pat[state]) { - i++; - state++; - - if (state == (int) pat_len) { - /* already matched the whole pattern */ - dd("pat len: %d", (int) pat_len); - - b->pos += i; - - if (u->length) { - cp->state = -1; - - } else { - cp->state = 0; - } - - if (cp->inclusive) { - rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, 0, - pat, state, - state); - - if (rc != NGX_OK) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; - return NGX_ERROR; - } - } - - return NGX_OK; - } - - continue; - } - - if (state == 0) { - u->buf_in->buf->last++; - - i++; - - if (u->length && --u->rest == 0) { - cp->state = state; - b->pos += i; - return NGX_OK; - } - - continue; - } - - matched = 0; - - if (cp->recovering && state >= 2) { - dd("accessing state: %d, index: %d", state, state - 2); - for (edge = cp->recovering[state - 2]; edge; edge = edge->next) { - - if (edge->chr == c) { - dd("matched '%c' and jumping to state %d", c, - edge->new_state); - - old_state = state; - state = edge->new_state; - matched = 1; - break; - } - } - } - - if (!matched) { -#if 1 - dd("adding pending data: %.*s", state, pat); - rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, i, pat, - state, state); - - if (rc != NGX_OK) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; - return NGX_ERROR; - } - -#endif - - if (u->length) { - if (u->rest <= (size_t) state) { - u->rest = 0; - cp->state = 0; - b->pos += i; - return NGX_OK; - - } else { - u->rest -= state; - } - } - - state = 0; - continue; - } - - /* matched */ - - dd("adding pending data: %.*s", (int) (old_state + 1 - state), - (char *) pat); - - rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, i, pat, - old_state + 1 - state, - old_state); - - if (rc != NGX_OK) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; - return NGX_ERROR; - } - - i++; - - if (u->length) { - if (u->rest <= (size_t) state) { - u->rest = 0; - cp->state = state; - b->pos += i; - return NGX_OK; - - } else { - u->rest -= state; - } - } - - continue; - } - - b->pos += i; - cp->state = state; - - return NGX_AGAIN; -} - - -static int -ngx_http_lua_socket_cleanup_compiled_pattern(lua_State *L) -{ - ngx_http_lua_socket_compiled_pattern_t *cp; - - ngx_http_lua_dfa_edge_t *edge, *p; - unsigned i; - - dd("cleanup compiled pattern"); - - cp = lua_touserdata(L, 1); - if (cp == NULL || cp->recovering == NULL) { - return 0; - } - - dd("pattern len: %d", (int) cp->pattern.len); - - for (i = 0; i < cp->pattern.len - 2; i++) { - edge = cp->recovering[i]; - - while (edge) { - p = edge; - edge = edge->next; - - dd("freeing edge %p", p); - - ngx_free(p); - - dd("edge: %p", edge); - } - } - -#if 1 - ngx_free(cp->recovering); - cp->recovering = NULL; -#endif - - return 0; -} - -static int -ngx_http_lua_socket_req_getfd(lua_State *L) -{ - ngx_connection_t *c; - ngx_http_request_t *r; - - r = ngx_http_lua_get_req(L); - c = r->connection; - if(c == NULL){ - return luaL_error(L, "unknown connection"); - } - lua_pushinteger(L,(int) c->fd); - - return 1; -} - - -static int -ngx_http_lua_req_socket(lua_State *L) -{ - int n, raw; - ngx_peer_connection_t *pc; - ngx_http_lua_loc_conf_t *llcf; - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_lua_ctx_t *ctx; - ngx_http_request_body_t *rb; - ngx_http_cleanup_t *cln; - ngx_http_lua_co_ctx_t *coctx; - - ngx_http_lua_socket_tcp_upstream_t *u; - - n = lua_gettop(L); - if (n == 0) { - raw = 0; - - } else if (n == 1) { - raw = lua_toboolean(L, 1); - lua_pop(L, 1); - - } else { - return luaL_error(L, "expecting zero arguments, but got %d", - lua_gettop(L)); - } - - r = ngx_http_lua_get_req(L); - - if (r != r->main) { - return luaL_error(L, "attempt to read the request body in a " - "subrequest"); - } - -#if (NGX_HTTP_SPDY) - if (r->spdy_stream) { - return luaL_error(L, "spdy not supported yet"); - } -#endif - -#if (NGX_HTTP_V2) - if (r->stream) { - return luaL_error(L, "http v2 not supported yet"); - } -#endif - -#if nginx_version >= 1003009 - if (!raw && r->headers_in.chunked) { - lua_pushnil(L); - lua_pushliteral(L, "chunked request bodies not supported yet"); - return 2; - } -#endif - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - return luaL_error(L, "no ctx found"); - } - - ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE - | NGX_HTTP_LUA_CONTEXT_ACCESS - | NGX_HTTP_LUA_CONTEXT_CONTENT); - - c = r->connection; - - if (raw) { -#if !defined(nginx_version) || nginx_version < 1003013 - lua_pushnil(L); - lua_pushliteral(L, "nginx version too old"); - return 2; -#else - if (r->request_body) { - if (r->request_body->rest > 0) { - lua_pushnil(L); - lua_pushliteral(L, "pending request body reading in some " - "other thread"); - return 2; - } - - } else { - rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); - if (rb == NULL) { - return luaL_error(L, "no memory"); - } - r->request_body = rb; - } - - if (c->buffered & NGX_HTTP_LOWLEVEL_BUFFERED) { - lua_pushnil(L); - lua_pushliteral(L, "pending data to write"); - return 2; - } - - if (ctx->buffering) { - lua_pushnil(L); - lua_pushliteral(L, "http 1.0 buffering"); - return 2; - } - - if (!r->header_sent) { - /* prevent other parts of nginx from sending out - * the response header */ - r->header_sent = 1; - } - - ctx->header_sent = 1; - - dd("ctx acquired raw req socket: %d", ctx->acquired_raw_req_socket); - - if (ctx->acquired_raw_req_socket) { - lua_pushnil(L); - lua_pushliteral(L, "duplicate call"); - return 2; - } - - ctx->acquired_raw_req_socket = 1; - r->keepalive = 0; - r->lingering_close = 1; -#endif - - } else { - /* request body reader */ - - if (r->request_body) { - lua_pushnil(L); - lua_pushliteral(L, "request body already exists"); - return 2; - } - - if (r->discard_body) { - lua_pushnil(L); - lua_pushliteral(L, "request body discarded"); - return 2; - } - - dd("req content length: %d", (int) r->headers_in.content_length_n); - - if (r->headers_in.content_length_n <= 0) { - lua_pushnil(L); - lua_pushliteral(L, "no body"); - return 2; - } - - if (ngx_http_lua_test_expect(r) != NGX_OK) { - lua_pushnil(L); - lua_pushliteral(L, "test expect failed"); - return 2; - } - - /* prevent other request body reader from running */ - - rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); - if (rb == NULL) { - return luaL_error(L, "no memory"); - } - - rb->rest = r->headers_in.content_length_n; - - r->request_body = rb; - } - - lua_createtable(L, 2 /* narr */, 3 /* nrec */); /* the object */ - - if (raw) { - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - raw_req_socket_metatable_key)); - - } else { - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - req_socket_metatable_key)); - } - - lua_rawget(L, LUA_REGISTRYINDEX); - lua_setmetatable(L, -2); - - u = lua_newuserdata(L, sizeof(ngx_http_lua_socket_tcp_upstream_t)); - if (u == NULL) { - return luaL_error(L, "no memory"); - } - -#if 1 - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - downstream_udata_metatable_key)); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_setmetatable(L, -2); -#endif - - lua_rawseti(L, 1, SOCKET_CTX_INDEX); - - ngx_memzero(u, sizeof(ngx_http_lua_socket_tcp_upstream_t)); - - if (raw) { - u->raw_downstream = 1; - - } else { - u->body_downstream = 1; - } - - coctx = ctx->cur_co_ctx; - - u->request = r; - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - u->conf = llcf; - - u->read_timeout = u->conf->read_timeout; - u->connect_timeout = u->conf->connect_timeout; - u->send_timeout = u->conf->send_timeout; - - cln = ngx_http_lua_cleanup_add(r, 0); - if (cln == NULL) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; - lua_pushnil(L); - lua_pushliteral(L, "no memory"); - return 2; - } - - cln->handler = ngx_http_lua_socket_tcp_cleanup; - cln->data = u; - u->cleanup = &cln->handler; - - pc = &u->peer; - - pc->log = c->log; - pc->log_error = NGX_ERROR_ERR; - - pc->connection = c; - - dd("setting data to %p", u); - - coctx->data = u; - ctx->downstream = u; - - if (c->read->timer_set) { - ngx_del_timer(c->read); - } - - if (raw) { - if (c->write->timer_set) { - ngx_del_timer(c->write); - } - } - - lua_settop(L, 1); - return 1; -} - - -static void -ngx_http_lua_req_socket_rev_handler(ngx_http_request_t *r) -{ - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_socket_tcp_upstream_t *u; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua request socket read event handler"); - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - r->read_event_handler = ngx_http_block_reading; - return; - } - - u = ctx->downstream; - if (u == NULL || u->peer.connection == NULL) { - r->read_event_handler = ngx_http_block_reading; - return; - } - - u->read_event_handler(r, u); -} - -static int -ngx_http_lua_socket_tcp_getreusedtimes(lua_State *L) -{ - ngx_http_lua_socket_tcp_upstream_t *u; - - if (lua_gettop(L) != 1) { - return luaL_error(L, "expecting 1 argument " - "(including the object), but got %d", lua_gettop(L)); - } - - luaL_checktype(L, 1, LUA_TTABLE); - - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); - - if (u == NULL - || u->peer.connection == NULL - || (u->read_closed && u->write_closed)) - { - lua_pushnil(L); - lua_pushliteral(L, "closed"); - return 2; - } - - lua_pushinteger(L, u->reused); - return 1; -} - - -static int -ngx_http_lua_socket_tcp_setkeepalive(lua_State *L) -{ - ngx_http_lua_loc_conf_t *llcf; - ngx_http_lua_socket_tcp_upstream_t *u; - ngx_connection_t *c; - ngx_http_lua_socket_pool_t *spool; - ngx_str_t key; - ngx_queue_t *q; - ngx_peer_connection_t *pc; - ngx_http_request_t *r; - ngx_msec_t timeout; - ngx_int_t pool_size; - int n; - ngx_int_t rc; - ngx_buf_t *b; - const char *msg; - - ngx_http_lua_socket_pool_item_t *item; - - n = lua_gettop(L); - - if (n < 1 || n > 3) { - return luaL_error(L, "expecting 1 to 3 arguments " - "(including the object), but got %d", n); - } - - luaL_checktype(L, 1, LUA_TTABLE); - - lua_rawgeti(L, 1, SOCKET_CTX_INDEX); - u = lua_touserdata(L, -1); - lua_pop(L, 1); - - if (u == NULL) { - lua_pushnil(L); - lua_pushliteral(L, "closed"); - return 2; - } - - /* stack: obj timeout? size? */ - - pc = &u->peer; - c = pc->connection; - - if (c == NULL || u->read_closed || u->write_closed) { - lua_pushnil(L); - lua_pushliteral(L, "closed"); - return 2; - } - - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - - if (u->request != r) { - return luaL_error(L, "bad request"); - } - - ngx_http_lua_socket_check_busy_connecting(r, u, L); - ngx_http_lua_socket_check_busy_reading(r, u, L); - ngx_http_lua_socket_check_busy_writing(r, u, L); - - b = &u->buffer; - - if (b->start && ngx_buf_size(b)) { - ngx_http_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, b->pos, - b->last - b->pos); - - lua_pushnil(L); - lua_pushliteral(L, "unread data in buffer"); - return 2; - } - - if (c->read->eof - || c->read->error - || c->read->timedout - || c->write->error - || c->write->timedout) - { - lua_pushnil(L); - lua_pushliteral(L, "invalid connection"); - return 2; - } - - if (ngx_handle_read_event(c->read, 0) != NGX_OK) { - lua_pushnil(L); - lua_pushliteral(L, "failed to handle read event"); - return 2; - } - - if (ngx_terminate || ngx_exiting) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "lua tcp socket set keepalive while process exiting, " - "closing connection %p", c); - - ngx_http_lua_socket_tcp_finalize(r, u); - lua_pushinteger(L, 1); - return 1; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "lua tcp socket set keepalive: saving connection %p", c); - - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(socket_pool_key)); - lua_rawget(L, LUA_REGISTRYINDEX); - - /* stack: obj timeout? size? pools */ - - lua_rawgeti(L, 1, SOCKET_KEY_INDEX); - key.data = (u_char *) lua_tolstring(L, -1, &key.len); - if (key.data == NULL) { - lua_pushnil(L); - lua_pushliteral(L, "key not found"); - return 2; - } - - dd("saving connection to key %s", lua_tostring(L, -1)); - - lua_pushvalue(L, -1); - lua_rawget(L, -3); - spool = lua_touserdata(L, -1); - lua_pop(L, 1); - - /* stack: obj timeout? size? pools cache_key */ - - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - - if (spool == NULL) { - /* create a new socket pool for the current peer key */ - - if (n >= 3 && !lua_isnil(L, 3)) { - pool_size = luaL_checkinteger(L, 3); - - } else { - pool_size = llcf->pool_size; - } - - if (pool_size <= 0) { - msg = lua_pushfstring(L, "bad \"pool_size\" option value: %i", - pool_size); - return luaL_argerror(L, n, msg); - } - - ngx_http_lua_socket_tcp_create_socket_pool(L, r, key, pool_size, -1, - &spool); - } - - if (ngx_queue_empty(&spool->free)) { - - q = ngx_queue_last(&spool->cache); - ngx_queue_remove(q); - - item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue); - - ngx_http_lua_socket_tcp_close_connection(item->connection); - - /* only decrease the counter for connections which were counted */ - if (u->socket_pool != NULL) { - u->socket_pool->connections--; - } - - } else { - q = ngx_queue_head(&spool->free); - ngx_queue_remove(q); - - item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue); - - /* we should always increase connections after getting connected, - * and decrease connections after getting closed. - * however, we don't create connection pool in previous connect method. - * so we increase connections here for backward compatibility. - */ - if (u->socket_pool == NULL) { - spool->connections++; - } - } - - item->connection = c; - ngx_queue_insert_head(&spool->cache, q); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "lua tcp socket clear current socket connection"); - - pc->connection = NULL; - -#if 0 - if (u->cleanup) { - *u->cleanup = NULL; - u->cleanup = NULL; - } -#endif - - if (c->read->timer_set) { - ngx_del_timer(c->read); - } - - if (c->write->timer_set) { - ngx_del_timer(c->write); - } - - if (n >= 2 && !lua_isnil(L, 2)) { - timeout = (ngx_msec_t) luaL_checkinteger(L, 2); - - } else { - timeout = llcf->keepalive_timeout; - } - -#if (NGX_DEBUG) - if (timeout == 0) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket keepalive timeout: unlimited"); - } -#endif - - if (timeout) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket keepalive timeout: %M ms", timeout); - - ngx_add_timer(c->read, timeout); - } - - c->write->handler = ngx_http_lua_socket_keepalive_dummy_handler; - c->read->handler = ngx_http_lua_socket_keepalive_rev_handler; - - c->data = item; - c->idle = 1; - c->log = ngx_cycle->log; - c->pool->log = ngx_cycle->log; - c->read->log = ngx_cycle->log; - c->write->log = ngx_cycle->log; - - item->socklen = pc->socklen; - ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen); - item->reused = u->reused; - - if (c->read->ready) { - rc = ngx_http_lua_socket_keepalive_close_handler(c->read); - if (rc != NGX_OK) { - lua_pushnil(L); - lua_pushliteral(L, "connection in dubious state"); - return 2; - } - } - -#if 1 - ngx_http_lua_socket_tcp_finalize(r, u); -#endif - - /* since we set u->peer->connection to NULL previously, the connect - * operation won't be resumed in the ngx_http_lua_socket_tcp_finalize. - * Therefore we need to resume it here. - */ - ngx_http_lua_socket_tcp_resume_conn_op(spool); - - lua_pushinteger(L, 1); - return 1; -} - - -static ngx_int_t -ngx_http_lua_get_keepalive_peer(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_http_lua_socket_pool_item_t *item; - ngx_http_lua_socket_pool_t *spool; - ngx_http_cleanup_t *cln; - ngx_queue_t *q; - ngx_peer_connection_t *pc; - ngx_connection_t *c; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket pool get keepalive peer"); - - pc = &u->peer; - spool = u->socket_pool; - - if (!ngx_queue_empty(&spool->cache)) { - q = ngx_queue_head(&spool->cache); - - item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue); - c = item->connection; - - ngx_queue_remove(q); - ngx_queue_insert_head(&spool->free, q); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "lua tcp socket get keepalive peer: using connection %p," - " fd:%d", c, c->fd); - - c->idle = 0; - c->log = pc->log; - c->pool->log = pc->log; - c->read->log = pc->log; - c->write->log = pc->log; - c->data = u; - -#if 1 - c->write->handler = ngx_http_lua_socket_tcp_handler; - c->read->handler = ngx_http_lua_socket_tcp_handler; -#endif - - if (c->read->timer_set) { - ngx_del_timer(c->read); - } - - pc->connection = c; - pc->cached = 1; - - u->reused = item->reused + 1; - -#if 1 - u->write_event_handler = ngx_http_lua_socket_dummy_handler; - u->read_event_handler = ngx_http_lua_socket_dummy_handler; -#endif - - if (u->cleanup == NULL) { - cln = ngx_http_lua_cleanup_add(r, 0); - if (cln == NULL) { - u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; - return NGX_ERROR; - } - - cln->handler = ngx_http_lua_socket_tcp_cleanup; - cln->data = u; - u->cleanup = &cln->handler; - } - - return NGX_OK; - } - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, - "lua tcp socket keepalive: connection pool empty"); - - return NGX_DECLINED; -} - - -static void -ngx_http_lua_socket_keepalive_dummy_handler(ngx_event_t *ev) -{ - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, - "keepalive dummy handler"); -} - - -static void -ngx_http_lua_socket_keepalive_rev_handler(ngx_event_t *ev) -{ - (void) ngx_http_lua_socket_keepalive_close_handler(ev); -} - - -static ngx_int_t -ngx_http_lua_socket_keepalive_close_handler(ngx_event_t *ev) -{ - ngx_http_lua_socket_pool_item_t *item; - ngx_http_lua_socket_pool_t *spool; - - int n; - char buf[1]; - ngx_connection_t *c; - - c = ev->data; - - if (c->close) { - goto close; - } - - if (c->read->timedout) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, - "lua tcp socket keepalive max idle timeout"); - - goto close; - } - - dd("read event ready: %d", (int) c->read->ready); - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, - "lua tcp socket keepalive close handler check stale events"); - - n = recv(c->fd, buf, 1, MSG_PEEK); - - if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { - /* stale event */ - - if (ngx_handle_read_event(c->read, 0) != NGX_OK) { - goto close; - } - - return NGX_OK; - } - -close: - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, - "lua tcp socket keepalive close handler: fd:%d", c->fd); - - item = c->data; - spool = item->socket_pool; - - ngx_http_lua_socket_tcp_close_connection(c); - - ngx_queue_remove(&item->queue); - ngx_queue_insert_head(&spool->free, &item->queue); - spool->connections--; - - dd("keepalive: connections: %u", (unsigned) spool->connections); - - if (spool->connections == 0) { - ngx_http_lua_socket_free_pool(ev->log, spool); - - } else { - ngx_http_lua_socket_tcp_resume_conn_op(spool); - } - - return NGX_DECLINED; -} - - -static void -ngx_http_lua_socket_free_pool(ngx_log_t *log, ngx_http_lua_socket_pool_t *spool) -{ - lua_State *L; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, - "lua tcp socket keepalive: free connection pool for \"%s\"", - spool->key); - - L = spool->lua_vm; - - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(socket_pool_key)); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_pushstring(L, (char *) spool->key); - lua_pushnil(L); - lua_rawset(L, -3); - lua_pop(L, 1); -} - - -static void -ngx_http_lua_socket_shutdown_pool_helper(ngx_http_lua_socket_pool_t *spool) -{ - ngx_queue_t *q; - ngx_connection_t *c; - ngx_http_lua_socket_pool_item_t *item; - ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; - - while (!ngx_queue_empty(&spool->cache)) { - q = ngx_queue_head(&spool->cache); - - item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue); - c = item->connection; - - ngx_http_lua_socket_tcp_close_connection(c); - - ngx_queue_remove(q); - ngx_queue_insert_head(&spool->free, q); - } - - while (!ngx_queue_empty(&spool->cache_connect_op)) { - q = ngx_queue_head(&spool->cache_connect_op); - ngx_queue_remove(q); - conn_op_ctx = ngx_queue_data(q, ngx_http_lua_socket_tcp_conn_op_ctx_t, - queue); - ngx_http_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx); - } - - while (!ngx_queue_empty(&spool->wait_connect_op)) { - q = ngx_queue_head(&spool->wait_connect_op); - ngx_queue_remove(q); - conn_op_ctx = ngx_queue_data(q, ngx_http_lua_socket_tcp_conn_op_ctx_t, - queue); - - if (conn_op_ctx->event.timer_set) { - ngx_del_timer(&conn_op_ctx->event); - } - - ngx_http_lua_socket_tcp_free_conn_op_ctx(conn_op_ctx); - } - - /* spool->connections will be decreased down to zero in - * ngx_http_lua_socket_tcp_finalize */ -} - - -static int -ngx_http_lua_socket_shutdown_pool(lua_State *L) -{ - ngx_http_lua_socket_pool_t *spool; - - spool = lua_touserdata(L, 1); - - if (spool != NULL) { - ngx_http_lua_socket_shutdown_pool_helper(spool); - } - - return 0; -} - - -static int -ngx_http_lua_socket_tcp_upstream_destroy(lua_State *L) -{ - ngx_http_lua_socket_tcp_upstream_t *u; - - dd("upstream destroy triggered by Lua GC"); - - u = lua_touserdata(L, 1); - if (u == NULL) { - return 0; - } - - if (u->cleanup) { - ngx_http_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */ - } - - return 0; -} - - -static int -ngx_http_lua_socket_downstream_destroy(lua_State *L) -{ - ngx_http_lua_socket_tcp_upstream_t *u; - - dd("downstream destroy"); - - u = lua_touserdata(L, 1); - if (u == NULL) { - dd("u is NULL"); - return 0; - } - - if (u->cleanup) { - ngx_http_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */ - } - - return 0; -} - - -static ngx_int_t -ngx_http_lua_socket_push_input_data(ngx_http_request_t *r, - ngx_http_lua_ctx_t *ctx, ngx_http_lua_socket_tcp_upstream_t *u, - lua_State *L) -{ - ngx_chain_t *cl; - ngx_chain_t **ll; -#if (DDEBUG) || (NGX_DTRACE) - size_t size = 0; -#endif - size_t chunk_size; - ngx_buf_t *b; - size_t nbufs; - luaL_Buffer luabuf; - - dd("bufs_in: %p, buf_in: %p", u->bufs_in, u->buf_in); - - nbufs = 0; - ll = NULL; - - luaL_buffinit(L, &luabuf); - - for (cl = u->bufs_in; cl; cl = cl->next) { - b = cl->buf; - chunk_size = b->last - b->pos; - - dd("copying input data chunk from %p: \"%.*s\"", cl, - (int) chunk_size, b->pos); - - luaL_addlstring(&luabuf, (char *) b->pos, chunk_size); - - if (cl->next) { - ll = &cl->next; - } - -#if (DDEBUG) || (NGX_DTRACE) - size += chunk_size; -#endif - - nbufs++; - } - - luaL_pushresult(&luabuf); - -#if (DDEBUG) - dd("size: %d, nbufs: %d", (int) size, (int) nbufs); -#endif - -#if (NGX_DTRACE) - ngx_http_lua_probe_socket_tcp_receive_done(r, u, - (u_char *) lua_tostring(L, -1), - size); -#endif - - if (nbufs > 1 && ll) { - dd("recycle buffers: %d", (int) (nbufs - 1)); - - *ll = ctx->free_recv_bufs; - ctx->free_recv_bufs = u->bufs_in; - u->bufs_in = u->buf_in; - } - - if (u->buffer.pos == u->buffer.last) { - dd("resetting u->buffer pos & last"); - u->buffer.pos = u->buffer.start; - u->buffer.last = u->buffer.start; - } - - if (u->bufs_in) { - u->buf_in->buf->last = u->buffer.pos; - u->buf_in->buf->pos = u->buffer.pos; - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_lua_socket_add_input_buffer(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u) -{ - ngx_chain_t *cl; - ngx_http_lua_ctx_t *ctx; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, - &ctx->free_recv_bufs, - u->conf->buffer_size); - - if (cl == NULL) { - return NGX_ERROR; - } - - u->buf_in->next = cl; - u->buf_in = cl; - u->buffer = *cl->buf; - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_lua_socket_add_pending_data(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, u_char *pos, size_t len, u_char *pat, - int prefix, int old_state) -{ - u_char *last; - ngx_buf_t *b; - - dd("resuming data: %d: [%.*s]", prefix, prefix, pat); - - last = &pos[len]; - - b = u->buf_in->buf; - - if (last - b->last == old_state) { - b->last += prefix; - return NGX_OK; - } - - dd("need more buffers because %d != %d", (int) (last - b->last), - (int) old_state); - - if (ngx_http_lua_socket_insert_buffer(r, u, pat, prefix) != NGX_OK) { - return NGX_ERROR; - } - - b->pos = last; - b->last = last; - - return NGX_OK; -} - - -static ngx_int_t ngx_http_lua_socket_insert_buffer(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u, u_char *pat, size_t prefix) -{ - ngx_chain_t *cl, *new_cl, **ll; - ngx_http_lua_ctx_t *ctx; - size_t size; - ngx_buf_t *b; - - if (prefix <= u->conf->buffer_size) { - size = u->conf->buffer_size; - - } else { - size = prefix; - } - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - - new_cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, - &ctx->free_recv_bufs, - size); - - if (new_cl == NULL) { - return NGX_ERROR; - } - - b = new_cl->buf; - - b->last = ngx_copy(b->last, pat, prefix); - - dd("copy resumed data to %p: %d: \"%.*s\"", - new_cl, (int) (b->last - b->pos), (int) (b->last - b->pos), b->pos); - - dd("before resuming data: bufs_in %p, buf_in %p, buf_in next %p", - u->bufs_in, u->buf_in, u->buf_in->next); - - ll = &u->bufs_in; - for (cl = u->bufs_in; cl->next; cl = cl->next) { - ll = &cl->next; - } - - *ll = new_cl; - new_cl->next = u->buf_in; - - dd("after resuming data: bufs_in %p, buf_in %p, buf_in next %p", - u->bufs_in, u->buf_in, u->buf_in->next); - -#if (DDEBUG) - for (cl = u->bufs_in; cl; cl = cl->next) { - b = cl->buf; - - dd("result buf after resuming data: %p: %.*s", cl, - (int) ngx_buf_size(b), b->pos); - } -#endif - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_lua_socket_tcp_conn_op_resume(ngx_http_request_t *r) -{ - return ngx_http_lua_socket_tcp_resume_helper(r, SOCKET_OP_RESUME_CONN); -} - - -static ngx_int_t -ngx_http_lua_socket_tcp_conn_resume(ngx_http_request_t *r) -{ - return ngx_http_lua_socket_tcp_resume_helper(r, SOCKET_OP_CONNECT); -} - - -static ngx_int_t -ngx_http_lua_socket_tcp_read_resume(ngx_http_request_t *r) -{ - return ngx_http_lua_socket_tcp_resume_helper(r, SOCKET_OP_READ); -} - - -static ngx_int_t -ngx_http_lua_socket_tcp_write_resume(ngx_http_request_t *r) -{ - return ngx_http_lua_socket_tcp_resume_helper(r, SOCKET_OP_WRITE); -} - - -static ngx_int_t -ngx_http_lua_socket_tcp_resume_helper(ngx_http_request_t *r, int socket_op) -{ - int nret; - lua_State *vm; - ngx_int_t rc; - ngx_uint_t nreqs; - ngx_connection_t *c; - ngx_http_lua_ctx_t *ctx; - ngx_http_lua_co_ctx_t *coctx; - ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; - - ngx_http_lua_socket_tcp_retval_handler prepare_retvals; - - ngx_http_lua_socket_tcp_upstream_t *u; - - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - if (ctx == NULL) { - return NGX_ERROR; - } - - ctx->resume_handler = ngx_http_lua_wev_handler; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp operation done, resuming lua thread"); - - coctx = ctx->cur_co_ctx; - - dd("coctx: %p", coctx); - - switch (socket_op) { - - case SOCKET_OP_RESUME_CONN: - conn_op_ctx = coctx->data; - u = conn_op_ctx->u; - prepare_retvals = u->write_prepare_retvals; - break; - - case SOCKET_OP_CONNECT: - case SOCKET_OP_WRITE: - u = coctx->data; - prepare_retvals = u->write_prepare_retvals; - break; - - case SOCKET_OP_READ: - u = coctx->data; - prepare_retvals = u->read_prepare_retvals; - break; - - default: - /* impossible to reach here */ - return NGX_ERROR; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua tcp socket calling prepare retvals handler %p, " - "u:%p", prepare_retvals, u); - - nret = prepare_retvals(r, u, ctx->cur_co_ctx->co); - if (socket_op == SOCKET_OP_CONNECT - && nret > 1 - && !u->conn_closed - && u->socket_pool != NULL) - { - u->socket_pool->connections--; - ngx_http_lua_socket_tcp_resume_conn_op(u->socket_pool); - } - - if (nret == NGX_AGAIN) { - return NGX_DONE; - } - - c = r->connection; - vm = ngx_http_lua_get_lua_vm(r, ctx); - nreqs = c->requests; - - rc = ngx_http_lua_run_thread(vm, r, ctx, nret); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "lua run thread returned %d", rc); - - if (rc == NGX_AGAIN) { - return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); - } - - if (rc == NGX_DONE) { - ngx_http_lua_finalize_request(r, NGX_DONE); - return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); - } - - if (ctx->entered_content_phase) { - ngx_http_lua_finalize_request(r, rc); - return NGX_DONE; - } - - return rc; -} - - -static void -ngx_http_lua_tcp_queue_conn_op_cleanup(void *data) -{ - ngx_http_lua_co_ctx_t *coctx = data; - ngx_http_lua_socket_tcp_upstream_t *u; - ngx_http_lua_socket_tcp_conn_op_ctx_t *conn_op_ctx; - - conn_op_ctx = coctx->data; - u = conn_op_ctx->u; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, - "lua tcp socket abort queueing, conn_op_ctx: %p, u: %p", - conn_op_ctx, u); - - if (conn_op_ctx->event.posted) { - ngx_delete_posted_event(&conn_op_ctx->event); - - } else if (conn_op_ctx->event.timer_set) { - ngx_del_timer(&conn_op_ctx->event); - } - - ngx_queue_remove(&conn_op_ctx->queue); - ngx_queue_insert_head(&u->socket_pool->cache_connect_op, - &conn_op_ctx->queue); - - u->socket_pool->connections--; - ngx_http_lua_socket_tcp_resume_conn_op(u->socket_pool); -} - - -static void -ngx_http_lua_tcp_resolve_cleanup(void *data) -{ - ngx_resolver_ctx_t *rctx; - ngx_http_lua_socket_tcp_upstream_t *u; - ngx_http_lua_co_ctx_t *coctx = data; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, - "lua tcp socket abort resolver"); - - u = coctx->data; - if (u == NULL) { - return; - } - - if (u->socket_pool != NULL) { - u->socket_pool->connections--; - ngx_http_lua_socket_tcp_resume_conn_op(u->socket_pool); - } - - rctx = u->resolved->ctx; - if (rctx == NULL) { - return; - } - - /* just to be safer */ - rctx->handler = ngx_http_lua_socket_empty_resolve_handler; - - ngx_resolve_name_done(rctx); -} - - -static void -ngx_http_lua_coctx_cleanup(void *data) -{ - ngx_http_lua_socket_tcp_upstream_t *u; - ngx_http_lua_co_ctx_t *coctx = data; - - dd("running coctx cleanup"); - - u = coctx->data; - if (u == NULL) { - return; - } - - if (u->request == NULL) { - return; - } - - ngx_http_lua_socket_tcp_finalize(u->request, u); -} - - -#if (NGX_HTTP_SSL) - -static int -ngx_http_lua_ssl_free_session(lua_State *L) -{ - ngx_ssl_session_t **psession; - - psession = lua_touserdata(L, 1); - if (psession && *psession != NULL) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, - "lua ssl free session: %p", *psession); - - ngx_ssl_free_session(*psession); - } - - return 0; -} - -#endif /* NGX_HTTP_SSL */ - - -void -ngx_http_lua_cleanup_conn_pools(lua_State *L) -{ - ngx_http_lua_socket_pool_t *spool; - - lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( - socket_pool_key)); - lua_rawget(L, LUA_REGISTRYINDEX); /* table */ - - lua_pushnil(L); /* first key */ - while (lua_next(L, -2) != 0) { - /* tb key val */ - spool = lua_touserdata(L, -1); - - if (spool != NULL) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, - "lua tcp socket keepalive: free connection pool %p " - "for \"%s\"", spool, spool->key); - - ngx_http_lua_socket_shutdown_pool_helper(spool); - } - - lua_pop(L, 1); - } - - lua_pop(L, 1); -} - -/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ From 80d108c639b4346c22390f89b76460e3cbf0df5d Mon Sep 17 00:00:00 2001 From: Mathew Heard Date: Sat, 25 Apr 2020 08:11:31 +1000 Subject: [PATCH 16/16] ssl: cert timer --- src/ngx_http_lua_ssl_client_helloby.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ngx_http_lua_ssl_client_helloby.c b/src/ngx_http_lua_ssl_client_helloby.c index 2413928ffc..44abd98f4e 100644 --- a/src/ngx_http_lua_ssl_client_helloby.c +++ b/src/ngx_http_lua_ssl_client_helloby.c @@ -216,6 +216,12 @@ ngx_http_lua_ssl_client_hello_handler(ngx_ssl_conn_t *ssl_conn, dd("first time"); + // remove hello timer, move to cert timer + if(c->read->timer_set) { + ngx_del_timer(c->read); + ngx_add_timer(c->read, c->listening->ssl_certificate_timeout); + } + ngx_reusable_connection(c, 0); hc = c->data;