From 3f3953599a2de05f82f09cb0efe442d3d58f3e16 Mon Sep 17 00:00:00 2001 From: Aviram Date: Wed, 24 Apr 2013 10:36:55 +0300 Subject: [PATCH 01/18] Added an optional parameter for ngx.req.set_header and ngx.req.clear_header that determines whether or not to replace underscores with hyphens. Previously, underscores were replaced unconditionally. Currently each of the functions has another boolean argument. If it's false, underscores would not be touched. If it's true, they would. The default value of the argument is true. --- src/ngx_http_lua_headers.c | 40 ++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c index 0f30a78ff8..a91a3c9314 100644 --- a/src/ngx_http_lua_headers.c +++ b/src/ngx_http_lua_headers.c @@ -581,12 +581,26 @@ ngx_http_lua_ngx_header_set(lua_State *L) static int ngx_http_lua_ngx_req_header_clear(lua_State *L) { - if (lua_gettop(L) != 1) { - return luaL_error(L, "expecting one arguments, but seen %d", + ngx_uint_t n; + n = lua_gettop(L); + if ((n != 1) && (n != 2)) { + return luaL_error(L, "expecting one or two arguments, but seen %d", lua_gettop(L)); } - lua_pushnil(L); + if (n == 2) { + u_char *p; + size_t len; + int replace_underscores = 1; + p = (u_char *) luaL_checklstring(L, 1, &len); + replace_underscores = lua_toboolean(L, 2); + lua_pop(L, 2); + lua_pushlstring(L, (char *) p, len); + lua_pushnil(L); + lua_pushboolean(L, replace_underscores); + } else { + lua_pushnil(L); + } return ngx_http_lua_ngx_req_header_set_helper(L); } @@ -595,7 +609,7 @@ ngx_http_lua_ngx_req_header_clear(lua_State *L) static int ngx_http_lua_ngx_req_header_set(lua_State *L) { - if (lua_gettop(L) != 2) { + if ((lua_gettop(L) != 2) && (lua_gettop(L) != 3)) { return luaL_error(L, "expecting two arguments, but seen %d", lua_gettop(L)); } @@ -615,6 +629,7 @@ ngx_http_lua_ngx_req_header_set_helper(lua_State *L) size_t len; ngx_int_t rc; ngx_uint_t n; + int replace_underscores = 1; lua_pushlightuserdata(L, &ngx_http_lua_request_key); lua_rawget(L, LUA_GLOBALSINDEX); @@ -627,17 +642,26 @@ ngx_http_lua_ngx_req_header_set_helper(lua_State *L) ngx_http_lua_check_fake_request(L, r); + n = lua_gettop(L); + if (n >= 3) { + replace_underscores = lua_toboolean(L, 3); + } else { + replace_underscores = 1; + } + p = (u_char *) luaL_checklstring(L, 1, &len); dd("key: %.*s, len %d", (int) len, p, (int) len); + if (replace_underscores) { /* replace "_" with "-" */ - for (i = 0; i < len; i++) { - if (p[i] == '_') { - p[i] = '-'; + for (i = 0; i < len; i++) { + if (p[i] == '_') { + p[i] = '-'; + } } } - + key.data = ngx_palloc(r->pool, len + 1); if (key.data == NULL) { return luaL_error(L, "out of memory"); From 1c5e5b9f0d030f66aec123b4cd22c7e23feaf34e Mon Sep 17 00:00:00 2001 From: aviramc Date: Wed, 8 May 2013 13:23:50 +0300 Subject: [PATCH 02/18] Changed the replace_underscores parameter to a table of parameters (options), in which the only possible option currently is replace_underscores. --- src/ngx_http_lua_headers.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c index a91a3c9314..95df6111c8 100644 --- a/src/ngx_http_lua_headers.c +++ b/src/ngx_http_lua_headers.c @@ -643,8 +643,11 @@ ngx_http_lua_ngx_req_header_set_helper(lua_State *L) ngx_http_lua_check_fake_request(L, r); n = lua_gettop(L); - if (n >= 3) { - replace_underscores = lua_toboolean(L, 3); + if (n == 3) { + luaL_checktype(L, 3, LUA_TTABLE); + lua_getfield(L, 3, "replace_underscores"); + replace_underscores = lua_toboolean(L, -1); + lua_pop(L, 1); } else { replace_underscores = 1; } From f2e849a7efc23f86971596110a819d1057aeee37 Mon Sep 17 00:00:00 2001 From: aviramc Date: Wed, 8 May 2013 13:44:32 +0300 Subject: [PATCH 03/18] Added an options table to clean_header as well. --- src/ngx_http_lua_headers.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c index 95df6111c8..36f6a01c5a 100644 --- a/src/ngx_http_lua_headers.c +++ b/src/ngx_http_lua_headers.c @@ -589,15 +589,9 @@ ngx_http_lua_ngx_req_header_clear(lua_State *L) } if (n == 2) { - u_char *p; - size_t len; - int replace_underscores = 1; - p = (u_char *) luaL_checklstring(L, 1, &len); - replace_underscores = lua_toboolean(L, 2); - lua_pop(L, 2); - lua_pushlstring(L, (char *) p, len); lua_pushnil(L); - lua_pushboolean(L, replace_underscores); + /* Top element is now 3, replace it with element 3 */ + lua_insert(L, 2); } else { lua_pushnil(L); } From f602d0fed547ee7aa8f2d82b25761fe80865fb45 Mon Sep 17 00:00:00 2001 From: aviram Date: Sun, 29 Sep 2013 10:53:49 +0200 Subject: [PATCH 04/18] Added ngx.req.set_keepalive and ngx.req.get_keepalive to control the keepalive option for the request. --- config | 2 + src/ngx_http_lua_headers.c | 1 - src/ngx_http_lua_req_keepalive.c | 79 ++++++++++++++++++++++++++++++++ src/ngx_http_lua_req_keepalive.h | 19 ++++++++ src/ngx_http_lua_util.c | 2 + 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 src/ngx_http_lua_req_keepalive.c create mode 100644 src/ngx_http_lua_req_keepalive.h diff --git a/config b/config index 84f717218f..c7c750e549 100644 --- a/config +++ b/config @@ -219,6 +219,7 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ $ngx_addon_dir/src/ngx_http_lua_initby.c \ $ngx_addon_dir/src/ngx_http_lua_socket_udp.c \ $ngx_addon_dir/src/ngx_http_lua_req_method.c \ + $ngx_addon_dir/src/ngx_http_lua_req_keepalive.c \ $ngx_addon_dir/src/ngx_http_lua_phase.c \ $ngx_addon_dir/src/ngx_http_lua_uthread.c \ $ngx_addon_dir/src/ngx_http_lua_timer.c \ @@ -269,6 +270,7 @@ NGX_ADDON_DEPS="$NGX_ADDON_DEPS \ $ngx_addon_dir/src/ngx_http_lua_initby.h \ $ngx_addon_dir/src/ngx_http_lua_socket_udp.h \ $ngx_addon_dir/src/ngx_http_lua_req_method.h \ + $ngx_addon_dir/src/ngx_http_lua_req_keepalive.h \ $ngx_addon_dir/src/ngx_http_lua_phase.h \ $ngx_addon_dir/src/ngx_http_lua_probe.h \ $ngx_addon_dir/src/ngx_http_lua_uthread.h \ diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c index 6960a3c238..3a0226517d 100644 --- a/src/ngx_http_lua_headers.c +++ b/src/ngx_http_lua_headers.c @@ -604,7 +604,6 @@ ngx_http_lua_ngx_req_header_set_helper(lua_State *L) size_t len; ngx_int_t rc; ngx_uint_t n; - int replace_underscores = 1; r = ngx_http_lua_get_req(L); if (r == NULL) { diff --git a/src/ngx_http_lua_req_keepalive.c b/src/ngx_http_lua_req_keepalive.c new file mode 100644 index 0000000000..73aa510cbb --- /dev/null +++ b/src/ngx_http_lua_req_keepalive.c @@ -0,0 +1,79 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif + + +#include "ddebug.h" +#include "ngx_http_lua_req_keepalive.h" +#include "ngx_http_lua_util.h" + + +static int ngx_http_lua_ngx_req_get_keepalive(lua_State *L); +static int ngx_http_lua_ngx_req_set_keepalive(lua_State *L); + + +void +ngx_http_lua_inject_req_keepalive_api(lua_State *L) +{ + lua_pushcfunction(L, ngx_http_lua_ngx_req_get_keepalive); + lua_setfield(L, -2, "get_keepalive"); + + lua_pushcfunction(L, ngx_http_lua_ngx_req_set_keepalive); + lua_setfield(L, -2, "set_keepalive"); +} + + +static int +ngx_http_lua_ngx_req_get_keepalive(lua_State *L) +{ + int n; + ngx_http_request_t *r; + + n = lua_gettop(L); + if (n != 0) { + return luaL_error(L, "no arguments expected but got %d", n); + } + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "request object not found"); + } + + ngx_http_lua_check_fake_request(L, r); + + lua_pushboolean(L, r->keepalive); + return 1; +} + + +static int +ngx_http_lua_ngx_req_set_keepalive(lua_State *L) +{ + int n; + ngx_http_request_t *r; + int keepalive; + + n = lua_gettop(L); + if (n != 1) { + return luaL_error(L, "only one argument expected but got %d", n); + } + + keepalive = lua_toboolean(L, 1); + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "request object not found"); + } + + ngx_http_lua_check_fake_request(L, r); + + r->keepalive = keepalive; + + return 1; +} diff --git a/src/ngx_http_lua_req_keepalive.h b/src/ngx_http_lua_req_keepalive.h new file mode 100644 index 0000000000..9c8da71c55 --- /dev/null +++ b/src/ngx_http_lua_req_keepalive.h @@ -0,0 +1,19 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_HTTP_LUA_KEEPALIVE_H_INCLUDED_ +#define _NGX_HTTP_LUA_KEEPALIVE_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +void ngx_http_lua_inject_req_keepalive_api(lua_State *L); + + +#endif /* _NGX_HTTP_LUA_KEEPALIVE_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 481f95a135..8247e66650 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -32,6 +32,7 @@ #include "ngx_http_lua_misc.h" #include "ngx_http_lua_consts.h" #include "ngx_http_lua_req_method.h" +#include "ngx_http_lua_req_keepalive.h" #include "ngx_http_lua_shdict.h" #include "ngx_http_lua_coroutine.h" #include "ngx_http_lua_socket_tcp.h" @@ -2128,6 +2129,7 @@ ngx_http_lua_inject_req_api(ngx_log_t *log, lua_State *L) ngx_http_lua_inject_req_body_api(L); ngx_http_lua_inject_req_socket_api(L); ngx_http_lua_inject_req_method_api(L); + ngx_http_lua_inject_req_keepalive_api(L); ngx_http_lua_inject_req_time_api(L); lua_setfield(L, -2, "req"); From d4d2988488c5a6758d2e87a47c592d833513329d Mon Sep 17 00:00:00 2001 From: aviram Date: Sun, 29 Sep 2013 10:54:33 +0200 Subject: [PATCH 05/18] Removed unneeded variable. --- src/ngx_http_lua_headers.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c index 6960a3c238..3a0226517d 100644 --- a/src/ngx_http_lua_headers.c +++ b/src/ngx_http_lua_headers.c @@ -604,7 +604,6 @@ ngx_http_lua_ngx_req_header_set_helper(lua_State *L) size_t len; ngx_int_t rc; ngx_uint_t n; - int replace_underscores = 1; r = ngx_http_lua_get_req(L); if (r == NULL) { From ef2ce04ae1d1f73d174a8376e34557fc773cc6ff Mon Sep 17 00:00:00 2001 From: aviram Date: Sun, 29 Sep 2013 12:28:15 +0200 Subject: [PATCH 06/18] Added SSL support for TCP cosockets. --- src/ngx_http_lua_common.h | 7 ++ src/ngx_http_lua_module.c | 106 ++++++++++++++++++++- src/ngx_http_lua_socket_tcp.c | 170 +++++++++++++++++++++++++++++++++- src/ngx_http_lua_socket_tcp.h | 3 + 4 files changed, 281 insertions(+), 5 deletions(-) diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index b03d7bb994..e2d6da0f01 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -201,6 +201,13 @@ typedef struct { ngx_flag_t transform_underscores_in_resp_headers; ngx_flag_t log_socket_errors; ngx_flag_t check_client_abort; + +#if (NGX_HTTP_SSL) + ngx_ssl_t *ssl; + ngx_flag_t ssl_verify; + ngx_uint_t ssl_verify_depth; + ngx_str_t ssl_trusted_certificate; +#endif } ngx_http_lua_loc_conf_t; diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index 163f6d64e2..8ff255a22b 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -34,6 +34,8 @@ static char *ngx_http_lua_init_main_conf(ngx_conf_t *cf, void *conf); static void *ngx_http_lua_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); +static ngx_int_t ngx_http_lua_set_ssl(ngx_conf_t *cf, + ngx_http_lua_loc_conf_t *plcf); static char *ngx_http_lua_init_vm(ngx_conf_t *cf, ngx_http_lua_main_conf_t *lmcf); static void ngx_http_lua_cleanup_vm(void *data); @@ -343,6 +345,30 @@ static ngx_command_t ngx_http_lua_cmds[] = { offsetof(ngx_http_lua_loc_conf_t, check_client_abort), NULL }, +#if (NGX_HTTP_SSL) + + { ngx_string("lua_ssl_verify"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, ssl_verify), + NULL }, + + { ngx_string("lua_ssl_verify_depth"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, ssl_verify_depth), + NULL }, + + { ngx_string("lua_ssl_trusted_certificate"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, ssl_trusted_certificate), + NULL }, + +#endif ngx_null_command }; @@ -610,6 +636,8 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) * conf->body_filter_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->body_filter_src_key = NULL * conf->body_filter_handler = NULL; + * + * conf->ssl_trusted_certificate = NULL; */ conf->force_read_body = NGX_CONF_UNSET; @@ -628,7 +656,13 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) conf->transform_underscores_in_resp_headers = NGX_CONF_UNSET; conf->log_socket_errors = NGX_CONF_UNSET; - +#if (NGX_HTTP_SSL) + + conf->ssl_verify = NGX_CONF_UNSET; + conf->ssl_verify_depth = NGX_CONF_UNSET_UINT; + +#endif + return conf; } @@ -706,10 +740,80 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->log_socket_errors, prev->log_socket_errors, 1); +#if (NGX_HTTP_SSL) + + if (ngx_http_lua_set_ssl(cf, conf) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ngx_conf_merge_value(conf->ssl_verify, + prev->ssl_verify, 0); + ngx_conf_merge_uint_value(conf->ssl_verify_depth, + prev->ssl_verify_depth, 1); + ngx_conf_merge_str_value(conf->ssl_trusted_certificate, + prev->ssl_trusted_certificate, ""); + + if (conf->ssl_verify) { + if (conf->ssl_trusted_certificate.len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "no \"lua_ssl_trusted_certificate\" is " + " defined for the \"lua_ssl_verify\" " + "directive"); + + return NGX_CONF_ERROR; + } + } + + if (ngx_ssl_trusted_certificate(cf, conf->ssl, + &conf->ssl_trusted_certificate, + conf->ssl_verify_depth) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + +#endif + return NGX_CONF_OK; } +#if (NGX_HTTP_SSL) + +static ngx_int_t +ngx_http_lua_set_ssl(ngx_conf_t *cf, ngx_http_lua_loc_conf_t *plcf) +{ + ngx_pool_cleanup_t *cln; + + plcf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); + if (plcf->ssl == NULL) { + return NGX_ERROR; + } + + plcf->ssl->log = cf->log; + + if (ngx_ssl_create(plcf->ssl, + NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1 + |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2, + NULL) + != NGX_OK) + { + return NGX_ERROR; + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_ssl_cleanup_ctx; + cln->data = plcf->ssl; + + return NGX_OK; +} + +#endif + static void ngx_http_lua_cleanup_vm(void *data) { diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index d27edbb60a..668b5cd849 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -106,6 +106,16 @@ static void ngx_http_lua_tcp_resolve_cleanup(void *data); static void ngx_http_lua_tcp_socket_cleanup(void *data); +#if (NGX_HTTP_SSL) + +static int ngx_http_lua_socket_ssl_handshake_ended(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static void ngx_http_lua_socket_ssl_handshake(ngx_connection_t *c); +static ngx_int_t ngx_http_lua_socket_ssl_init_connection(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); + +#endif + enum { SOCKET_CTX_INDEX = 1, SOCKET_TIMEOUT_INDEX = 2, @@ -294,6 +304,10 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) const char *msg; ngx_http_lua_co_ctx_t *coctx; +#if (NGX_HTTP_SSL) + ngx_int_t ssl = 0; +#endif + ngx_http_lua_socket_tcp_upstream_t *u; n = lua_gettop(L); @@ -355,7 +369,6 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) break; case LUA_TNIL: - lua_pop(L, 2); break; default: @@ -365,6 +378,24 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) break; } +#if (NGX_HTTP_SSL) + + lua_getfield(L, n, "ssl"); + + if (lua_type(L, -1) != LUA_TBOOLEAN) { + msg = lua_pushfstring(L, "bad \"ssl\" option type: %s", + luaL_typename(L, -1)); + return luaL_argerror(L, n, msg); + } + + ssl = lua_toboolean(L, -1); + lua_pop(L, 1); + +#endif + if (!custom_pool) { + lua_pop(L, 2); + } + n--; } @@ -400,6 +431,8 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) u = lua_touserdata(L, -1); lua_pop(L, 1); + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + if (u) { if (u->waiting) { lua_pushnil(L); @@ -439,12 +472,18 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) ngx_memzero(u, sizeof(ngx_http_lua_socket_tcp_upstream_t)); +#if (NGX_HTTP_SSL) + + if (llcf->ssl) { + u->ssl = ssl; + } + +#endif + coctx = ctx->cur_co_ctx; u->request = r; /* set the controlling request */ - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - u->conf = llcf; pc = &u->peer; @@ -766,6 +805,104 @@ ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) } +#if (NGX_HTTP_SSL) +static ngx_int_t +ngx_http_lua_socket_ssl_init_connection(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_connection_t *c; + ngx_int_t rc; + + c = u->peer.connection; + + if (ngx_ssl_create_connection(u->conf->ssl, c, + NGX_SSL_BUFFER|NGX_SSL_CLIENT) + != NGX_OK) + { + return luaL_error(L, "error creating ssl session"); + } + /* TODO: Add SSL session reuse here. */ + + rc = ngx_ssl_handshake(c); + + if (rc == NGX_AGAIN) { + c->ssl->handler = ngx_http_lua_socket_ssl_handshake; + u->prepare_retvals = ngx_http_lua_socket_ssl_handshake_ended; + return NGX_AGAIN; + } + + return ngx_http_lua_socket_ssl_handshake_ended(r, u, L); +} + + +static void +ngx_http_lua_socket_ssl_handshake(ngx_connection_t *c) +{ + ngx_http_request_t *r; + ngx_http_lua_socket_tcp_upstream_t *u; + ngx_http_lua_ctx_t *ctx; + + u = c->data; + r = u->request; + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + if (ctx == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "ngx_lua ctx not found"); + return; + } + + c->write->handler = ngx_http_lua_socket_tcp_handler; + c->read->handler = ngx_http_lua_socket_tcp_handler; + + ctx->resume_handler = ngx_http_lua_socket_tcp_resume; + r->write_event_handler(r); + ngx_http_run_posted_requests(r->connection); +} + + +static int +ngx_http_lua_socket_ssl_handshake_ended(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_connection_t *c; + ngx_http_lua_loc_conf_t *llcf; + X509 *cert; + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + c = u->peer.connection; + + if (!c->ssl->handshaked) { + lua_pushnil(L); + lua_pushliteral(L, "SSL handshake failed"); + return 2; + } + + if (llcf->ssl_verify) { + if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) { + lua_pushnil(L); + lua_pushliteral(L, "SSL certificate verfication failed"); + return 2; + } + + cert = SSL_get_peer_certificate(c->ssl->connection); + if (cert == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "SSL server did not return certificate"); + return 2; + } + + X509_free(cert); + } + + c->log->action = "SSL connection transaction"; + lua_pushinteger(L, 1); + return 1; +} +#endif + + 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) @@ -898,13 +1035,21 @@ ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); lua_pushnil(L); - lua_pushliteral(L, "failed to handle write event"); + 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; +#if (NGX_HTTP_SSL) + + if (u->ssl) { + return ngx_http_lua_socket_ssl_init_connection(r, u, L); + } + +#endif + lua_pushinteger(L, 1); return 1; } @@ -1014,6 +1159,14 @@ ngx_http_lua_socket_tcp_connect_retval_handler(ngx_http_request_t *r, return ngx_http_lua_socket_error_retval_handler(r, u, L); } +#if (NGX_HTTP_SSL) + + if (u->ssl) { + return ngx_http_lua_socket_ssl_init_connection(r, u, L); + } + +#endif + lua_pushinteger(L, 1); return 1; } @@ -2429,6 +2582,15 @@ ngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua close socket connection"); +#if (NGX_HTTP_SSL) + + if (u->peer.connection->ssl) { + u->peer.connection->ssl->no_wait_shutdown = 1; + (void) ngx_ssl_shutdown(u->peer.connection); + } + +#endif + ngx_close_connection(u->peer.connection); u->peer.connection = NULL; diff --git a/src/ngx_http_lua_socket_tcp.h b/src/ngx_http_lua_socket_tcp.h index f977ef7133..ce3cd6316f 100644 --- a/src/ngx_http_lua_socket_tcp.h +++ b/src/ngx_http_lua_socket_tcp.h @@ -88,6 +88,9 @@ struct ngx_http_lua_socket_tcp_upstream_s { unsigned no_close:1; unsigned waiting:1; unsigned eof:1; +#if (NGX_HTTP_SSL) + unsigned ssl:1; +#endif unsigned body_downstream:1; unsigned raw_downstream:1; }; From a3f9aa962d7a446a31a66fefe749666eac6f3b2f Mon Sep 17 00:00:00 2001 From: aviram Date: Sun, 29 Sep 2013 14:40:43 +0200 Subject: [PATCH 07/18] Multiple changes: - Now receive and send operations can be done simultaneous from several threads. - A different timeout can be set for receive and send. - Added fake_close for the client socket (ngx.req.socket), so that a thread that receives on this socket can be notified that we don't want to read from it anymore. --- src/ngx_http_lua_socket_tcp.c | 416 +++++++++++++++++++++++++++++----- src/ngx_http_lua_socket_tcp.h | 6 +- 2 files changed, 361 insertions(+), 61 deletions(-) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 668b5cd849..852ae3a1d5 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -28,8 +28,11 @@ static int ngx_http_lua_socket_tcp_connect(lua_State *L); static int ngx_http_lua_socket_tcp_receive(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_fake_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_set_receive_timeout(lua_State *L); +static int ngx_http_lua_socket_tcp_set_send_timeout(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); @@ -49,8 +52,16 @@ 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_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_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_connect_retval_handler(ngx_http_request_t *r, @@ -170,6 +181,12 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) 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_set_receive_timeout); + lua_setfield(L, -2, "set_receive_timeout"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_fake_close); + lua_setfield(L, -2, "fake_close"); + lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); @@ -223,6 +240,12 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) 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_set_receive_timeout); + lua_setfield(L, -2, "set_receive_timeout"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_set_send_timeout); + lua_setfield(L, -2, "set_send_timeout"); /* ngx socket mt */ + lua_pushcfunction(L, ngx_http_lua_socket_tcp_getreusedtimes); lua_setfield(L, -2, "getreusedtimes"); @@ -600,7 +623,8 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) rctx->timeout = clcf->resolver_timeout; u->resolved->ctx = rctx; - u->co_ctx = ctx->cur_co_ctx; + /* For connect and resolve, use co_ctx_read. */ + u->co_ctx_read = ctx->cur_co_ctx; coctx->data = u; @@ -679,9 +703,9 @@ ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) return; } - lctx->cur_co_ctx = u->co_ctx; + lctx->cur_co_ctx = u->co_ctx_read; - u->co_ctx->cleanup = NULL; + u->co_ctx_read->cleanup = NULL; L = lctx->cur_co_ctx->co; @@ -1067,7 +1091,8 @@ ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, r->write_event_handler = ngx_http_core_run_phases; } - u->co_ctx = ctx->cur_co_ctx; + /* For connect and resolve, use co_ctx_read. */ + u->co_ctx_read = ctx->cur_co_ctx; u->waiting = 1; u->prepare_retvals = ngx_http_lua_socket_tcp_connect_retval_handler; @@ -1095,8 +1120,12 @@ ngx_http_lua_socket_error_retval_handler(ngx_http_request_t *r, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket error retval handler"); - if (u->co_ctx) { - u->co_ctx->cleanup = NULL; + if (u->co_ctx_read) { + u->co_ctx_read->cleanup = NULL; + } + + if (u->co_ctx_write) { + u->co_ctx_write->cleanup = NULL; } ft_type = u->ft_type; @@ -1223,9 +1252,9 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) return 2; } - if (u->waiting) { + if (u->waiting_read) { lua_pushnil(L); - lua_pushliteral(L, "socket busy"); + lua_pushliteral(L, "socket already busy reading"); return 2; } @@ -1325,7 +1354,7 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) r->read_event_handler = ngx_http_lua_req_socket_rev_handler; } - u->waiting = 0; + u->waiting_read = 0; rc = ngx_http_lua_socket_tcp_read(r, u); @@ -1347,7 +1376,6 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) /* rc == NGX_AGAIN */ u->read_event_handler = ngx_http_lua_socket_read_handler; - u->write_event_handler = ngx_http_lua_socket_dummy_handler; ctx->cur_co_ctx->cleanup = ngx_http_lua_tcp_socket_cleanup; @@ -1358,8 +1386,8 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) r->write_event_handler = ngx_http_core_run_phases; } - u->co_ctx = ctx->cur_co_ctx; - u->waiting = 1; + u->co_ctx_read = ctx->cur_co_ctx; + u->waiting_read = 1; u->prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler; coctx = ctx->cur_co_ctx; @@ -1537,7 +1565,7 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, rev = c->read; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua tcp socket read data: waiting: %d", (int) u->waiting); + "lua tcp socket read data: waiting: %d", (int) u->waiting_read); b = &u->buffer; read = 0; @@ -1554,7 +1582,7 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, 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->waiting, (int) u->eof, + "uri:\"%V?%V\"", (int) u->waiting_read, (int) u->eof, &r->uri, &r->args); if (u->body_downstream @@ -1572,12 +1600,12 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, } if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST) { - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); } else { - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); } return NGX_ERROR; @@ -1586,23 +1614,23 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, #if 1 if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + 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_success(r, u); + ngx_http_lua_socket_handle_read_success(r, u); return NGX_OK; } if (rc == NGX_ERROR) { dd("input filter error: ft_type:%d waiting:%d", - (int) u->ft_type, (int) u->waiting); + (int) u->ft_type, (int) u->waiting_read); - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } @@ -1625,8 +1653,8 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, if (size == 0) { rc = ngx_http_lua_socket_add_input_buffer(r, u); if (rc == NGX_ERROR) { - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_NOMEM); + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_NOMEM); return NGX_ERROR; } @@ -1742,7 +1770,7 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, if (llcf->check_client_abort) { - ngx_http_lua_socket_handle_error(r, u, + ngx_http_lua_socket_handle_read_error(r, u, NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); return NGX_ERROR; } @@ -1750,7 +1778,7 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, /* llcf->check_client_abort == 0 */ if (u->body_downstream && r->request_body->rest) { - ngx_http_lua_socket_handle_error(r, u, + ngx_http_lua_socket_handle_read_error(r, u, NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); return NGX_ERROR; } @@ -1766,8 +1794,8 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, if (n == NGX_ERROR) { u->socket_errno = ngx_socket_errno; - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } @@ -1781,8 +1809,8 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, #if 1 if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } #endif @@ -1848,9 +1876,9 @@ ngx_http_lua_socket_tcp_send(lua_State *L) return 2; } - if (u->waiting) { + if (u->waiting_write) { lua_pushnil(L); - lua_pushliteral(L, "socket busy"); + lua_pushliteral(L, "socket already busy writing"); return 2; } @@ -1927,7 +1955,7 @@ ngx_http_lua_socket_tcp_send(lua_State *L) /* mimic ngx_http_upstream_init_request here */ #if 1 - u->waiting = 0; + u->waiting_write = 0; #endif ngx_http_lua_probe_socket_tcp_send_start(r, u, b->pos, len); @@ -1957,8 +1985,8 @@ ngx_http_lua_socket_tcp_send(lua_State *L) r->write_event_handler = ngx_http_core_run_phases; } - u->co_ctx = ctx->cur_co_ctx; - u->waiting = 1; + u->co_ctx_write = ctx->cur_co_ctx; + u->waiting_write = 1; u->prepare_retvals = ngx_http_lua_socket_tcp_send_retval_handler; dd("setting data to %p", u); @@ -2096,7 +2124,7 @@ ngx_http_lua_socket_tcp_close(lua_State *L) return 2; } - if (u->waiting) { + if ((u->waiting_read) || (u->waiting_write) || (u->waiting)) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; @@ -2114,6 +2142,42 @@ ngx_http_lua_socket_tcp_close(lua_State *L) return 1; } +static int +ngx_http_lua_socket_tcp_fake_close(lua_State *L) +{ + 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 (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"); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + u->fake_eof = 1; + coctx = ctx->cur_co_ctx; + ngx_http_lua_socket_read_handler(r, u); + ctx->cur_co_ctx = coctx; + + return 0; +} static int ngx_http_lua_socket_tcp_setoption(lua_State *L) @@ -2163,6 +2227,76 @@ ngx_http_lua_socket_tcp_settimeout(lua_State *L) } +static int +ngx_http_lua_socket_tcp_set_receive_timeout(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 set_receive_timeout: expecting at least 2 " + "arguments (including the object) but seen %d", + lua_gettop(L)); + } + + timeout = (ngx_int_t) lua_tonumber(L, 2); + + lua_rawseti(L, 1, SOCKET_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; + } else { + u->read_timeout = u->conf->read_timeout; + } + } + + return 0; +} + + +static int +ngx_http_lua_socket_tcp_set_send_timeout(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 set_send_timeout: expecting at least 2 " + "arguments (including the object) but seen %d", + lua_gettop(L)); + } + + timeout = (ngx_int_t) lua_tonumber(L, 2); + + lua_rawseti(L, 1, SOCKET_TIMEOUT_INDEX); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u) { + if (timeout > 0) { + u->send_timeout = (ngx_msec_t) timeout; + } else { + u->send_timeout = u->conf->send_timeout; + } + } + + return 0; +} + + static void ngx_http_lua_socket_tcp_handler(ngx_event_t *ev) { @@ -2215,6 +2349,11 @@ ngx_http_lua_socket_read_handler(ngx_http_request_t *r, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket read handler"); + if (u->fake_eof) { + ngx_http_lua_socket_handle_read_error(r, u, NGX_HTTP_LUA_SOCKET_FT_CLOSED); + return; + } + if (c->read->timedout) { c->read->timedout = 0; @@ -2225,7 +2364,7 @@ ngx_http_lua_socket_read_handler(ngx_http_request_t *r, "lua tcp socket read timed out"); } - ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); + ngx_http_lua_socket_handle_read_error(r, u, NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); return; } @@ -2261,7 +2400,7 @@ ngx_http_lua_socket_send_handler(ngx_http_request_t *r, "lua tcp socket write timed out"); } - ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); + ngx_http_lua_socket_handle_write_error(r, u, NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); return; } @@ -2289,8 +2428,8 @@ ngx_http_lua_socket_send(ngx_http_request_t *r, ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_write_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } @@ -2323,12 +2462,12 @@ ngx_http_lua_socket_send(ngx_http_request_t *r, 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_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_write_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } - ngx_http_lua_socket_handle_success(r, u); + ngx_http_lua_socket_handle_write_success(r, u); return NGX_OK; } @@ -2342,7 +2481,7 @@ ngx_http_lua_socket_send(ngx_http_request_t *r, if (n == NGX_ERROR) { u->socket_errno = ngx_socket_errno; - ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_write_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } @@ -2353,13 +2492,12 @@ ngx_http_lua_socket_send(ngx_http_request_t *r, } u->write_event_handler = ngx_http_lua_socket_send_handler; - u->read_event_handler = ngx_http_lua_socket_dummy_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_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_write_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } @@ -2378,8 +2516,8 @@ ngx_http_lua_socket_handle_success(ngx_http_request_t *r, u->write_event_handler = ngx_http_lua_socket_dummy_handler; #endif - if (u->co_ctx) { - u->co_ctx->cleanup = NULL; + if (u->co_ctx_read) { + u->co_ctx_read->cleanup = NULL; } #if 0 @@ -2397,7 +2535,85 @@ ngx_http_lua_socket_handle_success(ngx_http_request_t *r, } ctx->resume_handler = ngx_http_lua_socket_tcp_resume; - ctx->cur_co_ctx = u->co_ctx; + ctx->cur_co_ctx = u->co_ctx_read; + + 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_success(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_http_lua_ctx_t *ctx; + +#if 1 + u->read_event_handler = ngx_http_lua_socket_dummy_handler; +#endif + + if (u->co_ctx_read) { + u->co_ctx_read->cleanup = NULL; + } + +#if 0 + if (u->eof) { + ngx_http_lua_socket_tcp_finalize(r, u); + } +#endif + + if (u->waiting_read) { + u->waiting_read = 0; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_http_lua_socket_tcp_resume; + ctx->cur_co_ctx = u->co_ctx_read; + + 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_success(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_http_lua_ctx_t *ctx; + +#if 1 + u->write_event_handler = ngx_http_lua_socket_dummy_handler; +#endif + + if (u->co_ctx_write) { + u->co_ctx_write->cleanup = NULL; + } + +#if 0 + if (u->eof) { + ngx_http_lua_socket_tcp_finalize(r, u); + } +#endif + + if (u->waiting_write) { + u->waiting_write = 0; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_http_lua_socket_tcp_resume; + ctx->cur_co_ctx = u->co_ctx_write; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket waking up the current request"); @@ -2422,8 +2638,8 @@ ngx_http_lua_socket_handle_error(ngx_http_request_t *r, ngx_http_lua_socket_tcp_finalize(r, u); #endif - if (u->co_ctx) { - u->co_ctx->cleanup = NULL; + if (u->co_ctx_read) { + u->co_ctx_read->cleanup = NULL; } u->read_event_handler = ngx_http_lua_socket_dummy_handler; @@ -2438,7 +2654,87 @@ ngx_http_lua_socket_handle_error(ngx_http_request_t *r, } ctx->resume_handler = ngx_http_lua_socket_tcp_resume; - ctx->cur_co_ctx = u->co_ctx; + ctx->cur_co_ctx = u->co_ctx_read; + + 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_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket handle error"); + + u->ft_type |= ft_type; + +#if 0 + ngx_http_lua_socket_tcp_finalize(r, u); +#endif + + if (u->co_ctx_read) { + u->co_ctx_read->cleanup = NULL; + } + + u->read_event_handler = ngx_http_lua_socket_dummy_handler; + + if (u->waiting_read) { + u->waiting_read = 0; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_http_lua_socket_tcp_resume; + ctx->cur_co_ctx = u->co_ctx_read; + + 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_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket handle error"); + + u->ft_type |= ft_type; + +#if 0 + ngx_http_lua_socket_tcp_finalize(r, u); +#endif + + if (u->co_ctx_write) { + u->co_ctx_write->cleanup = NULL; + } + + u->write_event_handler = ngx_http_lua_socket_dummy_handler; + + if (u->waiting_write) { + u->waiting_write = 0; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_http_lua_socket_tcp_resume; + ctx->cur_co_ctx = u->co_ctx_write; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket waking up the current request"); @@ -2818,7 +3114,7 @@ ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) return 2; } - if (u->waiting) { + if (u->waiting_read) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; @@ -2880,7 +3176,7 @@ ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) r->read_event_handler = ngx_http_lua_req_socket_rev_handler; } - u->waiting = 0; + u->waiting_read = 0; rc = ngx_http_lua_socket_tcp_read(r, u); @@ -2913,8 +3209,8 @@ ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) r->write_event_handler = ngx_http_core_run_phases; } - u->co_ctx = ctx->cur_co_ctx; - u->waiting = 1; + u->co_ctx_read = ctx->cur_co_ctx; + u->waiting_read = 1; u->prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler; coctx = ctx->cur_co_ctx; @@ -3611,7 +3907,7 @@ static int ngx_http_lua_socket_tcp_setkeepalive(lua_State *L) return 2; } - if (u->waiting) { + if ((u->waiting_read) || (u->waiting_write) || (u->waiting)) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; diff --git a/src/ngx_http_lua_socket_tcp.h b/src/ngx_http_lua_socket_tcp.h index ce3cd6316f..518941986f 100644 --- a/src/ngx_http_lua_socket_tcp.h +++ b/src/ngx_http_lua_socket_tcp.h @@ -81,13 +81,17 @@ struct ngx_http_lua_socket_tcp_upstream_s { size_t request_len; ngx_chain_t *request_bufs; - ngx_http_lua_co_ctx_t *co_ctx; + ngx_http_lua_co_ctx_t *co_ctx_read; + ngx_http_lua_co_ctx_t *co_ctx_write; ngx_uint_t reused; unsigned no_close:1; unsigned waiting:1; + unsigned waiting_read:1; + unsigned waiting_write:1; unsigned eof:1; + unsigned fake_eof:1; #if (NGX_HTTP_SSL) unsigned ssl:1; #endif From d4864a9e0c1541c51cf4628520c8846d43631d3c Mon Sep 17 00:00:00 2001 From: aviram Date: Sun, 29 Sep 2013 15:08:07 +0200 Subject: [PATCH 08/18] Added the option 'bsd_receive' to the receive method, which enables us to receive just like BSD's recv call, meaning the maximum number of bytes given. This will only work only when a number is given as the first parameter for receive. Note that this differs from the original LuaSocket API. --- src/ngx_http_lua_socket_tcp.c | 84 ++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 5 deletions(-) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 852ae3a1d5..4250e0602d 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -83,6 +83,7 @@ static int ngx_http_lua_socket_error_retval_handler(ngx_http_request_t *r, 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_exact_chunk(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, @@ -1215,10 +1216,11 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) int typ; ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_co_ctx_t *coctx; + int bsd_read; n = lua_gettop(L); - if (n != 1 && n != 2) { - return luaL_error(L, "expecting 1 or 2 arguments " + if (n != 1 && n != 2 && n != 3) { + return luaL_error(L, "expecting 1, 2 or 3 arguments " "(including the object), but got %d", n); } @@ -1311,7 +1313,37 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) } #endif - u->input_filter = ngx_http_lua_socket_read_chunk; + bsd_read = 0; + + /* Check if the options table is given. */ + if (n == 3) { + luaL_checktype(L, 3, LUA_TTABLE); + lua_getfield(L, 3, "bsd_read"); + + switch (lua_type(L, -1)) { + case LUA_TNIL: + /* use default value - false */ + break; + + case LUA_TBOOLEAN: + bsd_read = lua_toboolean(L, -1); + break; + + default: + return luaL_error(L, "bad \"bsd_read\" option value type: %s", + luaL_typename(L, -1)); + + } + + lua_pop(L, 1); + } + + if (bsd_read) { + u->input_filter = ngx_http_lua_socket_read_exact_chunk; + } else { + u->input_filter = ngx_http_lua_socket_read_chunk; + } + u->length = (size_t) bytes; u->rest = u->length; @@ -1445,6 +1477,48 @@ ngx_http_lua_socket_read_chunk(void *data, ssize_t bytes) } +static ngx_int_t +ngx_http_lua_socket_read_exact_chunk(void *data, ssize_t bytes) +{ + ngx_http_lua_socket_tcp_upstream_t *u = data; + + ngx_buf_t *b; +#if (NGX_DEBUG) + ngx_http_request_t *r; + + r = u->request; +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket read max chunk %z", bytes); + + if (bytes == 0) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; + return NGX_ERROR; + } + + b = &u->buffer; + + if (bytes >= (ssize_t) u->rest) { + + u->buf_in->buf->last += u->rest; + b->pos += u->rest; + u->rest = 0; + + return NGX_OK; + } + + /* bytes < u->rest */ + + u->buf_in->buf->last += bytes; + b->pos += bytes; + + u->rest = 0; + + return NGX_OK; +} + + static ngx_int_t ngx_http_lua_socket_read_all(void *data, ssize_t bytes) { @@ -3689,8 +3763,8 @@ ngx_http_lua_req_socket(lua_State *L) } dd("req content length: %d", (int) r->headers_in.content_length_n); - - if (r->headers_in.content_length_n <= 0) { + + if (r->headers_in.content_length_n == 0) { lua_pushnil(L); lua_pushliteral(L, "no body"); return 2; From 08dc8b4657ad6b1f557594c02f8a6e2db1735d7d Mon Sep 17 00:00:00 2001 From: aviram Date: Sun, 29 Sep 2013 17:46:59 +0200 Subject: [PATCH 09/18] Added the lua_correct_location_header directive. If off, when setting the "Location" response header, Nginx won't try to make absolute in case it's relative. Default is on. --- src/ngx_http_lua_common.h | 2 ++ src/ngx_http_lua_headers_out.c | 14 ++++++++++++++ src/ngx_http_lua_module.c | 10 ++++++++++ 3 files changed, 26 insertions(+) diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index b03d7bb994..d4ff192387 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -201,6 +201,8 @@ typedef struct { ngx_flag_t transform_underscores_in_resp_headers; ngx_flag_t log_socket_errors; ngx_flag_t check_client_abort; + + ngx_flag_t correct_location_header; } ngx_http_lua_loc_conf_t; diff --git a/src/ngx_http_lua_headers_out.c b/src/ngx_http_lua_headers_out.c index 2ee10d1ffb..4f0daa1795 100644 --- a/src/ngx_http_lua_headers_out.c +++ b/src/ngx_http_lua_headers_out.c @@ -434,6 +434,9 @@ ngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_str_t key, ngx_http_lua_header_val_t hv; ngx_http_lua_set_header_t *handlers = ngx_http_lua_set_handlers; ngx_uint_t i; + ngx_http_lua_loc_conf_t *llcf; + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); dd("set header value: %.*s", (int) value.len, value.data); @@ -455,6 +458,17 @@ ngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_str_t key, continue; } + if (!llcf->correct_location_header + && ngx_strncasecmp(hv.key.data, + (u_char *) "Location", + sizeof("Location")) + == 0) { + /* XXX The best way to get the index of the last of the structure */ + i = (sizeof(ngx_http_lua_set_handlers) / + sizeof(ngx_http_lua_set_handlers[0])) - 1; + break; + } + dd("Matched handler: %s %s", handlers[i].name.data, hv.key.data); hv.offset = handlers[i].offset; diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index 163f6d64e2..a737e1868d 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -343,6 +343,14 @@ static ngx_command_t ngx_http_lua_cmds[] = { offsetof(ngx_http_lua_loc_conf_t, check_client_abort), NULL }, + { ngx_string("lua_correct_location_header"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + |NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, correct_location_header), + NULL }, + ngx_null_command }; @@ -616,6 +624,7 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) conf->enable_code_cache = NGX_CONF_UNSET; conf->http10_buffering = NGX_CONF_UNSET; conf->check_client_abort = NGX_CONF_UNSET; + conf->correct_location_header = NGX_CONF_UNSET; conf->keepalive_timeout = NGX_CONF_UNSET_MSEC; conf->connect_timeout = NGX_CONF_UNSET_MSEC; @@ -679,6 +688,7 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->enable_code_cache, prev->enable_code_cache, 1); ngx_conf_merge_value(conf->http10_buffering, prev->http10_buffering, 1); ngx_conf_merge_value(conf->check_client_abort, prev->check_client_abort, 0); + ngx_conf_merge_value(conf->correct_location_header, prev->correct_location_header, 1); ngx_conf_merge_msec_value(conf->keepalive_timeout, prev->keepalive_timeout, 60000); From 7a7b7fc75d5acb819858d4e0c7c0265ebe9bd14d Mon Sep 17 00:00:00 2001 From: aviram Date: Sun, 29 Sep 2013 18:16:59 +0200 Subject: [PATCH 10/18] Added the 'lua_enforce_content_type' directive. When this is off and response headers weren't set, ngx_lua won't enforce setting the content type header. Default is on, and this causes the default behavior - if the headers aren't set, then the 'Content-Type' header is set to the default content type. --- src/ngx_http_lua_common.h | 2 ++ src/ngx_http_lua_headers.c | 2 +- src/ngx_http_lua_module.c | 10 ++++++++++ src/ngx_http_lua_util.c | 9 +++++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index b03d7bb994..847607d252 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -201,6 +201,8 @@ typedef struct { ngx_flag_t transform_underscores_in_resp_headers; ngx_flag_t log_socket_errors; ngx_flag_t check_client_abort; + + ngx_flag_t enforce_content_type; } ngx_http_lua_loc_conf_t; diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c index 3a0226517d..fb391c111e 100644 --- a/src/ngx_http_lua_headers.c +++ b/src/ngx_http_lua_headers.c @@ -485,7 +485,7 @@ ngx_http_lua_ngx_header_set(lua_State *L) } } - if (!ctx->headers_set) { + if (!ctx->headers_set && llcf->enforce_content_type) { rc = ngx_http_set_content_type(r); if (rc != NGX_OK) { return luaL_error(L, diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index 163f6d64e2..9c460e05da 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -343,6 +343,14 @@ static ngx_command_t ngx_http_lua_cmds[] = { offsetof(ngx_http_lua_loc_conf_t, check_client_abort), NULL }, + { ngx_string("lua_enforce_content_type"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + |NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, enforce_content_type), + NULL }, + ngx_null_command }; @@ -616,6 +624,7 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) conf->enable_code_cache = NGX_CONF_UNSET; conf->http10_buffering = NGX_CONF_UNSET; conf->check_client_abort = NGX_CONF_UNSET; + conf->enforce_content_type = NGX_CONF_UNSET; conf->keepalive_timeout = NGX_CONF_UNSET_MSEC; conf->connect_timeout = NGX_CONF_UNSET_MSEC; @@ -679,6 +688,7 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->enable_code_cache, prev->enable_code_cache, 1); ngx_conf_merge_value(conf->http10_buffering, prev->http10_buffering, 1); ngx_conf_merge_value(conf->check_client_abort, prev->check_client_abort, 0); + ngx_conf_merge_value(conf->enforce_content_type, prev->enforce_content_type, 1); ngx_conf_merge_msec_value(conf->keepalive_timeout, prev->keepalive_timeout, 60000); diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 481f95a135..1e227f7bd5 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -477,14 +477,19 @@ ngx_int_t ngx_http_lua_send_header_if_needed(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { - ngx_int_t rc; + ngx_http_lua_loc_conf_t *llcf; + ngx_int_t rc; + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (!r->header_sent) { if (r->headers_out.status == 0) { r->headers_out.status = NGX_HTTP_OK; } - if (!ctx->headers_set && ngx_http_set_content_type(r) != NGX_OK) { + if (!ctx->headers_set + && llcf->enforce_content_type + && ngx_http_set_content_type(r) != NGX_OK) { return NGX_ERROR; } From 2a84a4c0c78a3b62d98cb5d1b0306dde1e09a8b8 Mon Sep 17 00:00:00 2001 From: aviram Date: Mon, 30 Sep 2013 07:56:14 +0200 Subject: [PATCH 11/18] Added two killing functions - ngx.thread.kill - kills a thread. ngx.thread.kill_sleeping - either kills immediately a sleeping thread, or, if the thread is not sleeping, can wait for it to end. Useful for a thread that has a loop in which it sleeps. --- src/ngx_http_lua_uthread.c | 141 ++++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 1 deletion(-) diff --git a/src/ngx_http_lua_uthread.c b/src/ngx_http_lua_uthread.c index a93b84c553..72bbfaa243 100644 --- a/src/ngx_http_lua_uthread.c +++ b/src/ngx_http_lua_uthread.c @@ -24,7 +24,8 @@ static int ngx_http_lua_uthread_spawn(lua_State *L); static int ngx_http_lua_uthread_wait(lua_State *L); - +static int ngx_http_lua_uthread_kill(lua_State *L); +static int ngx_http_lua_uthread_kill_sleeping_thread(lua_State *L); void ngx_http_lua_inject_uthread_api(ngx_log_t *log, lua_State *L) @@ -38,6 +39,12 @@ ngx_http_lua_inject_uthread_api(ngx_log_t *log, lua_State *L) lua_pushcfunction(L, ngx_http_lua_uthread_wait); lua_setfield(L, -2, "wait"); + lua_pushcfunction(L, ngx_http_lua_uthread_kill); + lua_setfield(L, -2, "kill"); + + lua_pushcfunction(L, ngx_http_lua_uthread_kill_sleeping_thread); + lua_setfield(L, -2, "kill_sleeping"); + lua_setfield(L, -2, "thread"); } @@ -181,4 +188,136 @@ ngx_http_lua_uthread_wait(lua_State *L) return lua_yield(L, 0); } + +static int +ngx_http_lua_uthread_kill(lua_State *L) +{ + int i, nargs; + lua_State *sub_co; + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx, *sub_coctx; + + 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 request 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); + + coctx = ctx->cur_co_ctx; + + nargs = lua_gettop(L); + + for (i = 1; i <= nargs; i++) { + sub_co = lua_tothread(L, i); + + luaL_argcheck(L, sub_co, i, "lua thread expected"); + + sub_coctx = ngx_http_lua_get_co_ctx(sub_co, ctx); + if (sub_coctx == NULL) { + return luaL_error(L, "no co ctx found for the ngx.thread " + "instance given"); + } + + if (!sub_coctx->is_uthread) { + return luaL_error(L, "attempt to kill a coroutine that is " + "not a user thread"); + } + + if (sub_coctx->parent_co_ctx != coctx) { + return luaL_error(L, "only the parent coroutine can kill the " + "thread"); + } + + ngx_http_lua_del_thread(r, L, ctx, sub_coctx); + ctx->uthreads--; + } + + return 0; +} + + +static int +ngx_http_lua_uthread_kill_sleeping_thread(lua_State *L) +{ + int nargs; + lua_State *sub_co; + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + ngx_http_lua_co_ctx_t *coctx, *sub_coctx; + int wait_for_non_sleeping = 1; + + 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 request 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); + + coctx = ctx->cur_co_ctx; + + nargs = lua_gettop(L); + + if ((nargs != 2) && (nargs != 1)) { + return luaL_error(L, "one or two arguments expected"); + } + + sub_co = lua_tothread(L, 1); + luaL_argcheck(L, sub_co, 1, "lua thread expected"); + + if (nargs == 2) { + wait_for_non_sleeping = lua_toboolean(L, 2); + } else { + wait_for_non_sleeping = 1; + } + + sub_coctx = ngx_http_lua_get_co_ctx(sub_co, ctx); + if (sub_coctx == NULL) { + return luaL_error(L, "no co ctx found for the ngx.thread " + "instance given"); + } + + if (!sub_coctx->is_uthread) { + return luaL_error(L, "attempt to wait on a coroutine that is " + "not a user thread"); + } + + if (sub_coctx->parent_co_ctx != coctx) { + return luaL_error(L, "only the parent coroutine can wait on the " + "thread"); + } + + /* If the process is not sleeping - wait for it. + If the process is sleeping - delete it. + */ + if (sub_coctx->sleep.timer_set) { + ngx_http_lua_del_thread(r, L, ctx, sub_coctx); + ctx->uthreads--; + return 0; + } else { + if (wait_for_non_sleeping) { + ngx_http_lua_probe_user_thread_wait(L, sub_coctx->co); + sub_coctx->waited_by_parent = 1; + return lua_yield(L, 0); + } + } + return 0; +} /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ From 2799aad1906b99a54f9bb18b10cad7966bef06d6 Mon Sep 17 00:00:00 2001 From: aviram Date: Mon, 30 Sep 2013 10:49:17 +0200 Subject: [PATCH 12/18] Lua subrequests - added support for passing the method optional parameter as a string. If the method string isn't a known Nginx method, it is still passed (and Nginx considers it as unknown). --- src/ngx_http_lua_subrequest.c | 100 ++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 11 deletions(-) diff --git a/src/ngx_http_lua_subrequest.c b/src/ngx_http_lua_subrequest.c index bb10fff4b0..c21ad1795e 100644 --- a/src/ngx_http_lua_subrequest.c +++ b/src/ngx_http_lua_subrequest.c @@ -50,6 +50,23 @@ ngx_str_t ngx_http_lua_patch_method = ngx_str_t ngx_http_lua_trace_method = ngx_http_lua_method_name("TRACE"); +ngx_str_t * ngx_http_lua_ordered_methods[] = { + &ngx_http_lua_get_method, + &ngx_http_lua_head_method, + &ngx_http_lua_post_method, + &ngx_http_lua_put_method, + &ngx_http_lua_delete_method, + &ngx_http_lua_mkcol_method, + &ngx_http_lua_copy_method, + &ngx_http_lua_move_method, + &ngx_http_lua_options_method, + &ngx_http_lua_propfind_method, + &ngx_http_lua_proppatch_method, + &ngx_http_lua_lock_method, + &ngx_http_lua_unlock_method, + &ngx_http_lua_patch_method, + &ngx_http_lua_trace_method, +}; static ngx_str_t ngx_http_lua_content_length_header_key = ngx_string("Content-Length"); @@ -58,7 +75,7 @@ static ngx_str_t ngx_http_lua_content_length_header_key = static ngx_int_t ngx_http_lua_set_content_length_header(ngx_http_request_t *r, off_t len); static ngx_int_t ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, - ngx_uint_t method, int forward_body, + ngx_uint_t method, ngx_str_t *method_name, int forward_body, ngx_http_request_body_t *body, unsigned vars_action, ngx_array_t *extra_vars); static int ngx_http_lua_ngx_location_capture(lua_State *L); @@ -136,6 +153,11 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) unsigned vars_action; ngx_uint_t nsubreqs; ngx_uint_t index; + ngx_uint_t method_index; + ngx_uint_t methods_number; + ngx_str_t *ngx_method_name = NULL; + u_char *lua_method_name; + size_t method_name_length; size_t sr_statuses_len; size_t sr_headers_len; size_t sr_bodies_len; @@ -386,15 +408,62 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) type = lua_type(L, -1); - if (type == LUA_TNIL) { - method = NGX_HTTP_GET; + switch (type) { + case LUA_TNIL: + method = NGX_HTTP_GET; + break; + case LUA_TNUMBER: + method = (ngx_uint_t) lua_tonumber(L, -1); + break; + case LUA_TSTRING: + lua_method_name = (u_char *) lua_tolstring(L, -1, + &method_name_length); + if (method_name_length == 0) { + return luaL_error(L, "Bad http request method"); + } + + methods_number = sizeof(ngx_http_lua_ordered_methods) / + sizeof(ngx_http_lua_ordered_methods[0]); + for (method_index = 0; method_index < methods_number; + method_index++) { + if (ngx_strncasecmp( + ngx_http_lua_ordered_methods[method_index]->data, + lua_method_name, + method_name_length) + == 0) { + break; + } + } - } else { - if (type != LUA_TNUMBER) { + if (method_index == methods_number) { + /* unknown method */ + method = NGX_HTTP_UNKNOWN; + ngx_method_name = ngx_palloc(r->pool, + sizeof(ngx_str_t)); + if (ngx_method_name == NULL) { + return luaL_error(L, "out of memory"); + } + ngx_method_name->data = ngx_palloc(r->pool, + method_name_length); + if (ngx_method_name->data == NULL) { + return luaL_error(L, "out of memory"); + } + ngx_memcpy(ngx_method_name->data, + lua_method_name, + method_name_length); + ngx_memcpy(ngx_method_name->data + method_name_length, + " ", + 1); + ngx_method_name->len = method_name_length; + } else { + /* the method is a bit field, the first value is + of NGX_HTTP_GET */ + method = NGX_HTTP_GET << method_index; + ngx_method_name = NULL; + } + break; + default: return luaL_error(L, "Bad http request method"); - } - - method = (ngx_uint_t) lua_tonumber(L, -1); } lua_pop(L, 1); @@ -577,7 +646,8 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) ngx_http_set_ctx(sr, sr_ctx, ngx_http_lua_module); - rc = ngx_http_lua_adjust_subrequest(sr, method, always_forward_body, + rc = ngx_http_lua_adjust_subrequest(sr, method, ngx_method_name, + always_forward_body, body, vars_action, extra_vars); if (rc != NGX_OK) { @@ -613,8 +683,9 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) static ngx_int_t ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method, - int always_forward_body, ngx_http_request_body_t *body, - unsigned vars_action, ngx_array_t *extra_vars) + ngx_str_t *method_name, int always_forward_body, + ngx_http_request_body_t *body, unsigned vars_action, + ngx_array_t *extra_vars) { ngx_http_request_t *r; ngx_int_t rc; @@ -665,6 +736,13 @@ ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method, sr->method = method; switch (method) { + case NGX_HTTP_UNKNOWN: + if (method_name == NULL) { + return NGX_ERROR; + } + sr->method_name = *method_name; + break; + case NGX_HTTP_GET: sr->method_name = ngx_http_lua_get_method; break; From c1115829d09cc68d7f0e77d7a2dd901334cd6e48 Mon Sep 17 00:00:00 2001 From: aviram Date: Mon, 30 Sep 2013 11:20:04 +0200 Subject: [PATCH 13/18] Added ngx.location.capture_stream, which enables a single subrequest to be performed in a streaming fashion. --- src/ngx_http_lua_capturefilter.c | 45 ++- src/ngx_http_lua_common.h | 18 ++ src/ngx_http_lua_contentby.c | 58 +++- src/ngx_http_lua_subrequest.c | 478 +++++++++++++++++++++++++++++-- src/ngx_http_lua_subrequest.h | 2 + 5 files changed, 570 insertions(+), 31 deletions(-) diff --git a/src/ngx_http_lua_capturefilter.c b/src/ngx_http_lua_capturefilter.c index 9a945ff80d..01243e2c6e 100644 --- a/src/ngx_http_lua_capturefilter.c +++ b/src/ngx_http_lua_capturefilter.c @@ -16,7 +16,7 @@ #include "ngx_http_lua_util.h" #include "ngx_http_lua_exception.h" #include "ngx_http_lua_subrequest.h" - +#include "ngx_http_lua_contentby.h" ngx_http_output_header_filter_pt ngx_http_lua_next_header_filter; ngx_http_output_body_filter_pt ngx_http_lua_next_body_filter; @@ -104,6 +104,15 @@ ngx_http_lua_capture_header_filter(ngx_http_request_t *r) } +#ifdef NGX_LUA_CAPTURE_DOWN_STREAMING +static ngx_int_t +_should_store_chain_link(ngx_chain_t *in) +{ + return ((in != NULL) && ((in->buf->pos != in->buf->last))); +} +#endif + + static ngx_int_t ngx_http_lua_capture_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { @@ -162,6 +171,40 @@ ngx_http_lua_capture_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ctx->seen_last_for_subreq = 1; } +#ifdef NGX_LUA_CAPTURE_DOWN_STREAMING + if (pr_ctx->async_capture) { + /* In order to wake the parent up, we should call post and not discard + the buffer */ + pr_ctx->current_subrequest = r; /* Required for wake up (?) */ + pr_ctx->current_subrequest_ctx = ctx; /* Required for the buffer */ + + /* XXX: In some cases, pr_ctx->current_subrequest_buffer is being + cleaned by Nginx and buf gets the value 0x1... */ + if (((pr_ctx->current_subrequest_buffer == NULL) + || (pr_ctx->current_subrequest_buffer->buf == (void *) 1)) + && (_should_store_chain_link(in))) { + pr_ctx->current_subrequest_buffer = in; + } + + /* TODO: Is this line needed? */ + r->parent->write_event_handler = ngx_http_lua_content_wev_handler; + + if (!eof) { + pr_ctx->wakeup_subrequest = 1; + /* On EOF, the post subrequest callback is called, and it handles + the setting of the resume handler. The parent request would be + woken up anyway by Nginx. + */ + if (ngx_http_post_request(r->parent, NULL) != NGX_OK) { + return NGX_ERROR; + } + return NGX_OK; + } else { + pr_ctx->wakeup_subrequest = 0; + } + } +#endif + ngx_http_lua_discard_bufs(r->pool, in); return NGX_OK; diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index b03d7bb994..121b28e2a6 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -330,6 +330,19 @@ typedef struct ngx_http_lua_ctx_s { ngx_int_t exit_code; +#ifdef NGX_LUA_CAPTURE_DOWN_STREAMING + ngx_int_t async_capture; + ngx_http_request_t *current_subrequest; + struct ngx_http_lua_ctx_s *current_subrequest_ctx; + ngx_chain_t *current_subrequest_buffer; + ngx_int_t returned_headers; +#endif + + ngx_http_lua_co_ctx_t *calling_coctx; /* co ctx for the caller to location.capture */ + + ngx_http_lua_co_ctx_t *req_body_reader_co_ctx; /* co ctx for the coroutine + reading the request + body */ ngx_http_lua_co_ctx_t *downstream_co_ctx; /* co ctx for the coroutine reading the request body */ @@ -371,6 +384,11 @@ typedef struct ngx_http_lua_ctx_s { unsigned headers_set:1; /* whether the user has set custom response headers */ +#ifdef NGX_LUA_CAPTURE_DOWN_STREAMING + unsigned wakeup_subrequest:1; + unsigned subrequest_yield:1; +#endif + unsigned entered_rewrite_phase:1; unsigned entered_access_phase:1; unsigned entered_content_phase:1; diff --git a/src/ngx_http_lua_contentby.c b/src/ngx_http_lua_contentby.c index c635ab72cb..1168040ed5 100644 --- a/src/ngx_http_lua_contentby.c +++ b/src/ngx_http_lua_contentby.c @@ -20,6 +20,12 @@ static void ngx_http_lua_content_phase_post_read(ngx_http_request_t *r); +#ifdef NGX_LUA_CAPTURE_DOWN_STREAMING +static ngx_int_t _is_chain_valid(ngx_chain_t * cl); +static ngx_int_t _is_last_chain_link(ngx_chain_t * cl); +static ngx_int_t _post_request_if_not_posted(ngx_http_request_t *r, + ngx_http_posted_request_t *pr); +#endif ngx_int_t ngx_http_lua_content_by_chunk(lua_State *L, ngx_http_request_t *r) @@ -115,17 +121,67 @@ ngx_http_lua_content_by_chunk(lua_State *L, ngx_http_request_t *r) } +#ifdef NGX_LUA_CAPTURE_DOWN_STREAMING +static ngx_int_t +_post_request_if_not_posted(ngx_http_request_t *r, ngx_http_posted_request_t *pr) +{ + ngx_http_posted_request_t *p; + + /* Search request in the posted requests list, so that it would not be posted twice. */ + for (p = r->main->posted_requests; p; p = p->next) { + if (p->request == r) { + return NGX_OK; + } + } + + return ngx_http_post_request(r, pr); +} + +static ngx_int_t +_is_chain_valid(ngx_chain_t * cl) +{ + /* For some reason, sometimes when cl->buf is cleaned, 1 is assigned to it. */ + return ((cl != NULL) && (cl->buf != NULL) && (cl->buf != (void *) 1)); +} +static ngx_int_t +_is_last_chain_link(ngx_chain_t * cl) +{ + /* last_in_chain is for subrequests. */ + return cl->buf->last_in_chain || cl->buf->last_buf; +} +#endif + void ngx_http_lua_content_wev_handler(ngx_http_request_t *r) { ngx_http_lua_ctx_t *ctx; + ngx_int_t rc; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } - (void) ctx->resume_handler(r); + rc = ctx->resume_handler(r); + + if (rc == NGX_DONE) { + return; + } + +#ifdef NGX_LUA_CAPTURE_DOWN_STREAMING + if (ctx->current_subrequest && ctx->wakeup_subrequest) { + /* Make sure that the subrequest continues */ + if (_post_request_if_not_posted(ctx->current_subrequest, NULL) != NGX_OK) { + ngx_http_lua_finalize_request(r, NGX_ERROR); + } + /* Don't try to discard the last buffer, as it will cause a NULL dereference... */ + if (_is_chain_valid(ctx->current_subrequest_buffer) && (!_is_last_chain_link(ctx->current_subrequest_buffer))) { + ngx_http_lua_discard_bufs(ctx->current_subrequest->pool, ctx->current_subrequest_buffer); + } + ctx->current_subrequest_buffer = NULL; + ctx->wakeup_subrequest = 0; + } +#endif } diff --git a/src/ngx_http_lua_subrequest.c b/src/ngx_http_lua_subrequest.c index bb10fff4b0..3a3eca01bf 100644 --- a/src/ngx_http_lua_subrequest.c +++ b/src/ngx_http_lua_subrequest.c @@ -70,13 +70,18 @@ static ngx_int_t ngx_http_lua_subrequest_add_extra_vars(ngx_http_request_t *r, static ngx_int_t ngx_http_lua_subrequest(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr, ngx_http_post_subrequest_t *ps, ngx_uint_t flags); -static ngx_int_t ngx_http_lua_subrequest_resume(ngx_http_request_t *r); +ngx_int_t ngx_http_lua_subrequest_resume(ngx_http_request_t *r); static void ngx_http_lua_handle_subreq_responses(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); static void ngx_http_lua_cancel_subreq(ngx_http_request_t *r); static ngx_int_t ngx_http_post_request_to_head(ngx_http_request_t *r); static ngx_int_t ngx_http_lua_copy_in_file_request_body(ngx_http_request_t *r); +#ifdef NGX_LUA_CAPTURE_DOWN_STREAMING +static int ngx_http_lua_ngx_location_capture_stream(lua_State *L); +static int ngx_http_lua_ngx_location_get_subrequest_buffer(lua_State *L); +static ngx_int_t _prepare_subrequest_body_chunk(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, u_char ** dest_buffer, unsigned long * length); +#endif /* ngx.location.capture is just a thin wrapper around * ngx.location.capture_multi */ @@ -108,6 +113,51 @@ ngx_http_lua_ngx_location_capture(lua_State *L) return ngx_http_lua_ngx_location_capture_multi(L); } +#ifdef NGX_LUA_CAPTURE_DOWN_STREAMING +static int +ngx_http_lua_ngx_location_capture_stream(lua_State *L) +{ + int n; + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + + r = ngx_http_lua_get_req(L); + + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + n = lua_gettop(L); + + if (n != 1 && n != 2) { + return luaL_error(L, "expecting one or two arguments"); + } + + ctx->async_capture = 1; + ctx->calling_coctx = ctx->cur_co_ctx; + + lua_createtable(L, n, 0); /* uri opts? table */ + lua_insert(L, 1); /* table uri opts? */ + if (n == 1) { /* table uri */ + lua_rawseti(L, 1, 1); /* table */ + + } else { /* table uri opts */ + lua_rawseti(L, 1, 2); /* table uri */ + lua_rawseti(L, 1, 1); /* table */ + } + + lua_createtable(L, 1, 0); /* table table' */ + lua_insert(L, 1); /* table' table */ + lua_rawseti(L, 1, 1); /* table' */ + + return ngx_http_lua_ngx_location_capture_multi(L); +} +#endif static int ngx_http_lua_ngx_location_capture_multi(lua_State *L) @@ -607,10 +657,349 @@ ngx_http_lua_ngx_location_capture_multi(lua_State *L) ctx->no_abort = 1; +#ifdef NGX_LUA_CAPTURE_DOWN_STREAMING + /* Different resume handler for async requests. */ + if (ctx->async_capture) { + ctx->resume_handler = ngx_http_lua_ngx_capture_buffer_handler; + } +#endif + return lua_yield(L, 0); } +static void +_create_headers_table(lua_State *L, ngx_http_request_t *request) +{ + ngx_http_headers_out_t *sr_headers; + ngx_table_elt_t *header; + ngx_list_part_t *part; + ngx_uint_t i; + u_char buf[sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1]; + + lua_newtable(L); + + sr_headers = &(request->headers_out); + + dd("saving subrequest response headers"); + + part = &sr_headers->headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + dd("checking sr header %.*s", (int) header[i].key.len, + header[i].key.data); + +#if 1 + if (header[i].hash == 0) { + continue; + } +#endif + + header[i].hash = 0; + + dd("pushing sr header %.*s", (int) header[i].key.len, + header[i].key.data); + + lua_pushlstring(L, (char *) header[i].key.data, + header[i].key.len); /* header key */ + lua_pushvalue(L, -1); /* stack: table key key */ + + /* check if header already exists */ + lua_rawget(L, -3); /* stack: table key value */ + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* stack: table key */ + + lua_pushlstring(L, (char *) header[i].value.data, + header[i].value.len); + /* stack: table key value */ + + lua_rawset(L, -3); /* stack: table */ + + } else { + + if (!lua_istable(L, -1)) { /* already inserted one value */ + lua_createtable(L, 4, 0); + /* stack: table key value table */ + + lua_insert(L, -2); /* stack: table key table value */ + lua_rawseti(L, -2, 1); /* stack: table key table */ + + lua_pushlstring(L, (char *) header[i].value.data, + header[i].value.len); + /* stack: table key table value */ + + lua_rawseti(L, -2, lua_objlen(L, -2) + 1); + /* stack: table key table */ + + lua_rawset(L, -3); /* stack: table */ + + } else { + lua_pushlstring(L, (char *) header[i].value.data, + header[i].value.len); + /* stack: table key table value */ + + lua_rawseti(L, -2, lua_objlen(L, -2) + 1); + /* stack: table key table */ + + lua_pop(L, 2); /* stack: table */ + } + } + } + + if (sr_headers->content_type.len) { + lua_pushliteral(L, "Content-Type"); /* header key */ + lua_pushlstring(L, (char *) sr_headers->content_type.data, + sr_headers->content_type.len); /* head key value */ + lua_rawset(L, -3); /* head */ + } + + if (sr_headers->content_length == NULL + && sr_headers->content_length_n >= 0) + { + lua_pushliteral(L, "Content-Length"); /* header key */ + + lua_pushnumber(L, sr_headers->content_length_n); + /* head key value */ + + lua_rawset(L, -3); /* head */ + } + + /* to work-around an issue in ngx_http_static_module + * (github issue #41) */ + if (sr_headers->location && sr_headers->location->value.len) { + lua_pushliteral(L, "Location"); /* header key */ + lua_pushlstring(L, (char *) sr_headers->location->value.data, + sr_headers->location->value.len); + /* head key value */ + lua_rawset(L, -3); /* head */ + } + + if (sr_headers->last_modified_time != -1) { + if (sr_headers->status != NGX_HTTP_OK + && sr_headers->status != NGX_HTTP_PARTIAL_CONTENT + && sr_headers->status != NGX_HTTP_NOT_MODIFIED + && sr_headers->status != NGX_HTTP_NO_CONTENT) + { + sr_headers->last_modified_time = -1; + sr_headers->last_modified = NULL; + } + } + + if (sr_headers->last_modified == NULL + && sr_headers->last_modified_time != -1) + { + (void) ngx_http_time(buf, sr_headers->last_modified_time); + + lua_pushliteral(L, "Last-Modified"); /* header key */ + lua_pushlstring(L, (char *) buf, sizeof(buf)); /* head key value */ + lua_rawset(L, -3); /* head */ + } +} + + +#ifdef NGX_LUA_CAPTURE_DOWN_STREAMING +static int +ngx_http_lua_ngx_location_get_subrequest_buffer(lua_State *L) +{ + ngx_http_request_t *r = NULL; + ngx_http_lua_ctx_t *ctx = NULL; + unsigned long buffer_length = 0; + u_char *current_buffer = NULL; + + r = ngx_http_lua_get_req(L); + + if (r == NULL) { + return luaL_error(L, "no request object found"); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + if ((!ctx->async_capture) || (ctx->current_subrequest_ctx == NULL)) { + return luaL_error(L, "no capture streaming currently"); + } + + /* If there are chunks currently, we should return them immediately. Otherwise, wait.. */ + if (ctx->current_subrequest_ctx->body) { + if (_prepare_subrequest_body_chunk(r, ctx, ¤t_buffer, &buffer_length) != NGX_OK) { + return luaL_error(L, "memory allocation failure"); + } + ctx->subrequest_yield = 0; + lua_pushlstring(L, (char *) current_buffer, buffer_length); + /* Free the buffer that was copied to Lua */ + ngx_pfree(r->pool, current_buffer); + return 1; + } + + /* If the body is NULL and the post subrequest handler was called, this means there are no more buffers. */ + if (ctx->current_subrequest_ctx->run_post_subrequest) { + /* TODO: Tight coupling between the ctx and the request object. */ + ctx->current_subrequest_ctx = NULL; + ctx->current_subrequest = NULL; + ctx->resume_handler = ngx_http_lua_wev_handler; + return 0; + } + + ctx->resume_handler = ngx_http_lua_ngx_capture_buffer_handler; + + ctx->subrequest_yield = 1; + + return lua_yield(L, 0); +} + + +static unsigned long _get_chain_size(ngx_chain_t *in) +{ + ngx_chain_t *current_chain_link = NULL; + unsigned long chain_link_size = 0; + + for (current_chain_link = in, chain_link_size = 0; current_chain_link != NULL ;current_chain_link = current_chain_link->next) { + chain_link_size += current_chain_link->buf->last - current_chain_link->buf->pos; + } + + return chain_link_size; +} + + +static ngx_int_t _prepare_subrequest_body_chunk(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, u_char ** dest_buffer, unsigned long * length) +{ + ngx_chain_t *cl; + unsigned long buffer_length = 0; + u_char *current_buffer; + + /* Prepare the buffer for the callback */ + buffer_length = _get_chain_size(ctx->current_subrequest_ctx->body); + current_buffer = ngx_palloc(r->pool, buffer_length); + if (current_buffer == NULL) { + return NGX_ERROR; + } + + *length = buffer_length; + *dest_buffer = current_buffer; + + /* TODO: Support the limitation of the buffer size */ + for (cl = ctx->current_subrequest_ctx->body; cl; cl = cl->next) { + current_buffer = ngx_copy(current_buffer, cl->buf->pos, cl->buf->last - cl->buf->pos); + /* TODO: Should we acutally call ngx_pfree? According to the post subrequest callback, we shouldn't. */ + cl->buf->last = cl->buf->pos; + ctx->current_subrequest_ctx->body = NULL; + ctx->current_subrequest_ctx->last_body = &(ctx->current_subrequest_ctx->body); + } + + return NGX_OK; +} + +ngx_int_t +ngx_http_lua_ngx_capture_buffer_handler(ngx_http_request_t *r) +{ + ngx_http_lua_ctx_t *ctx; + unsigned long buffer_length = 0; + u_char *current_buffer; + ngx_http_lua_co_ctx_t *current_co_ctx; + lua_State * co; + ngx_int_t rc; + int returned_values; + ngx_http_lua_main_conf_t *lmcf; + + lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + goto error_handling; + } + + if ((!ctx->current_subrequest_ctx) || (!ctx->current_subrequest)) { + return NGX_AGAIN; + } + + current_co_ctx = ctx->cur_co_ctx; + ctx->cur_co_ctx = ctx->calling_coctx; + co = ctx->cur_co_ctx->co; + + if (_prepare_subrequest_body_chunk(r, ctx, ¤t_buffer, &buffer_length) != NGX_OK) { + goto error_handling; + } + + if (buffer_length == 0) { + ctx->current_subrequest_buffer = NULL; + } + + /* Headers are to be returned only once. */ + if (!ctx->returned_headers) { + _create_headers_table(co, ctx->current_subrequest); + lua_pushinteger(co, ctx->current_subrequest->headers_out.status); + ctx->returned_headers = 1; + returned_values = 3; + } else { + returned_values = 1; + } + + lua_pushlstring(co, (char *) current_buffer, buffer_length); + /* Free the buffer that was copied to Lua */ + ngx_pfree(r->pool, current_buffer); + + ctx->subrequest_yield = 0; + rc = ngx_http_lua_run_thread(co, r, ctx, returned_values); + + ctx->cur_co_ctx = current_co_ctx; + + if (rc == NGX_AGAIN) { + /* NGX_AGAIN can be returned if: + - Any built-in Lua function in the current thread needs waiting. + - The function get_subrequest_buffer needs to wait for the subrequest. + + If a Lua function needs waiting, we'll remove our resume handler. + If we need waiting, we shall keep it. + Anyway, if the subrequest isn't over, we should try to wake it up. + */ + if (!ctx->current_subrequest_ctx || !ctx->current_subrequest_ctx->seen_last_for_subreq) { + ctx->wakeup_subrequest = 1; + } else { + ctx->wakeup_subrequest = 0; + } + + if (ctx->subrequest_yield) { + ctx->subrequest_yield = 0; + } else { + ctx->resume_handler = ngx_http_lua_wev_handler; + } + + return NGX_AGAIN; + } + + if (rc == NGX_DONE) { + ngx_http_lua_finalize_request(r, NGX_DONE); + return ngx_http_lua_run_posted_threads(r->connection, lmcf->lua, r, ctx); + } + + if (ctx->entered_content_phase) { + ngx_http_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; + + error_handling: + ngx_http_lua_finalize_request(r, NGX_ERROR); + return NGX_ERROR; +} +#endif + static ngx_int_t ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method, int always_forward_body, ngx_http_request_body_t *body, @@ -975,7 +1364,13 @@ ngx_http_lua_post_subrequest(ngx_http_request_t *r, void *data, ngx_int_t rc) dd("all subrequests are done"); pr_ctx->no_abort = 0; - pr_ctx->resume_handler = ngx_http_lua_subrequest_resume; + if (!pr_ctx->async_capture) { + pr_ctx->resume_handler = ngx_http_lua_subrequest_resume; + } else { + /* XXX: Make sure that the parent request has the correct context. */ + pr_ctx->current_subrequest = r; + pr_ctx->current_subrequest_ctx = ctx; + } pr_ctx->cur_co_ctx = pr_coctx; } @@ -1033,45 +1428,47 @@ ngx_http_lua_post_subrequest(ngx_http_request_t *r, void *data, ngx_int_t rc) body_str->len = len; - if (len == 0) { - body_str->data = NULL; + /* If we're in capture streaming mode, we want to keep the buffer, and to free it from the resume handler's context. */ + if (!pr_ctx->async_capture) { + if (len == 0) { + body_str->data = NULL; - } else { - p = ngx_palloc(r->pool, len); - if (p == NULL) { - return NGX_ERROR; - } - - body_str->data = p; + } else { + p = ngx_palloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } - /* copy from and then free the data buffers */ + body_str->data = p; - for (cl = ctx->body; cl; cl = cl->next) { - p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos); + /* copy from and then free the data buffers */ - cl->buf->last = cl->buf->pos; + for (cl = ctx->body; cl; cl = cl->next) { + p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos); + cl->buf->last = cl->buf->pos; #if 0 - dd("free body chain link buf ASAP"); - ngx_pfree(r->pool, cl->buf->start); + dd("free body chain link buf ASAP"); + ngx_pfree(r->pool, cl->buf->start); #endif + } } - } - if (ctx->body) { + if (ctx->body) { #if defined(nginx_version) && nginx_version >= 1001004 - ngx_chain_update_chains(r->pool, + ngx_chain_update_chains(r->pool, #else - ngx_chain_update_chains( + ngx_chain_update_chains( #endif - &pr_ctx->free_bufs, &pr_ctx->busy_bufs, - &ctx->body, - (ngx_buf_tag_t) &ngx_http_lua_module); + &pr_ctx->free_bufs, &pr_ctx->busy_bufs, + &ctx->body, + (ngx_buf_tag_t) &ngx_http_lua_module); - dd("free bufs: %p", pr_ctx->free_bufs); + dd("free bufs: %p", pr_ctx->free_bufs); + } } - + ngx_http_post_request_to_head(pr); if (r != r->connection->data) { @@ -1417,6 +1814,15 @@ ngx_http_lua_inject_subrequest_api(lua_State *L) lua_pushcfunction(L, ngx_http_lua_ngx_location_capture); lua_setfield(L, -2, "capture"); +#ifdef NGX_LUA_CAPTURE_DOWN_STREAMING + lua_pushcfunction(L, ngx_http_lua_ngx_location_capture_stream); + lua_setfield(L, -2, "capture_stream"); + + /* TODO: Call this 'get_subrequest_body_chunk' */ + lua_pushcfunction(L, ngx_http_lua_ngx_location_get_subrequest_buffer); + lua_setfield(L, -2, "get_subrequest_buffer"); +#endif + lua_pushcfunction(L, ngx_http_lua_ngx_location_capture_multi); lua_setfield(L, -2, "capture_multi"); @@ -1544,7 +1950,7 @@ ngx_http_lua_subrequest(ngx_http_request_t *r, } -static ngx_int_t +ngx_int_t ngx_http_lua_subrequest_resume(ngx_http_request_t *r) { ngx_int_t rc; @@ -1552,6 +1958,7 @@ ngx_http_lua_subrequest_resume(ngx_http_request_t *r) ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_main_conf_t *lmcf; + int returned_values; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { @@ -1569,7 +1976,20 @@ ngx_http_lua_subrequest_resume(ngx_http_request_t *r) dd("nsubreqs: %d", (int) coctx->nsubreqs); - ngx_http_lua_handle_subreq_responses(r, ctx); + if (ctx->async_capture) { + if (!ctx->returned_headers) { + _create_headers_table(coctx->co, ctx->current_subrequest); + lua_pushinteger(coctx->co, ctx->current_subrequest->headers_out.status); + ctx->returned_headers = 1; + returned_values = 3; + } else { + returned_values = 1; + } + lua_pushnil(coctx->co); + } else { + ngx_http_lua_handle_subreq_responses(r, ctx); + returned_values = coctx->nsubreqs; + } dd("free sr_statues/headers/bodies memory ASAP"); @@ -1584,7 +2004,7 @@ ngx_http_lua_subrequest_resume(ngx_http_request_t *r) c = r->connection; - rc = ngx_http_lua_run_thread(lmcf->lua, r, ctx, coctx->nsubreqs); + rc = ngx_http_lua_run_thread(lmcf->lua, r, ctx, returned_values); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run thread returned %d", rc); diff --git a/src/ngx_http_lua_subrequest.h b/src/ngx_http_lua_subrequest.h index aad4236d84..ba7c3b3fc6 100644 --- a/src/ngx_http_lua_subrequest.h +++ b/src/ngx_http_lua_subrequest.h @@ -40,6 +40,8 @@ typedef struct ngx_http_lua_post_subrequest_data_s { } ngx_http_lua_post_subrequest_data_t; +ngx_int_t ngx_http_lua_ngx_capture_buffer_handler(ngx_http_request_t *r); +ngx_int_t ngx_http_lua_subrequest_resume(ngx_http_request_t *r); #endif /* _NGX_HTTP_LUA_SUBREQUEST_H_INCLUDED_ */ From 5e9e3c3b9cb63426c3626cd48261c7d6dbf067f5 Mon Sep 17 00:00:00 2001 From: aviram Date: Mon, 30 Sep 2013 11:53:55 +0200 Subject: [PATCH 14/18] Merge branch 'socket-changes' into patches --- src/ngx_http_lua_common.h | 7 + src/ngx_http_lua_socket_tcp.c | 670 ++++++++++++++++++++++++++++++---- src/ngx_http_lua_socket_tcp.h | 9 +- 3 files changed, 616 insertions(+), 70 deletions(-) diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index 4ec5f51474..b10c1aa2cf 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -204,6 +204,13 @@ typedef struct { ngx_flag_t enforce_content_type; ngx_flag_t correct_location_header; + +#if (NGX_HTTP_SSL) + ngx_ssl_t *ssl; + ngx_flag_t ssl_verify; + ngx_uint_t ssl_verify_depth; + ngx_str_t ssl_trusted_certificate; +#endif } ngx_http_lua_loc_conf_t; diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index d27edbb60a..4250e0602d 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -28,8 +28,11 @@ static int ngx_http_lua_socket_tcp_connect(lua_State *L); static int ngx_http_lua_socket_tcp_receive(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_fake_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_set_receive_timeout(lua_State *L); +static int ngx_http_lua_socket_tcp_set_send_timeout(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); @@ -49,8 +52,16 @@ 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_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_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_connect_retval_handler(ngx_http_request_t *r, @@ -72,6 +83,7 @@ static int ngx_http_lua_socket_error_retval_handler(ngx_http_request_t *r, 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_exact_chunk(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, @@ -106,6 +118,16 @@ static void ngx_http_lua_tcp_resolve_cleanup(void *data); static void ngx_http_lua_tcp_socket_cleanup(void *data); +#if (NGX_HTTP_SSL) + +static int ngx_http_lua_socket_ssl_handshake_ended(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); +static void ngx_http_lua_socket_ssl_handshake(ngx_connection_t *c); +static ngx_int_t ngx_http_lua_socket_ssl_init_connection(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); + +#endif + enum { SOCKET_CTX_INDEX = 1, SOCKET_TIMEOUT_INDEX = 2, @@ -160,6 +182,12 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) 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_set_receive_timeout); + lua_setfield(L, -2, "set_receive_timeout"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_fake_close); + lua_setfield(L, -2, "fake_close"); + lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); @@ -213,6 +241,12 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) 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_set_receive_timeout); + lua_setfield(L, -2, "set_receive_timeout"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_set_send_timeout); + lua_setfield(L, -2, "set_send_timeout"); /* ngx socket mt */ + lua_pushcfunction(L, ngx_http_lua_socket_tcp_getreusedtimes); lua_setfield(L, -2, "getreusedtimes"); @@ -294,6 +328,10 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) const char *msg; ngx_http_lua_co_ctx_t *coctx; +#if (NGX_HTTP_SSL) + ngx_int_t ssl = 0; +#endif + ngx_http_lua_socket_tcp_upstream_t *u; n = lua_gettop(L); @@ -355,7 +393,6 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) break; case LUA_TNIL: - lua_pop(L, 2); break; default: @@ -365,6 +402,24 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) break; } +#if (NGX_HTTP_SSL) + + lua_getfield(L, n, "ssl"); + + if (lua_type(L, -1) != LUA_TBOOLEAN) { + msg = lua_pushfstring(L, "bad \"ssl\" option type: %s", + luaL_typename(L, -1)); + return luaL_argerror(L, n, msg); + } + + ssl = lua_toboolean(L, -1); + lua_pop(L, 1); + +#endif + if (!custom_pool) { + lua_pop(L, 2); + } + n--; } @@ -400,6 +455,8 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) u = lua_touserdata(L, -1); lua_pop(L, 1); + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + if (u) { if (u->waiting) { lua_pushnil(L); @@ -439,12 +496,18 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) ngx_memzero(u, sizeof(ngx_http_lua_socket_tcp_upstream_t)); +#if (NGX_HTTP_SSL) + + if (llcf->ssl) { + u->ssl = ssl; + } + +#endif + coctx = ctx->cur_co_ctx; u->request = r; /* set the controlling request */ - llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); - u->conf = llcf; pc = &u->peer; @@ -561,7 +624,8 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) rctx->timeout = clcf->resolver_timeout; u->resolved->ctx = rctx; - u->co_ctx = ctx->cur_co_ctx; + /* For connect and resolve, use co_ctx_read. */ + u->co_ctx_read = ctx->cur_co_ctx; coctx->data = u; @@ -640,9 +704,9 @@ ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) return; } - lctx->cur_co_ctx = u->co_ctx; + lctx->cur_co_ctx = u->co_ctx_read; - u->co_ctx->cleanup = NULL; + u->co_ctx_read->cleanup = NULL; L = lctx->cur_co_ctx->co; @@ -766,6 +830,104 @@ ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) } +#if (NGX_HTTP_SSL) +static ngx_int_t +ngx_http_lua_socket_ssl_init_connection(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_connection_t *c; + ngx_int_t rc; + + c = u->peer.connection; + + if (ngx_ssl_create_connection(u->conf->ssl, c, + NGX_SSL_BUFFER|NGX_SSL_CLIENT) + != NGX_OK) + { + return luaL_error(L, "error creating ssl session"); + } + /* TODO: Add SSL session reuse here. */ + + rc = ngx_ssl_handshake(c); + + if (rc == NGX_AGAIN) { + c->ssl->handler = ngx_http_lua_socket_ssl_handshake; + u->prepare_retvals = ngx_http_lua_socket_ssl_handshake_ended; + return NGX_AGAIN; + } + + return ngx_http_lua_socket_ssl_handshake_ended(r, u, L); +} + + +static void +ngx_http_lua_socket_ssl_handshake(ngx_connection_t *c) +{ + ngx_http_request_t *r; + ngx_http_lua_socket_tcp_upstream_t *u; + ngx_http_lua_ctx_t *ctx; + + u = c->data; + r = u->request; + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + if (ctx == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "ngx_lua ctx not found"); + return; + } + + c->write->handler = ngx_http_lua_socket_tcp_handler; + c->read->handler = ngx_http_lua_socket_tcp_handler; + + ctx->resume_handler = ngx_http_lua_socket_tcp_resume; + r->write_event_handler(r); + ngx_http_run_posted_requests(r->connection); +} + + +static int +ngx_http_lua_socket_ssl_handshake_ended(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) +{ + ngx_connection_t *c; + ngx_http_lua_loc_conf_t *llcf; + X509 *cert; + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + c = u->peer.connection; + + if (!c->ssl->handshaked) { + lua_pushnil(L); + lua_pushliteral(L, "SSL handshake failed"); + return 2; + } + + if (llcf->ssl_verify) { + if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) { + lua_pushnil(L); + lua_pushliteral(L, "SSL certificate verfication failed"); + return 2; + } + + cert = SSL_get_peer_certificate(c->ssl->connection); + if (cert == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "SSL server did not return certificate"); + return 2; + } + + X509_free(cert); + } + + c->log->action = "SSL connection transaction"; + lua_pushinteger(L, 1); + return 1; +} +#endif + + 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) @@ -898,13 +1060,21 @@ ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); lua_pushnil(L); - lua_pushliteral(L, "failed to handle write event"); + 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; +#if (NGX_HTTP_SSL) + + if (u->ssl) { + return ngx_http_lua_socket_ssl_init_connection(r, u, L); + } + +#endif + lua_pushinteger(L, 1); return 1; } @@ -922,7 +1092,8 @@ ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, r->write_event_handler = ngx_http_core_run_phases; } - u->co_ctx = ctx->cur_co_ctx; + /* For connect and resolve, use co_ctx_read. */ + u->co_ctx_read = ctx->cur_co_ctx; u->waiting = 1; u->prepare_retvals = ngx_http_lua_socket_tcp_connect_retval_handler; @@ -950,8 +1121,12 @@ ngx_http_lua_socket_error_retval_handler(ngx_http_request_t *r, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket error retval handler"); - if (u->co_ctx) { - u->co_ctx->cleanup = NULL; + if (u->co_ctx_read) { + u->co_ctx_read->cleanup = NULL; + } + + if (u->co_ctx_write) { + u->co_ctx_write->cleanup = NULL; } ft_type = u->ft_type; @@ -1014,6 +1189,14 @@ ngx_http_lua_socket_tcp_connect_retval_handler(ngx_http_request_t *r, return ngx_http_lua_socket_error_retval_handler(r, u, L); } +#if (NGX_HTTP_SSL) + + if (u->ssl) { + return ngx_http_lua_socket_ssl_init_connection(r, u, L); + } + +#endif + lua_pushinteger(L, 1); return 1; } @@ -1033,10 +1216,11 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) int typ; ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_co_ctx_t *coctx; + int bsd_read; n = lua_gettop(L); - if (n != 1 && n != 2) { - return luaL_error(L, "expecting 1 or 2 arguments " + if (n != 1 && n != 2 && n != 3) { + return luaL_error(L, "expecting 1, 2 or 3 arguments " "(including the object), but got %d", n); } @@ -1070,9 +1254,9 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) return 2; } - if (u->waiting) { + if (u->waiting_read) { lua_pushnil(L); - lua_pushliteral(L, "socket busy"); + lua_pushliteral(L, "socket already busy reading"); return 2; } @@ -1129,7 +1313,37 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) } #endif - u->input_filter = ngx_http_lua_socket_read_chunk; + bsd_read = 0; + + /* Check if the options table is given. */ + if (n == 3) { + luaL_checktype(L, 3, LUA_TTABLE); + lua_getfield(L, 3, "bsd_read"); + + switch (lua_type(L, -1)) { + case LUA_TNIL: + /* use default value - false */ + break; + + case LUA_TBOOLEAN: + bsd_read = lua_toboolean(L, -1); + break; + + default: + return luaL_error(L, "bad \"bsd_read\" option value type: %s", + luaL_typename(L, -1)); + + } + + lua_pop(L, 1); + } + + if (bsd_read) { + u->input_filter = ngx_http_lua_socket_read_exact_chunk; + } else { + u->input_filter = ngx_http_lua_socket_read_chunk; + } + u->length = (size_t) bytes; u->rest = u->length; @@ -1172,7 +1386,7 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) r->read_event_handler = ngx_http_lua_req_socket_rev_handler; } - u->waiting = 0; + u->waiting_read = 0; rc = ngx_http_lua_socket_tcp_read(r, u); @@ -1194,7 +1408,6 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) /* rc == NGX_AGAIN */ u->read_event_handler = ngx_http_lua_socket_read_handler; - u->write_event_handler = ngx_http_lua_socket_dummy_handler; ctx->cur_co_ctx->cleanup = ngx_http_lua_tcp_socket_cleanup; @@ -1205,8 +1418,8 @@ ngx_http_lua_socket_tcp_receive(lua_State *L) r->write_event_handler = ngx_http_core_run_phases; } - u->co_ctx = ctx->cur_co_ctx; - u->waiting = 1; + u->co_ctx_read = ctx->cur_co_ctx; + u->waiting_read = 1; u->prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler; coctx = ctx->cur_co_ctx; @@ -1264,6 +1477,48 @@ ngx_http_lua_socket_read_chunk(void *data, ssize_t bytes) } +static ngx_int_t +ngx_http_lua_socket_read_exact_chunk(void *data, ssize_t bytes) +{ + ngx_http_lua_socket_tcp_upstream_t *u = data; + + ngx_buf_t *b; +#if (NGX_DEBUG) + ngx_http_request_t *r; + + r = u->request; +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket read max chunk %z", bytes); + + if (bytes == 0) { + u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; + return NGX_ERROR; + } + + b = &u->buffer; + + if (bytes >= (ssize_t) u->rest) { + + u->buf_in->buf->last += u->rest; + b->pos += u->rest; + u->rest = 0; + + return NGX_OK; + } + + /* bytes < u->rest */ + + u->buf_in->buf->last += bytes; + b->pos += bytes; + + u->rest = 0; + + return NGX_OK; +} + + static ngx_int_t ngx_http_lua_socket_read_all(void *data, ssize_t bytes) { @@ -1384,7 +1639,7 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, rev = c->read; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "lua tcp socket read data: waiting: %d", (int) u->waiting); + "lua tcp socket read data: waiting: %d", (int) u->waiting_read); b = &u->buffer; read = 0; @@ -1401,7 +1656,7 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, 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->waiting, (int) u->eof, + "uri:\"%V?%V\"", (int) u->waiting_read, (int) u->eof, &r->uri, &r->args); if (u->body_downstream @@ -1419,12 +1674,12 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, } if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST) { - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); } else { - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); } return NGX_ERROR; @@ -1433,23 +1688,23 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, #if 1 if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + 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_success(r, u); + ngx_http_lua_socket_handle_read_success(r, u); return NGX_OK; } if (rc == NGX_ERROR) { dd("input filter error: ft_type:%d waiting:%d", - (int) u->ft_type, (int) u->waiting); + (int) u->ft_type, (int) u->waiting_read); - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } @@ -1472,8 +1727,8 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, if (size == 0) { rc = ngx_http_lua_socket_add_input_buffer(r, u); if (rc == NGX_ERROR) { - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_NOMEM); + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_NOMEM); return NGX_ERROR; } @@ -1589,7 +1844,7 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, if (llcf->check_client_abort) { - ngx_http_lua_socket_handle_error(r, u, + ngx_http_lua_socket_handle_read_error(r, u, NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); return NGX_ERROR; } @@ -1597,7 +1852,7 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, /* llcf->check_client_abort == 0 */ if (u->body_downstream && r->request_body->rest) { - ngx_http_lua_socket_handle_error(r, u, + ngx_http_lua_socket_handle_read_error(r, u, NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); return NGX_ERROR; } @@ -1613,8 +1868,8 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, if (n == NGX_ERROR) { u->socket_errno = ngx_socket_errno; - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } @@ -1628,8 +1883,8 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, #if 1 if (ngx_handle_read_event(rev, 0) != NGX_OK) { - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_read_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } #endif @@ -1695,9 +1950,9 @@ ngx_http_lua_socket_tcp_send(lua_State *L) return 2; } - if (u->waiting) { + if (u->waiting_write) { lua_pushnil(L); - lua_pushliteral(L, "socket busy"); + lua_pushliteral(L, "socket already busy writing"); return 2; } @@ -1774,7 +2029,7 @@ ngx_http_lua_socket_tcp_send(lua_State *L) /* mimic ngx_http_upstream_init_request here */ #if 1 - u->waiting = 0; + u->waiting_write = 0; #endif ngx_http_lua_probe_socket_tcp_send_start(r, u, b->pos, len); @@ -1804,8 +2059,8 @@ ngx_http_lua_socket_tcp_send(lua_State *L) r->write_event_handler = ngx_http_core_run_phases; } - u->co_ctx = ctx->cur_co_ctx; - u->waiting = 1; + u->co_ctx_write = ctx->cur_co_ctx; + u->waiting_write = 1; u->prepare_retvals = ngx_http_lua_socket_tcp_send_retval_handler; dd("setting data to %p", u); @@ -1943,7 +2198,7 @@ ngx_http_lua_socket_tcp_close(lua_State *L) return 2; } - if (u->waiting) { + if ((u->waiting_read) || (u->waiting_write) || (u->waiting)) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; @@ -1961,6 +2216,42 @@ ngx_http_lua_socket_tcp_close(lua_State *L) return 1; } +static int +ngx_http_lua_socket_tcp_fake_close(lua_State *L) +{ + 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 (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"); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + lua_pop(L, 1); + + u->fake_eof = 1; + coctx = ctx->cur_co_ctx; + ngx_http_lua_socket_read_handler(r, u); + ctx->cur_co_ctx = coctx; + + return 0; +} static int ngx_http_lua_socket_tcp_setoption(lua_State *L) @@ -2010,6 +2301,76 @@ ngx_http_lua_socket_tcp_settimeout(lua_State *L) } +static int +ngx_http_lua_socket_tcp_set_receive_timeout(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 set_receive_timeout: expecting at least 2 " + "arguments (including the object) but seen %d", + lua_gettop(L)); + } + + timeout = (ngx_int_t) lua_tonumber(L, 2); + + lua_rawseti(L, 1, SOCKET_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; + } else { + u->read_timeout = u->conf->read_timeout; + } + } + + return 0; +} + + +static int +ngx_http_lua_socket_tcp_set_send_timeout(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 set_send_timeout: expecting at least 2 " + "arguments (including the object) but seen %d", + lua_gettop(L)); + } + + timeout = (ngx_int_t) lua_tonumber(L, 2); + + lua_rawseti(L, 1, SOCKET_TIMEOUT_INDEX); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u) { + if (timeout > 0) { + u->send_timeout = (ngx_msec_t) timeout; + } else { + u->send_timeout = u->conf->send_timeout; + } + } + + return 0; +} + + static void ngx_http_lua_socket_tcp_handler(ngx_event_t *ev) { @@ -2062,6 +2423,11 @@ ngx_http_lua_socket_read_handler(ngx_http_request_t *r, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket read handler"); + if (u->fake_eof) { + ngx_http_lua_socket_handle_read_error(r, u, NGX_HTTP_LUA_SOCKET_FT_CLOSED); + return; + } + if (c->read->timedout) { c->read->timedout = 0; @@ -2072,7 +2438,7 @@ ngx_http_lua_socket_read_handler(ngx_http_request_t *r, "lua tcp socket read timed out"); } - ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); + ngx_http_lua_socket_handle_read_error(r, u, NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); return; } @@ -2108,7 +2474,7 @@ ngx_http_lua_socket_send_handler(ngx_http_request_t *r, "lua tcp socket write timed out"); } - ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); + ngx_http_lua_socket_handle_write_error(r, u, NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); return; } @@ -2136,8 +2502,8 @@ ngx_http_lua_socket_send(ngx_http_request_t *r, ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { - ngx_http_lua_socket_handle_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_write_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } @@ -2170,12 +2536,12 @@ ngx_http_lua_socket_send(ngx_http_request_t *r, 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_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_write_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } - ngx_http_lua_socket_handle_success(r, u); + ngx_http_lua_socket_handle_write_success(r, u); return NGX_OK; } @@ -2189,7 +2555,7 @@ ngx_http_lua_socket_send(ngx_http_request_t *r, if (n == NGX_ERROR) { u->socket_errno = ngx_socket_errno; - ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_write_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } @@ -2200,13 +2566,12 @@ ngx_http_lua_socket_send(ngx_http_request_t *r, } u->write_event_handler = ngx_http_lua_socket_send_handler; - u->read_event_handler = ngx_http_lua_socket_dummy_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_error(r, u, - NGX_HTTP_LUA_SOCKET_FT_ERROR); + ngx_http_lua_socket_handle_write_error(r, u, + NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } @@ -2225,8 +2590,8 @@ ngx_http_lua_socket_handle_success(ngx_http_request_t *r, u->write_event_handler = ngx_http_lua_socket_dummy_handler; #endif - if (u->co_ctx) { - u->co_ctx->cleanup = NULL; + if (u->co_ctx_read) { + u->co_ctx_read->cleanup = NULL; } #if 0 @@ -2244,7 +2609,85 @@ ngx_http_lua_socket_handle_success(ngx_http_request_t *r, } ctx->resume_handler = ngx_http_lua_socket_tcp_resume; - ctx->cur_co_ctx = u->co_ctx; + ctx->cur_co_ctx = u->co_ctx_read; + + 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_success(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_http_lua_ctx_t *ctx; + +#if 1 + u->read_event_handler = ngx_http_lua_socket_dummy_handler; +#endif + + if (u->co_ctx_read) { + u->co_ctx_read->cleanup = NULL; + } + +#if 0 + if (u->eof) { + ngx_http_lua_socket_tcp_finalize(r, u); + } +#endif + + if (u->waiting_read) { + u->waiting_read = 0; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_http_lua_socket_tcp_resume; + ctx->cur_co_ctx = u->co_ctx_read; + + 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_success(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_http_lua_ctx_t *ctx; + +#if 1 + u->write_event_handler = ngx_http_lua_socket_dummy_handler; +#endif + + if (u->co_ctx_write) { + u->co_ctx_write->cleanup = NULL; + } + +#if 0 + if (u->eof) { + ngx_http_lua_socket_tcp_finalize(r, u); + } +#endif + + if (u->waiting_write) { + u->waiting_write = 0; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_http_lua_socket_tcp_resume; + ctx->cur_co_ctx = u->co_ctx_write; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket waking up the current request"); @@ -2269,8 +2712,8 @@ ngx_http_lua_socket_handle_error(ngx_http_request_t *r, ngx_http_lua_socket_tcp_finalize(r, u); #endif - if (u->co_ctx) { - u->co_ctx->cleanup = NULL; + if (u->co_ctx_read) { + u->co_ctx_read->cleanup = NULL; } u->read_event_handler = ngx_http_lua_socket_dummy_handler; @@ -2285,7 +2728,87 @@ ngx_http_lua_socket_handle_error(ngx_http_request_t *r, } ctx->resume_handler = ngx_http_lua_socket_tcp_resume; - ctx->cur_co_ctx = u->co_ctx; + ctx->cur_co_ctx = u->co_ctx_read; + + 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_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket handle error"); + + u->ft_type |= ft_type; + +#if 0 + ngx_http_lua_socket_tcp_finalize(r, u); +#endif + + if (u->co_ctx_read) { + u->co_ctx_read->cleanup = NULL; + } + + u->read_event_handler = ngx_http_lua_socket_dummy_handler; + + if (u->waiting_read) { + u->waiting_read = 0; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_http_lua_socket_tcp_resume; + ctx->cur_co_ctx = u->co_ctx_read; + + 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_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua tcp socket handle error"); + + u->ft_type |= ft_type; + +#if 0 + ngx_http_lua_socket_tcp_finalize(r, u); +#endif + + if (u->co_ctx_write) { + u->co_ctx_write->cleanup = NULL; + } + + u->write_event_handler = ngx_http_lua_socket_dummy_handler; + + if (u->waiting_write) { + u->waiting_write = 0; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + ctx->resume_handler = ngx_http_lua_socket_tcp_resume; + ctx->cur_co_ctx = u->co_ctx_write; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket waking up the current request"); @@ -2429,6 +2952,15 @@ ngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua close socket connection"); +#if (NGX_HTTP_SSL) + + if (u->peer.connection->ssl) { + u->peer.connection->ssl->no_wait_shutdown = 1; + (void) ngx_ssl_shutdown(u->peer.connection); + } + +#endif + ngx_close_connection(u->peer.connection); u->peer.connection = NULL; @@ -2656,7 +3188,7 @@ ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) return 2; } - if (u->waiting) { + if (u->waiting_read) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; @@ -2718,7 +3250,7 @@ ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) r->read_event_handler = ngx_http_lua_req_socket_rev_handler; } - u->waiting = 0; + u->waiting_read = 0; rc = ngx_http_lua_socket_tcp_read(r, u); @@ -2751,8 +3283,8 @@ ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) r->write_event_handler = ngx_http_core_run_phases; } - u->co_ctx = ctx->cur_co_ctx; - u->waiting = 1; + u->co_ctx_read = ctx->cur_co_ctx; + u->waiting_read = 1; u->prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler; coctx = ctx->cur_co_ctx; @@ -3231,8 +3763,8 @@ ngx_http_lua_req_socket(lua_State *L) } dd("req content length: %d", (int) r->headers_in.content_length_n); - - if (r->headers_in.content_length_n <= 0) { + + if (r->headers_in.content_length_n == 0) { lua_pushnil(L); lua_pushliteral(L, "no body"); return 2; @@ -3449,7 +3981,7 @@ static int ngx_http_lua_socket_tcp_setkeepalive(lua_State *L) return 2; } - if (u->waiting) { + if ((u->waiting_read) || (u->waiting_write) || (u->waiting)) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; diff --git a/src/ngx_http_lua_socket_tcp.h b/src/ngx_http_lua_socket_tcp.h index f977ef7133..518941986f 100644 --- a/src/ngx_http_lua_socket_tcp.h +++ b/src/ngx_http_lua_socket_tcp.h @@ -81,13 +81,20 @@ struct ngx_http_lua_socket_tcp_upstream_s { size_t request_len; ngx_chain_t *request_bufs; - ngx_http_lua_co_ctx_t *co_ctx; + ngx_http_lua_co_ctx_t *co_ctx_read; + ngx_http_lua_co_ctx_t *co_ctx_write; ngx_uint_t reused; unsigned no_close:1; unsigned waiting:1; + unsigned waiting_read:1; + unsigned waiting_write:1; unsigned eof:1; + unsigned fake_eof:1; +#if (NGX_HTTP_SSL) + unsigned ssl:1; +#endif unsigned body_downstream:1; unsigned raw_downstream:1; }; From ae9d9f7a40acce55a8f66bfcf849e2530552f4ab Mon Sep 17 00:00:00 2001 From: aviram Date: Mon, 30 Sep 2013 17:19:53 +0200 Subject: [PATCH 15/18] Added fake_close to the raw socket as well. --- src/ngx_http_lua_socket_tcp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 4250e0602d..99417d0c51 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -210,6 +210,9 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) 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_fake_close); + lua_setfield(L, -2, "fake_close"); + lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); From 7cd287acc713ba1b43b5332cd8365cd8afc17f9b Mon Sep 17 00:00:00 2001 From: aviram Date: Mon, 30 Sep 2013 17:39:47 +0200 Subject: [PATCH 16/18] del_thread doesn't remove the sleeping timer, which may cause segfaults when using ngx.thread.kill_sleeping. Now removing the timer. --- src/ngx_http_lua_util.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 481f95a135..d711ea3f73 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -334,6 +334,10 @@ ngx_http_lua_del_thread(ngx_http_request_t *r, lua_State *L, coctx->co_ref = LUA_NOREF; coctx->co_status = NGX_HTTP_LUA_CO_DEAD; + if (coctx->sleep.timer_set) { + ngx_del_timer(&coctx->sleep); + } + lua_pop(L, 1); } From fff19d63ee27a16c479b24e2c90aac528feb98bf Mon Sep 17 00:00:00 2001 From: aviram Date: Tue, 1 Oct 2013 09:04:02 +0200 Subject: [PATCH 17/18] Setting ctx->headers_set to 1 whenever setting a header successfully (even when enforce_content_type is off). --- src/ngx_http_lua_headers.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c index fb391c111e..fda5390c10 100644 --- a/src/ngx_http_lua_headers.c +++ b/src/ngx_http_lua_headers.c @@ -531,6 +531,7 @@ ngx_http_lua_ngx_header_set(lua_State *L) } } + ctx->headers_set = 1; return 0; } @@ -555,6 +556,7 @@ ngx_http_lua_ngx_header_set(lua_State *L) key.data, (int) rc); } + ctx->headers_set = 1; return 0; } From cf77dc34632059086bfd8d5a1df84feba17043d9 Mon Sep 17 00:00:00 2001 From: aviram Date: Wed, 2 Oct 2013 12:19:10 +0200 Subject: [PATCH 18/18] Now when using the raw request socket, one shouldn't read the entire request body before using it. This makes it possible to implement 'streaming' protocols, in which both client and server can read and write at the same time (such as forward HTTPS proxy with the CONNECT method). --- src/ngx_http_lua_socket_tcp.c | 39 ++++++++++++++--------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 99417d0c51..52a16e4425 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -210,6 +210,12 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) 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_set_receive_timeout); + lua_setfield(L, -2, "set_receive_timeout"); /* ngx socket mt */ + + lua_pushcfunction(L, ngx_http_lua_socket_tcp_set_send_timeout); + lua_setfield(L, -2, "set_send_timeout"); /* ngx socket mt */ + lua_pushcfunction(L, ngx_http_lua_socket_tcp_fake_close); lua_setfield(L, -2, "fake_close"); @@ -1740,25 +1746,7 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, 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 (u->body_downstream || u->raw_downstream) { if (r->request_body->rest == 0) { @@ -1800,7 +1788,7 @@ ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, r->header_in->pos += size; r->request_length += size; - if (r->request_body->rest) { + if (r->request_body->rest >= 0) { r->request_body->rest -= size; } @@ -3685,9 +3673,14 @@ ngx_http_lua_req_socket(lua_State *L) return 2; #else if (!r->request_body) { - lua_pushnil(L); - lua_pushliteral(L, "requesty body not read yet"); - return 2; + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); + if (rb == NULL) { + return luaL_error(L, "out of memory"); + } + + rb->rest = r->headers_in.content_length_n; + + r->request_body = rb; } if (c->buffered) {