From 3f3953599a2de05f82f09cb0efe442d3d58f3e16 Mon Sep 17 00:00:00 2001 From: Aviram Date: Wed, 24 Apr 2013 10:36:55 +0300 Subject: [PATCH 01/21] 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/21] 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/21] 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 d4d2988488c5a6758d2e87a47c592d833513329d Mon Sep 17 00:00:00 2001 From: aviram Date: Sun, 29 Sep 2013 10:54:33 +0200 Subject: [PATCH 04/21] 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 05/21] 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 06/21] 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 07/21] 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 ae9d9f7a40acce55a8f66bfcf849e2530552f4ab Mon Sep 17 00:00:00 2001 From: aviram Date: Mon, 30 Sep 2013 17:19:53 +0200 Subject: [PATCH 08/21] 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 b6df8d6396eccfc5f9b6329b9e3a1150b58c468c Mon Sep 17 00:00:00 2001 From: aviram Date: Thu, 3 Oct 2013 15:32:41 +0200 Subject: [PATCH 09/21] ngx_http_lua_socket_read_handler was declared twice... --- src/ngx_http_lua_socket_tcp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 99417d0c51..e4680fc296 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -36,8 +36,6 @@ 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); -static void ngx_http_lua_socket_read_handler(ngx_http_request_t *r, - ngx_http_lua_socket_tcp_upstream_t *u); static void ngx_http_lua_socket_send_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static void ngx_http_lua_socket_connected_handler(ngx_http_request_t *r, From e9e1dec69b88803a0080cbdbcb28151e31674fd9 Mon Sep 17 00:00:00 2001 From: aviram Date: Thu, 3 Oct 2013 17:40:18 +0200 Subject: [PATCH 10/21] Reverted unnecessary changes. --- src/ngx_http_lua_headers.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c index af56a2d8b0..3fd4672216 100644 --- a/src/ngx_http_lua_headers.c +++ b/src/ngx_http_lua_headers.c @@ -574,19 +574,13 @@ ngx_http_lua_ngx_req_header_clear(lua_State *L) { 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", + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one arguments, but seen %d", lua_gettop(L)); } - if (n == 2) { - lua_pushnil(L); - /* Top element is now 3, replace it with element 3 */ - lua_insert(L, 2); - } else { - lua_pushnil(L); - } - + lua_pushnil(L); + return ngx_http_lua_ngx_req_header_set_helper(L); } @@ -594,7 +588,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) && (lua_gettop(L) != 3)) { + if (lua_gettop(L) != 2) { return luaL_error(L, "expecting two arguments, but seen %d", lua_gettop(L)); } @@ -632,10 +626,9 @@ ngx_http_lua_ngx_req_header_set_helper(lua_State *L) #if 0 /* replace "_" with "-" */ - for (i = 0; i < len; i++) { - if (p[i] == '_') { - p[i] = '-'; - } + for (i = 0; i < len; i++) { + if (p[i] == '_') { + p[i] = '-'; } } #endif From 24cb6857e2a1452733be9185a59f600e962dc029 Mon Sep 17 00:00:00 2001 From: aviram Date: Thu, 3 Oct 2013 17:41:25 +0200 Subject: [PATCH 11/21] Reverted unnecessary changes. --- src/ngx_http_lua_headers.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c index 3fd4672216..c9d89aa941 100644 --- a/src/ngx_http_lua_headers.c +++ b/src/ngx_http_lua_headers.c @@ -572,15 +572,13 @@ ngx_http_lua_ngx_header_set(lua_State *L) static int ngx_http_lua_ngx_req_header_clear(lua_State *L) { - ngx_uint_t n; - n = lua_gettop(L); if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one arguments, but seen %d", lua_gettop(L)); } lua_pushnil(L); - + return ngx_http_lua_ngx_req_header_set_helper(L); } From 61b3feab04c7cea7636e74a84051503917ead205 Mon Sep 17 00:00:00 2001 From: aviram Date: Thu, 3 Oct 2013 17:50:02 +0200 Subject: [PATCH 12/21] Revereted unnecessary changes. --- src/ngx_http_lua_headers.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c index 3a0226517d..3afbd7ba76 100644 --- a/src/ngx_http_lua_headers.c +++ b/src/ngx_http_lua_headers.c @@ -562,20 +562,12 @@ ngx_http_lua_ngx_header_set(lua_State *L) static int ngx_http_lua_ngx_req_header_clear(lua_State *L) { - 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", + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting one arguments, but seen %d", lua_gettop(L)); } - if (n == 2) { - lua_pushnil(L); - /* Top element is now 3, replace it with element 3 */ - lua_insert(L, 2); - } else { - lua_pushnil(L); - } + lua_pushnil(L); return ngx_http_lua_ngx_req_header_set_helper(L); } @@ -584,7 +576,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) && (lua_gettop(L) != 3)) { + if (lua_gettop(L) != 2) { return luaL_error(L, "expecting two arguments, but seen %d", lua_gettop(L)); } @@ -622,10 +614,9 @@ ngx_http_lua_ngx_req_header_set_helper(lua_State *L) #if 0 /* replace "_" with "-" */ - for (i = 0; i < len; i++) { - if (p[i] == '_') { - p[i] = '-'; - } + for (i = 0; i < len; i++) { + if (p[i] == '_') { + p[i] = '-'; } } #endif From 1ba8d55edd693501d9bfb94b983f77d6089f57fa Mon Sep 17 00:00:00 2001 From: aviram Date: Thu, 17 Oct 2013 18:31:03 +0200 Subject: [PATCH 13/21] Setting `ctx->writing_raw_req_socket` to 0 when the raw downstream socket is finalized, so that if `ngx_http_lua_wev_handler` runs after the raw downstream socket is closed, it won't try to call its read/write handler. --- src/ngx_http_lua_socket_tcp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index e4680fc296..a8fcf604cc 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -2937,6 +2937,7 @@ ngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r, } u->peer.connection = NULL; + ctx->writing_raw_req_socket = 0; return; } From 5627c38dcf6d1bcbe6583ba185c14b6e934c64ee Mon Sep 17 00:00:00 2001 From: aviram Date: Wed, 23 Oct 2013 14:15:56 +0200 Subject: [PATCH 14/21] Bugfix - correcting u->prepare_retvals on each read/write success/error handling functions, as if both read and write are done simultaenously, they may override each others preapre_retvals function. --- src/ngx_http_lua_socket_tcp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index a8fcf604cc..1954d1c8ab 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -2650,6 +2650,7 @@ ngx_http_lua_socket_handle_read_success(ngx_http_request_t *r, ctx->resume_handler = ngx_http_lua_socket_tcp_resume; ctx->cur_co_ctx = u->co_ctx_read; + u->prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket waking up the current request"); @@ -2689,6 +2690,7 @@ ngx_http_lua_socket_handle_write_success(ngx_http_request_t *r, ctx->resume_handler = ngx_http_lua_socket_tcp_resume; ctx->cur_co_ctx = u->co_ctx_write; + u->prepare_retvals = ngx_http_lua_socket_tcp_send_retval_handler; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket waking up the current request"); @@ -2770,6 +2772,7 @@ ngx_http_lua_socket_handle_read_error(ngx_http_request_t *r, ctx->resume_handler = ngx_http_lua_socket_tcp_resume; ctx->cur_co_ctx = u->co_ctx_read; + u->prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket waking up the current request"); @@ -2810,6 +2813,7 @@ ngx_http_lua_socket_handle_write_error(ngx_http_request_t *r, ctx->resume_handler = ngx_http_lua_socket_tcp_resume; ctx->cur_co_ctx = u->co_ctx_write; + u->prepare_retvals = ngx_http_lua_socket_tcp_send_retval_handler; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket waking up the current request"); From 3bd5acc63dd0548aca20289dbe73f9d57039fdc4 Mon Sep 17 00:00:00 2001 From: aviram Date: Tue, 10 Dec 2013 17:20:47 +0200 Subject: [PATCH 15/21] `ngx_http_lua_req_socket_rev_handler()` could be called when `u->peer.connection` is NULL in case there was an error in the client socket, causing a SEGFAULT. --- src/ngx_http_lua_socket_tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 1954d1c8ab..e66bb00c5e 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -3898,7 +3898,7 @@ ngx_http_lua_req_socket_rev_handler(ngx_http_request_t *r) coctx = ctx->downstream_co_ctx; u = coctx->data; - if (u) { + if (u && u->peer.connection) { u->read_event_handler(r, u); } } From 228486782efc698f3fc02096ca1c5cafb2ddce48 Mon Sep 17 00:00:00 2001 From: aviramc Date: Wed, 18 Dec 2013 11:58:21 +0200 Subject: [PATCH 16/21] Author: aviramc Fixed merge issue (some function that were moved to a different file became duplicate). --- src/ngx_http_lua_module.c | 70 --------------------------------------- 1 file changed, 70 deletions(-) diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index 9f964c1153..cf2d91a6bc 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -36,9 +36,6 @@ 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); static ngx_int_t ngx_http_lua_init(ngx_conf_t *cf); static char *ngx_http_lua_lowat_check(ngx_conf_t *cf, void *post, void *data); @@ -845,71 +842,4 @@ ngx_http_lua_set_ssl(ngx_conf_t *cf, ngx_http_lua_loc_conf_t *plcf) #endif -static void -ngx_http_lua_cleanup_vm(void *data) -{ - lua_State *L = data; - - if (L != NULL) { - lua_close(L); - - dd("Lua VM closed!"); - } -} - - -static char * -ngx_http_lua_init_vm(ngx_conf_t *cf, ngx_http_lua_main_conf_t *lmcf) -{ - ngx_pool_cleanup_t *cln; - ngx_http_lua_preload_hook_t *hook; - lua_State *L; - ngx_uint_t i; - - ngx_http_lua_content_length_hash = - ngx_http_lua_hash_literal("content-length"); - - ngx_http_lua_location_hash = ngx_http_lua_hash_literal("location"); - - /* add new cleanup handler to config mem pool */ - cln = ngx_pool_cleanup_add(cf->pool, 0); - if (cln == NULL) { - return NGX_CONF_ERROR; - } - - /* create new Lua VM instance */ - lmcf->lua = ngx_http_lua_new_state(cf, lmcf); - if (lmcf->lua == NULL) { - return NGX_CONF_ERROR; - } - - /* register cleanup handler for Lua VM */ - cln->handler = ngx_http_lua_cleanup_vm; - cln->data = lmcf->lua; - - if (lmcf->preload_hooks) { - - /* register the 3rd-party module's preload hooks */ - - L = lmcf->lua; - - lua_getglobal(L, "package"); - lua_getfield(L, -1, "preload"); - - hook = lmcf->preload_hooks->elts; - - for (i = 0; i < lmcf->preload_hooks->nelts; i++) { - - ngx_http_lua_probe_register_preload_package(L, hook[i].package); - - lua_pushcfunction(L, hook[i].loader); - lua_setfield(L, -2, (char *) hook[i].package); - } - - lua_pop(L, 2); - } - - return NGX_CONF_OK; -} - /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ From 6921aa545834ba1284ff4bf21085ca3ebb14c23b Mon Sep 17 00:00:00 2001 From: aviramc Date: Mon, 23 Dec 2013 16:18:17 +0200 Subject: [PATCH 17/21] Now considering timeout error in handshake. Also closing the connection if any error has happened during the handshake. --- src/ngx_http_lua_socket_tcp.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 577d6b44f8..10df94eb30 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -901,7 +901,13 @@ ngx_http_lua_socket_ssl_handshake_ended(ngx_http_request_t *r, if (!c->ssl->handshaked) { lua_pushnil(L); - lua_pushliteral(L, "SSL handshake failed"); + if (c->read->timedout) { + lua_pushliteral(L, "SSL handshake timed out"); + } else { + lua_pushliteral(L, "SSL handshake failed"); + } + ngx_close_connection(u->peer.connection); + u->peer.connection = NULL; return 2; } From 701f6dba831d1ccb4dcc15728411ffb448faf0bc Mon Sep 17 00:00:00 2001 From: aviramc Date: Tue, 24 Dec 2013 13:12:32 +0200 Subject: [PATCH 18/21] Added name verification to the cosockets SSL API. The name to verify is an optional argument in the options table of tcpsock:connect() --- src/ngx_http_lua_socket_tcp.c | 174 +++++++++++++++++++++++++++++++++- src/ngx_http_lua_socket_tcp.h | 4 + 2 files changed, 176 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 10df94eb30..32b6c03cb1 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -124,6 +124,13 @@ 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); +/* TODO: When these functions are either built-in Nginx's OpenSSL module or + in the OpenSSL library itself, we should remove the following functions. + */ +static ngx_int_t ngx_http_lua_ssl_verify_name(X509 *cert, ngx_str_t *name); +static int ngx_http_lua_ssl_host_wildcard_match(ASN1_STRING *pattern, + ngx_str_t *hostname); + #endif enum { @@ -331,6 +338,7 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) #if (NGX_HTTP_SSL) ngx_int_t ssl = 0; + ngx_str_t ssl_verify_name = {0, NULL}; #endif ngx_http_lua_socket_tcp_upstream_t *u; @@ -415,8 +423,30 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) ssl = lua_toboolean(L, -1); lua_pop(L, 1); + + + /* TODO: Maybe if this is passed as true, used the host. + */ + lua_getfield(L, n, "ssl_verify_name"); + + p = (u_char *) lua_tolstring(L, -1, &len); + + if (p != NULL) { + ssl_verify_name.data = ngx_palloc(r->pool, len + 1); + + if (ssl_verify_name.data == NULL) { + return luaL_error(L, "out of memory"); + } + + ngx_memcpy(ssl_verify_name.data, p, len); + ssl_verify_name.data[len] = '\0'; + ssl_verify_name.len = len; + } + + lua_pop(L, 1); + +#endif -#endif if (!custom_pool) { lua_pop(L, 2); } @@ -501,6 +531,11 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) if (llcf->ssl) { u->ssl = ssl; + + if (ssl && ssl_verify_name.data) { + u->ssl_verify_name.data = ssl_verify_name.data; + u->ssl_verify_name.len = ssl_verify_name.len; + } } #endif @@ -924,7 +959,16 @@ ngx_http_lua_socket_ssl_handshake_ended(ngx_http_request_t *r, lua_pushliteral(L, "SSL server did not return certificate"); return 2; } - + + if (u->ssl_verify_name.data + && ngx_http_lua_ssl_verify_name(cert, &u->ssl_verify_name) != NGX_OK) + { + X509_free(cert); + lua_pushnil(L); + lua_pushliteral(L, "SSL certificate name validation error"); + return 2; + } + X509_free(cert); } @@ -932,6 +976,132 @@ ngx_http_lua_socket_ssl_handshake_ended(ngx_http_request_t *r, lua_pushinteger(L, 1); return 1; } + +static int +ngx_http_lua_ssl_host_wildcard_match(ASN1_STRING *pattern, + ngx_str_t *hostname) +{ + int n; + u_char *p; + u_char *wp; + + /* sanity check */ + if (!pattern + || pattern->length <= 0 + || !hostname + || !hostname->len) + { + return 0; + } + + /* trivial case */ + if (ngx_strncasecmp((u_char *) pattern->data, + hostname->data, + hostname->len) + == 0) + { + return 1; + } + + /* simple wildcard matching - only in the beginning of the string. */ + if (pattern->length > 2 + && pattern->data[0] == '*' + && pattern->data[1] == '.') + { + + wp = (u_char *) (pattern->data + 1); + + p = ngx_strlchr(hostname->data, + hostname->data + hostname->len, + '.'); + + /* + * If the pattern begings with "*." and the hostname consists of + * a top level domain, compare the pattern to the top level domain. + */ + if (p != NULL) { + n = hostname->len - (int) (p - hostname->data); + + if (n == pattern->length - 1 + && ngx_strncasecmp(wp, + p, + pattern->length - 1) + == 0) + { + return 1; + } + } + } + + return 0; +} + + +static ngx_int_t +ngx_http_lua_ssl_verify_name(X509 *cert, ngx_str_t *name) +{ + X509_NAME_ENTRY *ne; + GENERAL_NAMES *gens; + GENERAL_NAME *gen; + ASN1_STRING *cstr; + X509_NAME *sn; + int rc; + int i; + + /* based on OpenSSL's do_x509_check */ + + gens = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + if (gens) { + + rc = 0; + + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + + gen = sk_GENERAL_NAME_value(gens, i); + + /* we only check for name */ + switch (gen->type) { + + case GEN_DNS: + cstr = gen->d.dNSName; + rc = ngx_http_lua_ssl_host_wildcard_match(cstr, + name); + break; + + default: + cstr = NULL; + rc = 0; + } + + if (rc) { + break; + } + + } + + GENERAL_NAMES_free(gens); + + if (rc) { + return NGX_OK; + } + } + + sn = X509_get_subject_name(cert); + i = X509_NAME_get_index_by_NID(sn, NID_commonName, -1); + while (i >= 0) { + ne = X509_NAME_get_entry(sn, i); + cstr = X509_NAME_ENTRY_get_data(ne); + + if (ngx_http_lua_ssl_host_wildcard_match(cstr, name)) { + return NGX_OK; + } + + i = X509_NAME_get_index_by_NID(sn, NID_commonName, i); + } + + return NGX_ERROR; +} #endif diff --git a/src/ngx_http_lua_socket_tcp.h b/src/ngx_http_lua_socket_tcp.h index 69bdfe6f9e..92d9c654db 100644 --- a/src/ngx_http_lua_socket_tcp.h +++ b/src/ngx_http_lua_socket_tcp.h @@ -89,6 +89,10 @@ struct ngx_http_lua_socket_tcp_upstream_s { ngx_uint_t reused; +#if (NGX_HTTP_SSL) + ngx_str_t ssl_verify_name; +#endif + unsigned no_close:1; unsigned waiting:1; unsigned waiting_read:1; From dd99124de84cbcd38aeeffb2be0b654532a5d111 Mon Sep 17 00:00:00 2001 From: aviramc Date: Tue, 24 Dec 2013 15:26:41 +0200 Subject: [PATCH 19/21] Added CRL to the Lua options, made some refactoring. --- src/ngx_http_lua_common.h | 1 + src/ngx_http_lua_module.c | 29 ++++++++++++++++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index 20a87ad3c3..cfe6953fd6 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -216,6 +216,7 @@ typedef struct { ngx_flag_t ssl_verify; ngx_uint_t ssl_verify_depth; ngx_str_t ssl_trusted_certificate; + ngx_str_t ssl_crl; #endif ngx_flag_t use_default_type; diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index cf2d91a6bc..e551d0f67e 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -368,6 +368,13 @@ static ngx_command_t ngx_http_lua_cmds[] = { offsetof(ngx_http_lua_loc_conf_t, ssl_trusted_certificate), NULL }, + { ngx_string("lua_ssl_crl"), + 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_crl), + NULL }, + #endif { ngx_string("lua_use_default_type"), @@ -779,8 +786,12 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 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, ""); + prev->ssl_trusted_certificate, ""); + ngx_conf_merge_str_value(conf->ssl_crl, + prev->ssl_crl, ""); + /* TODO: Maybe the verification should be an option for the + tcpsock:connect() method. */ if (conf->ssl_verify) { if (conf->ssl_trusted_certificate.len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -790,16 +801,20 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } - } - if (ngx_ssl_trusted_certificate(cf, conf->ssl, + if (ngx_ssl_trusted_certificate(cf, conf->ssl, &conf->ssl_trusted_certificate, conf->ssl_verify_depth) - != NGX_OK) - { - return NGX_CONF_ERROR; + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_ssl_crl(cf, conf->ssl, &conf->ssl_crl) != NGX_OK) { + return NGX_CONF_ERROR; + } } - + #endif return NGX_CONF_OK; From 764f739240405989d1958a0c7df568f3f464d85f Mon Sep 17 00:00:00 2001 From: aviramc Date: Tue, 24 Dec 2013 16:22:16 +0200 Subject: [PATCH 20/21] Fixed typo. --- src/ngx_http_lua_socket_tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 07a24e4349..91e0e65926 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -1010,7 +1010,7 @@ ngx_http_lua_socket_ssl_handshake_ended(ngx_http_request_t *r, 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"); + lua_pushliteral(L, "SSL certificate verification failed"); return 2; } From 2254d008bdd774df659209fe8c1034167fec18a5 Mon Sep 17 00:00:00 2001 From: aviramc Date: Tue, 24 Dec 2013 16:49:33 +0200 Subject: [PATCH 21/21] Added the set timeout methods for the raw request socket. --- src/ngx_http_lua_socket_tcp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 91e0e65926..a3a1b60fcd 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -215,6 +215,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");