From 635b6e864c385bc16125bd43a2ffe8b4560ca9fa Mon Sep 17 00:00:00 2001 From: Guy Lewin Date: Tue, 7 Jul 2020 04:11:53 -0400 Subject: [PATCH 1/7] feature: supported receiveany on ngx.req.socket(true?) socks (#1623) --- README.markdown | 4 +- doc/HttpLuaModule.wiki | 4 +- src/ngx_http_lua_socket_tcp.c | 10 +++- t/062-count.t | 4 +- t/067-req-socket.t | 70 ++++++++++++++++++++++++++ t/116-raw-req-socket.t | 95 ++++++++++++++++++++++++++++++++++- 6 files changed, 178 insertions(+), 9 deletions(-) diff --git a/README.markdown b/README.markdown index ecaec83a5a..4046dfe15f 100644 --- a/README.markdown +++ b/README.markdown @@ -5181,7 +5181,7 @@ ngx.req.socket **context:** *rewrite_by_lua*, access_by_lua*, content_by_lua** -Returns a read-only cosocket object that wraps the downstream connection. Only [receive](#tcpsockreceive) and [receiveuntil](#tcpsockreceiveuntil) methods are supported on this object. +Returns a read-only cosocket object that wraps the downstream connection. Only [receive](#tcpsockreceive), [receiveany](#tcpsockreceiveany) and [receiveuntil](#tcpsockreceiveuntil) methods are supported on this object. In case of error, `nil` will be returned as well as a string describing the error. @@ -5190,7 +5190,7 @@ The socket object returned by this method is usually used to read the current re If any request body data has been pre-read into the Nginx core request header buffer, the resulting cosocket object will take care of this to avoid potential data loss resulting from such pre-reading. Chunked request bodies are not yet supported in this API. -Since the `v0.9.0` release, this function accepts an optional boolean `raw` argument. When this argument is `true`, this function returns a full-duplex cosocket object wrapping around the raw downstream connection socket, upon which you can call the [receive](#tcpsockreceive), [receiveuntil](#tcpsockreceiveuntil), and [send](#tcpsocksend) methods. +Since the `v0.9.0` release, this function accepts an optional boolean `raw` argument. When this argument is `true`, this function returns a full-duplex cosocket object wrapping around the raw downstream connection socket, upon which you can call the [receive](#tcpsockreceive), [receiveany](#tcpsockreceiveany), [receiveuntil](#tcpsockreceiveuntil), and [send](#tcpsocksend) methods. When the `raw` argument is `true`, it is required that no pending data from any previous [ngx.say](#ngxsay), [ngx.print](#ngxprint), or [ngx.send_headers](#ngxsend_headers) calls exists. So if you have these downstream output calls previously, you should call [ngx.flush(true)](#ngxflush) before calling `ngx.req.socket(true)` to ensure that there is no pending output data. If the request body has not been read yet, then this "raw socket" can also be used to read the request body. diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 215a5d5196..37d8a1367b 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -4342,7 +4342,7 @@ See also [[#ngx.req.init_body|ngx.req.init_body]]. '''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*'' -Returns a read-only cosocket object that wraps the downstream connection. Only [[#tcpsock:receive|receive]] and [[#tcpsock:receiveuntil|receiveuntil]] methods are supported on this object. +Returns a read-only cosocket object that wraps the downstream connection. Only [[#tcpsock:receive|receive]], [[#tcpsock:receiveany|receiveany]] and [[#tcpsock:receiveuntil|receiveuntil]] methods are supported on this object. In case of error, nil will be returned as well as a string describing the error. @@ -4351,7 +4351,7 @@ The socket object returned by this method is usually used to read the current re If any request body data has been pre-read into the Nginx core request header buffer, the resulting cosocket object will take care of this to avoid potential data loss resulting from such pre-reading. Chunked request bodies are not yet supported in this API. -Since the v0.9.0 release, this function accepts an optional boolean raw argument. When this argument is true, this function returns a full-duplex cosocket object wrapping around the raw downstream connection socket, upon which you can call the [[#tcpsock:receive|receive]], [[#tcpsock:receiveuntil|receiveuntil]], and [[#tcpsock:send|send]] methods. +Since the v0.9.0 release, this function accepts an optional boolean raw argument. When this argument is true, this function returns a full-duplex cosocket object wrapping around the raw downstream connection socket, upon which you can call the [[#tcpsock:receive|receive]], [[#tcpsock:receiveany|receiveany]], [[#tcpsock:receiveuntil|receiveuntil]], and [[#tcpsock:send|send]] methods. When the raw argument is true, it is required that no pending data from any previous [[#ngx.say|ngx.say]], [[#ngx.print|ngx.print]], or [[#ngx.send_headers|ngx.send_headers]] calls exists. So if you have these downstream output calls previously, you should call [[#ngx.flush|ngx.flush(true)]] before calling ngx.req.socket(true) to ensure that there is no pending output data. If the request body has not been read yet, then this "raw socket" can also be used to read the request body. diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 472fff0f87..c2776d83a7 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -255,11 +255,14 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) /* {{{req socket object metatable */ lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( req_socket_metatable_key)); - lua_createtable(L, 0 /* narr */, 5 /* nrec */); + lua_createtable(L, 0 /* narr */, 6 /* nrec */); lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); lua_setfield(L, -2, "receive"); + lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveany); + lua_setfield(L, -2, "receiveany"); + lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil); lua_setfield(L, -2, "receiveuntil"); @@ -278,11 +281,14 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) /* {{{raw req socket object metatable */ lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( raw_req_socket_metatable_key)); - lua_createtable(L, 0 /* narr */, 6 /* nrec */); + lua_createtable(L, 0 /* narr */, 7 /* nrec */); lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); lua_setfield(L, -2, "receive"); + lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveany); + lua_setfield(L, -2, "receiveany"); + lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil); lua_setfield(L, -2, "receiveuntil"); diff --git a/t/062-count.t b/t/062-count.t index ffb769a50d..841923b86d 100644 --- a/t/062-count.t +++ b/t/062-count.t @@ -259,7 +259,7 @@ n = 10 POST /test hello world --- response_body -n = 5 +n = 6 --- no_error_log [error] @@ -512,7 +512,7 @@ n = 6 --- request GET /test --- response_body -n = 6 +n = 7 --- no_error_log [error] diff --git a/t/067-req-socket.t b/t/067-req-socket.t index 6593360e4c..c5b2631e5e 100644 --- a/t/067-req-socket.t +++ b/t/067-req-socket.t @@ -1096,3 +1096,73 @@ done --- grep_error_log_out lua finalize socket GC cycle done + + + +=== TEST 18: receiveany +--- config + location = /t { + content_by_lua_block { + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", ngx.var.server_port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local bytes, err = sock:send("POST /back HTTP/1.0\r\nHost: localhost\r\nContent-Length: 1024\r\n\r\nabc") + if not bytes then + ngx.say("failed to send: ", err) + end + + ngx.sleep(0.2) + + local bytes, err = sock:send("hello world\n") + if not bytes then + ngx.say("failed to send: ", err) + end + + local reader = sock:receiveuntil("\r\n\r\n") + local header, err = reader() + if not header then + ngx.say("failed to receive header: ", err) + return + end + + local line, err = sock:receive() + if not line then + ngx.say("failed to receive line: ", err) + return + end + ngx.say("received: ", line) + } + } + + location = /back { + content_by_lua_block { + ngx.send_headers() + ngx.flush(true) + + local sock, err = ngx.req.socket() + + if not sock then + ngx.say("failed to get socket: ", err) + return nil + end + + local data, err = sock:receiveany(4096) + if not data then + ngx.say("err: ", err) + return nil + end + + ngx.say("received: ", data) + } + } + +--- request +GET /t +--- response_body +received: received: abc +--- no_error_log +[error] diff --git a/t/116-raw-req-socket.t b/t/116-raw-req-socket.t index ab06ee4a31..4dfb92664e 100644 --- a/t/116-raw-req-socket.t +++ b/t/116-raw-req-socket.t @@ -4,7 +4,7 @@ use Test::Nginx::Socket::Lua; repeat_each(2); -plan tests => repeat_each() * 40; +plan tests => repeat_each() * 43; our $HtmlDir = html_dir; @@ -876,3 +876,96 @@ request body: hey, hello world --- no_error_log [error] [alert] + + + +=== TEST 16: receiveany +--- config + server_tokens off; + location = /t { + #set $port 5000; + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = ngx.var.port + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local req = "GET /mysock HTTP/1.1\r\nUpgrade: mysock\r\nHost: localhost\r\nConnection: close\r\n\r\nhello" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + -- Will return to I/O loop, causing receiveany() in /mysock location to be called + ngx.sleep(1) + + local bytes, err = sock:send(", world") + if not bytes then + ngx.say("failed to send packet 1: ", err) + return + end + + local reader = sock:receiveuntil("\r\n\r\n") + local data, err, partial = reader() + if not data then + ngx.say("no response header found") + return + end + + local msg, err = sock:receive() + if not msg then + ngx.say("failed to receive: ", err) + return + end + + ngx.say("msg: ", msg) + + ok, err = sock:close() + if not ok then + ngx.say("failed to close socket: ", err) + return + end + } + } + + location = /mysock { + content_by_lua_block { + ngx.status = 101 + ngx.send_headers() + ngx.flush(true) + ngx.req.read_body() + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err) + return + end + + local data, err = sock:receiveany(1024) + if not data then + ngx.log(ngx.ERR, "server: failed to receive: ", err) + return + end + + local bytes, err = sock:send("1: received: " .. data .. "\n") + if not bytes then + ngx.log(ngx.ERR, "server: failed to send: ", err) + return + end + } + more_clear_headers Date; + } + +--- request +GET /t +--- response_body +msg: 1: received: hello +--- no_error_log +[error] From 4368a8ea2723e4d55d9ea362e758dc9bf0111ebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E6=B3=BD=E8=BD=A9?= Date: Fri, 10 Jul 2020 17:20:02 +0800 Subject: [PATCH 2/7] bugfix: macro contains operator should be wrapped inside a bracket. The bracket is missing in the previous refactor. --- src/ngx_http_lua_util.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h index 2d928f96cf..57c0814bfb 100644 --- a/src/ngx_http_lua_util.h +++ b/src/ngx_http_lua_util.h @@ -31,12 +31,12 @@ #define NGX_HTTP_LUA_ESCAPE_HEADER_VALUE 8 -#define NGX_HTTP_LUA_CONTEXT_YIELDABLE NGX_HTTP_LUA_CONTEXT_REWRITE \ +#define NGX_HTTP_LUA_CONTEXT_YIELDABLE (NGX_HTTP_LUA_CONTEXT_REWRITE \ | NGX_HTTP_LUA_CONTEXT_ACCESS \ | NGX_HTTP_LUA_CONTEXT_CONTENT \ | NGX_HTTP_LUA_CONTEXT_TIMER \ | NGX_HTTP_LUA_CONTEXT_SSL_CERT \ - | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH + | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH) /* key in Lua vm registry for all the "ngx.ctx" tables */ From af748420195c6fbe05e991d4d97b91eb08af120e Mon Sep 17 00:00:00 2001 From: Jinhua Tan <312841925@qq.com> Date: Thu, 16 Jul 2020 14:36:19 +0800 Subject: [PATCH 3/7] test: fix: add lua-resty-string to lua_package_path (#1748) --- .travis.yml | 1 + t/000--init.t | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fc2e4c20bf..bf433bef5e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -86,6 +86,7 @@ install: - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache - git clone https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql + - git clone https://github.com/openresty/lua-resty-string.git ../lua-resty-string - git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git luajit2 diff --git a/t/000--init.t b/t/000--init.t index 5865755372..0dd08fe188 100644 --- a/t/000--init.t +++ b/t/000--init.t @@ -10,7 +10,8 @@ $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; $ENV{TEST_NGINX_MYSQL_PORT} ||= 3306; our $http_config = <<'_EOC_'; - lua_package_path "../lua-resty-mysql/lib/?.lua;;"; + # lua-resty-string is required for lua-resty-mysql + lua_package_path "../lua-resty-mysql/lib/?.lua;../lua-resty-string/lib/?.lua;;"; _EOC_ no_shuffle(); From b050e1c944d38e0956da851b2c40ce9381a63e88 Mon Sep 17 00:00:00 2001 From: lijunlong Date: Sun, 19 Jul 2020 17:17:51 +0800 Subject: [PATCH 4/7] sytle: added blank line after code block. (#1752) --- src/ngx_http_lua_args.c | 2 ++ src/ngx_http_lua_directive.c | 2 ++ src/ngx_http_lua_headers.c | 1 + src/ngx_http_lua_headers_in.c | 3 +++ src/ngx_http_lua_initworkerby.c | 1 + src/ngx_http_lua_log.c | 1 + src/ngx_http_lua_req_body.c | 3 +++ src/ngx_http_lua_socket_tcp.c | 1 + src/ngx_http_lua_string.c | 3 +++ src/ngx_http_lua_util.c | 3 +++ 10 files changed, 20 insertions(+) diff --git a/src/ngx_http_lua_args.c b/src/ngx_http_lua_args.c index 7b7d5eae6c..1280846545 100644 --- a/src/ngx_http_lua_args.c +++ b/src/ngx_http_lua_args.c @@ -55,6 +55,7 @@ ngx_http_lua_escape_args(u_char *dst, u_char *src, size_t size) if (escape[*src >> 5] & (1 << (*src & 0x1f))) { n++; } + src++; size--; } @@ -72,6 +73,7 @@ ngx_http_lua_escape_args(u_char *dst, u_char *src, size_t size) } else { *dst++ = *src++; } + size--; } diff --git a/src/ngx_http_lua_directive.c b/src/ngx_http_lua_directive.c index 8f7e7fbef3..6b7ccc7cbe 100644 --- a/src/ngx_http_lua_directive.c +++ b/src/ngx_http_lua_directive.c @@ -1378,6 +1378,7 @@ ngx_http_lua_conf_lua_block_parse(ngx_conf_t *cf, ngx_command_t *cmd) if (dst == NULL) { return NGX_CONF_ERROR; } + dst->len = len; dst->len--; /* skip the trailing '}' block terminator */ @@ -1385,6 +1386,7 @@ ngx_http_lua_conf_lua_block_parse(ngx_conf_t *cf, ngx_command_t *cmd) if (p == NULL) { return NGX_CONF_ERROR; } + dst->data = p; for (i = 0; i < cf->args->nelts; i++) { diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c index b638277bd7..75985a1130 100644 --- a/src/ngx_http_lua_headers.c +++ b/src/ngx_http_lua_headers.c @@ -500,6 +500,7 @@ ngx_http_lua_ngx_resp_get_headers(lua_State *L) } else { lua_pushliteral(L, "close"); } + lua_rawset(L, -3); if (r->chunked) { diff --git a/src/ngx_http_lua_headers_in.c b/src/ngx_http_lua_headers_in.c index 713818efc2..fff8bafb92 100644 --- a/src/ngx_http_lua_headers_in.c +++ b/src/ngx_http_lua_headers_in.c @@ -359,6 +359,7 @@ ngx_http_lua_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc) if (dot_pos == i - 1) { return NGX_DECLINED; } + dot_pos = i; break; @@ -754,6 +755,7 @@ ngx_http_lua_rm_header_helper(ngx_list_t *l, ngx_list_part_t *cur, if (part->next == NULL) { return NGX_ERROR; } + part = part->next; } @@ -798,6 +800,7 @@ ngx_http_lua_rm_header_helper(ngx_list_t *l, ngx_list_part_t *cur, if (part->next == NULL) { return NGX_ERROR; } + part = part->next; } diff --git a/src/ngx_http_lua_initworkerby.c b/src/ngx_http_lua_initworkerby.c index fa094a2e8b..94de796ade 100644 --- a/src/ngx_http_lua_initworkerby.c +++ b/src/ngx_http_lua_initworkerby.c @@ -148,6 +148,7 @@ ngx_http_lua_init_worker(ngx_cycle_t *cycle) if (part->next == NULL) { break; } + part = part->next; ofile = part->elts; i = 0; diff --git a/src/ngx_http_lua_log.c b/src/ngx_http_lua_log.c index 5411831a18..e56b9e598f 100644 --- a/src/ngx_http_lua_log.c +++ b/src/ngx_http_lua_log.c @@ -121,6 +121,7 @@ log_wrapper(ngx_log_t *log, const char *ident, ngx_uint_t level, if (*p == '/' || *p == '\\') { name.data = p + 1; } + p++; } diff --git a/src/ngx_http_lua_req_body.c b/src/ngx_http_lua_req_body.c index 5fe4564e89..d6e5640a0b 100644 --- a/src/ngx_http_lua_req_body.c +++ b/src/ngx_http_lua_req_body.c @@ -443,6 +443,7 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) if (b->start == NULL) { return luaL_error(L, "no memory"); } + b->end = b->start + body.len; b->pos = b->start; @@ -454,6 +455,7 @@ ngx_http_lua_ngx_req_set_body_data(lua_State *L) if (rb->bufs == NULL) { return luaL_error(L, "no memory"); } + rb->bufs->next = NULL; b = ngx_create_temp_buf(r->pool, body.len); @@ -899,6 +901,7 @@ ngx_http_lua_ngx_req_set_body_file(lua_State *L) if (rb->bufs == NULL) { return luaL_error(L, "no memory"); } + rb->bufs->next = NULL; b = ngx_calloc_buf(r->pool); diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index c2776d83a7..c99551a963 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -4890,6 +4890,7 @@ ngx_http_lua_req_socket(lua_State *L) if (rb == NULL) { return luaL_error(L, "no memory"); } + r->request_body = rb; } diff --git a/src/ngx_http_lua_string.c b/src/ngx_http_lua_string.c index a94e181745..4c755f67f3 100644 --- a/src/ngx_http_lua_string.c +++ b/src/ngx_http_lua_string.c @@ -133,6 +133,7 @@ ngx_http_lua_ngx_escape_sql_str(u_char *dst, u_char *src, size_t size) break; } } + src++; size--; } @@ -192,9 +193,11 @@ ngx_http_lua_ngx_escape_sql_str(u_char *dst, u_char *src, size_t size) *dst++ = *src; break; } + } else { *dst++ = *src; } + src++; size--; } /* while (size) */ diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c index 272906cb96..4b4f2c7c6f 100644 --- a/src/ngx_http_lua_util.c +++ b/src/ngx_http_lua_util.c @@ -2067,6 +2067,7 @@ ngx_http_lua_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) if (escape[*src >> 5] & (1 << (*src & 0x1f))) { n++; } + src++; size--; } @@ -2084,6 +2085,7 @@ ngx_http_lua_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) } else { *dst++ = *src++; } + size--; } @@ -3886,6 +3888,7 @@ ngx_http_lua_init_vm(lua_State **new_vm, lua_State *parent_vm, if (state == NULL) { return NGX_ERROR; } + state->vm = L; state->count = 1; From 1669904d53e03762604defdd97db7e7d357dd7a3 Mon Sep 17 00:00:00 2001 From: lijunlong Date: Mon, 20 Jul 2020 12:34:07 +0800 Subject: [PATCH 5/7] * optimize: avoided use of lua_tolstring in ngx_http_lua_calc_strlen_in_table, ngx_http_lua_copy_str_in_table, ngx_http_lua_socket_udp_send, log_wrapper and ngx_http_lua_ngx_echo. --- src/ngx_http_lua_common.h | 9 ++ src/ngx_http_lua_log.c | 7 ++ src/ngx_http_lua_output.c | 13 ++- src/ngx_http_lua_output.h | 44 +++++++++ src/ngx_http_lua_socket_tcp.c | 15 ++- src/ngx_http_lua_socket_udp.c | 22 +++-- t/009-log.t | 20 ++++ t/058-tcp-socket.t | 173 +++++++++++++++++++++++++++++++++- t/087-udp-socket.t | 104 +++++++++++++++++++- t/164-say.t | 55 +++++++++++ 10 files changed, 449 insertions(+), 13 deletions(-) create mode 100644 t/164-say.t diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index 781a2454a1..1232984911 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -44,6 +44,15 @@ typedef struct { #endif +/** + * max positive +1.7976931348623158e+308 + * min positive +2.2250738585072014e-308 + */ +#ifndef NGX_DOUBLE_LEN +#define NGX_DOUBLE_LEN 25 +#endif + + #if (NGX_PCRE) #include # if (PCRE_MAJOR > 8) || (PCRE_MAJOR == 8 && PCRE_MINOR >= 21) diff --git a/src/ngx_http_lua_log.c b/src/ngx_http_lua_log.c index e56b9e598f..43ab82095a 100644 --- a/src/ngx_http_lua_log.c +++ b/src/ngx_http_lua_log.c @@ -14,6 +14,7 @@ #include "ngx_http_lua_log.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_log_ringbuf.h" +#include "ngx_http_lua_output.h" static int ngx_http_lua_print(lua_State *L); @@ -143,6 +144,9 @@ log_wrapper(ngx_log_t *log, const char *ident, ngx_uint_t level, type = lua_type(L, i); switch (type) { case LUA_TNUMBER: + size += ngx_http_lua_get_num_len(L, i); + break; + case LUA_TSTRING: lua_tolstring(L, i, &len); size += len; @@ -211,6 +215,9 @@ log_wrapper(ngx_log_t *log, const char *ident, ngx_uint_t level, type = lua_type(L, i); switch (type) { case LUA_TNUMBER: + p = ngx_http_lua_write_num(L, i, p); + break; + case LUA_TSTRING: q = (u_char *) lua_tolstring(L, i, &len); p = ngx_copy(p, q, len); diff --git a/src/ngx_http_lua_output.c b/src/ngx_http_lua_output.c index 0fe8840f95..7c7403f667 100644 --- a/src/ngx_http_lua_output.c +++ b/src/ngx_http_lua_output.c @@ -93,6 +93,9 @@ ngx_http_lua_ngx_echo(lua_State *L, unsigned newline) switch (type) { case LUA_TNUMBER: + size += ngx_http_lua_get_num_len(L, i); + break; + case LUA_TSTRING: lua_tolstring(L, i, &len); @@ -173,6 +176,9 @@ ngx_http_lua_ngx_echo(lua_State *L, unsigned newline) type = lua_type(L, i); switch (type) { case LUA_TNUMBER: + b->last = ngx_http_lua_write_num(L, i, b->last); + break; + case LUA_TSTRING: p = lua_tolstring(L, i, &len); b->last = ngx_copy(b->last, (u_char *) p, len); @@ -301,8 +307,10 @@ ngx_http_lua_calc_strlen_in_table(lua_State *L, int index, int arg_i, switch (type) { case LUA_TNUMBER: - case LUA_TSTRING: + size += ngx_http_lua_get_num_len(L, -1); + break; + case LUA_TSTRING: lua_tolstring(L, -1, &len); size += len; break; @@ -396,6 +404,9 @@ ngx_http_lua_copy_str_in_table(lua_State *L, int index, u_char *dst) type = lua_type(L, -1); switch (type) { case LUA_TNUMBER: + dst = ngx_http_lua_write_num(L, -1, dst); + break; + case LUA_TSTRING: p = (u_char *) lua_tolstring(L, -1, &len); dst = ngx_copy(dst, p, len); diff --git a/src/ngx_http_lua_output.h b/src/ngx_http_lua_output.h index 109a4b44b8..ec1db6d050 100644 --- a/src/ngx_http_lua_output.h +++ b/src/ngx_http_lua_output.h @@ -23,6 +23,50 @@ ngx_int_t ngx_http_lua_flush_resume_helper(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); +/* Get the maximum possible length, not the actual length */ +static ngx_inline size_t +ngx_http_lua_get_num_len(lua_State *L, int idx) +{ + double num; + + num = (double) lua_tonumber(L, idx); + if (num == (double) (long) num) { + return NGX_INT64_LEN; + } + + return NGX_DOUBLE_LEN; +} + + +static ngx_inline u_char * +ngx_http_lua_write_num(lua_State *L, int idx, u_char *dst) +{ + double num; + int n; + + num = (double) lua_tonumber(L, idx); + if (num == (double) (long) num) { + dst = ngx_snprintf(dst, NGX_INT64_LEN, "%l", (long) num); + + } else { + /** + * The maximum number of significant digits is 14 in lua. + * Please refer to lj_strfmt.c for more details. + */ + n = snprintf((char *) dst, NGX_DOUBLE_LEN, "%.14g", num); + if (n < 0) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "snprintf(\"%f\") failed"); + + } else { + dst += n; + } + } + + return dst; +} + + #endif /* _NGX_HTTP_LUA_OUTPUT_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index c99551a963..964774041e 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -2741,11 +2741,15 @@ ngx_http_lua_socket_tcp_send(lua_State *L) type = lua_type(L, 2); switch (type) { case LUA_TNUMBER: + len = ngx_http_lua_get_num_len(L, 2); + break; + case LUA_TSTRING: lua_tolstring(L, 2, &len); break; case LUA_TTABLE: + /* The maximum possible length, not the actual length */ len = ngx_http_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */); break; @@ -2789,13 +2793,16 @@ ngx_http_lua_socket_tcp_send(lua_State *L) switch (type) { case LUA_TNUMBER: + b->last = ngx_http_lua_write_num(L, 2, b->last); + break; + case LUA_TSTRING: - p = (u_char *) lua_tolstring(L, -1, &len); + p = (u_char *) lua_tolstring(L, 2, &len); b->last = ngx_copy(b->last, (u_char *) p, len); break; case LUA_TTABLE: - b->last = ngx_http_lua_copy_str_in_table(L, -1, b->last); + b->last = ngx_http_lua_copy_str_in_table(L, 2, b->last); break; case LUA_TNIL: @@ -2827,6 +2834,10 @@ ngx_http_lua_socket_tcp_send(lua_State *L) u->request_bufs = cl; + lua_assert(b->last - b->start <= len); + + len = b->last - b->start; + u->request_len = len; /* mimic ngx_http_upstream_init_request here */ diff --git a/src/ngx_http_lua_socket_udp.c b/src/ngx_http_lua_socket_udp.c index f9d8fc0307..fd3e074270 100644 --- a/src/ngx_http_lua_socket_udp.c +++ b/src/ngx_http_lua_socket_udp.c @@ -727,6 +727,7 @@ ngx_http_lua_socket_udp_send(lua_State *L) ssize_t n; ngx_http_request_t *r; u_char *p; + u_char *str; size_t len; ngx_http_lua_socket_udp_upstream_t *u; int type; @@ -781,11 +782,15 @@ ngx_http_lua_socket_udp_send(lua_State *L) type = lua_type(L, 2); switch (type) { case LUA_TNUMBER: + len = ngx_http_lua_get_num_len(L, 2); + break; + case LUA_TSTRING: lua_tolstring(L, 2, &len); break; case LUA_TTABLE: + /* The maximum possible length, not the actual length */ len = ngx_http_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */); break; @@ -812,29 +817,29 @@ ngx_http_lua_socket_udp_send(lua_State *L) } query.data = lua_newuserdata(L, len); - query.len = len; + p = query.data; switch (type) { case LUA_TNUMBER: + p = ngx_http_lua_write_num(L, 2, p); + break; + case LUA_TSTRING: - p = (u_char *) lua_tolstring(L, 2, &len); - ngx_memcpy(query.data, (u_char *) p, len); + str = (u_char *) lua_tolstring(L, 2, &len); + p = ngx_cpymem(p, (u_char *) str, len); break; case LUA_TTABLE: - (void) ngx_http_lua_copy_str_in_table(L, 2, query.data); + p = ngx_http_lua_copy_str_in_table(L, 2, p); break; case LUA_TNIL: - p = query.data; *p++ = 'n'; *p++ = 'i'; *p++ = 'l'; break; case LUA_TBOOLEAN: - p = query.data; - if (lua_toboolean(L, 2)) { *p++ = 't'; *p++ = 'r'; @@ -855,6 +860,9 @@ ngx_http_lua_socket_udp_send(lua_State *L) return luaL_error(L, "impossible to reach here"); } + query.len = p - query.data; + ngx_http_lua_assert(query.len <= len); + u->ft_type = 0; /* mimic ngx_http_upstream_init_request here */ diff --git a/t/009-log.t b/t/009-log.t index cb3895b8b2..c4597698ef 100644 --- a/t/009-log.t +++ b/t/009-log.t @@ -548,3 +548,23 @@ ok [error] --- error_log eval "2: hello\0world, client: " + + + +=== TEST 27: test log-level STDERR +Note: maximum number of digits after the decimal-point character is 13 +--- config + location /log { + content_by_lua_block { + ngx.say("before log") + ngx.log(ngx.STDERR, 3.14159265357939723846) + ngx.say("after log") + } + } +--- request +GET /log +--- response_body +before log +after log +--- error_log eval +qr/\[\] \S+: \S+ \[lua\] content_by_lua\(nginx\.conf:\d+\):3: 3.1415926535794/ diff --git a/t/058-tcp-socket.t b/t/058-tcp-socket.t index 6ac67e0849..cac464d830 100644 --- a/t/058-tcp-socket.t +++ b/t/058-tcp-socket.t @@ -4,7 +4,7 @@ use Test::Nginx::Socket::Lua; repeat_each(2); -plan tests => repeat_each() * 222; +plan tests => repeat_each() * 228; our $HtmlDir = html_dir; @@ -1493,7 +1493,7 @@ GET /t -=== TEST 25: send tables of string fragments +=== TEST 25: send tables of string fragments (with integers too) --- config server_tokens off; location /t { @@ -4158,3 +4158,172 @@ orld [error] --- error_log lua tcp socket calling receiveany() method to read at most 7 bytes + + + +=== TEST 70: send tables of string fragments (with floating point number too) +--- config + server_tokens off; + location /t { + #set $port 5000; + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = ngx.var.port + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = {"GET", " ", "/foo", " HTTP/", 1, ".", 0, "\r\n", + "Host: localhost\r\n", "Connection: close\r\n", + "Foo: ", 3.1415926, "\r\n", + "\r\n"} + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + } + + location /foo { + content_by_lua_block { + ngx.say(ngx.req.get_headers()["Foo"]) + } + more_clear_headers Date; + } +--- request +GET /t +--- response_body +connected: 1 +request sent: 73 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 10 +received: Connection: close +received: +received: 3.1415926 +failed to receive a line: closed [] +close: 1 nil +--- no_error_log +[error] + + + +=== TEST 71: send numbers +the maximum number of significant digits is 14 in lua +--- config + server_tokens off; + location /t { + #set $port 5000; + set $port $TEST_NGINX_SERVER_PORT; + + content_by_lua_block { + local sock = ngx.socket.tcp() + local port = ngx.var.port + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = {"GET", " ", "/foo", " HTTP/", 1, ".", 0, "\r\n", + "Host: localhost\r\n", "Connection: close\r\n", + "Foo: "} + -- req = "OK" + + local total_bytes = 0; + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + total_bytes = total_bytes + bytes; + + bytes, err = sock:send(3.14159265357939723846) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + total_bytes = total_bytes + bytes; + + bytes, err = sock:send(31415926) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + total_bytes = total_bytes + bytes; + + bytes, err = sock:send("\r\n\r\n") + if not bytes then + ngx.say("failed to send request: ", err) + return + end + total_bytes = total_bytes + bytes; + + ngx.say("request sent: ", total_bytes) + + while true do + local line, err, part = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err, " [", part, "]") + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + } + } + + location /foo { + content_by_lua_block { + ngx.say(ngx.req.get_headers()["Foo"]) + } + more_clear_headers Date; + } +--- request +GET /t +--- response_body +connected: 1 +request sent: 87 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 24 +received: Connection: close +received: +received: 3.141592653579431415926 +failed to receive a line: closed [] +close: 1 nil +--- no_error_log +[error] diff --git a/t/087-udp-socket.t b/t/087-udp-socket.t index b3d5e824f0..fc467d769a 100644 --- a/t/087-udp-socket.t +++ b/t/087-udp-socket.t @@ -4,7 +4,7 @@ use Test::Nginx::Socket::Lua; repeat_each(2); -plan tests => repeat_each() * (3 * blocks() + 13); +plan tests => repeat_each() * (3 * blocks() + 15); our $HtmlDir = html_dir; @@ -1117,3 +1117,105 @@ qr/send: fd:\d+ 4 of 4 send: fd:\d+ 5 of 5 send: fd:\d+ 3 of 3/ --- log_level: debug + + + +=== TEST 21: send numbers +Note: maximum number of digits after the decimal-point character is 13 +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_MEMCACHED_PORT; + + content_by_lua_block { + local socket = ngx.socket + local udp = socket.udp() + local port = ngx.var.port + udp:settimeout(1000) -- 1 sec + + local ok, err = udp:setpeername("127.0.0.1", ngx.var.port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local function send(data) + local bytes, err = udp:send(data) + if not bytes then + ngx.say("failed to send: ", err) + return + end + ngx.say("sent ok") + end + + send(123456) + send(3.141926) + send(3.141592653579397238) + } + } +--- request +GET /t +--- response_body +sent ok +sent ok +sent ok +--- no_error_log +[error] +--- grep_error_log eval +qr/send: fd:\d+ \d+ of \d+/ +--- grep_error_log_out eval +qr/send: fd:\d+ 6 of 6 +send: fd:\d+ 8 of 8 +send: fd:\d+ 15 of 15/ +--- log_level: debug + + + +=== TEST 22: send tables of string framents (with numbers too) +the maximum number of significant digits is 14 in lua +--- config + server_tokens off; + location /t { + set $port $TEST_NGINX_MEMCACHED_PORT; + + content_by_lua_block { + local socket = ngx.socket + local udp = socket.udp() + local port = ngx.var.port + udp:settimeout(1000) -- 1 sec + + local ok, err = udp:setpeername("127.0.0.1", ngx.var.port) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local function send(data) + local bytes, err = udp:send(data) + if not bytes then + ngx.say("failed to send: ", err) + return + end + ngx.say("sent ok") + end + + send({"integer: ", 1234567890123}) + send({"float: ", 3.1419265}) + send({"float: ", 3.141592653579397238}) + } + } +--- request +GET /t +--- response_body +sent ok +sent ok +sent ok +--- no_error_log +[error] +--- grep_error_log eval +qr/send: fd:\d+ \d+ of \d+/ +--- grep_error_log_out eval +qr/send: fd:\d+ 22 of 22 +send: fd:\d+ 16 of 16 +send: fd:\d+ 22 of 22/ +--- log_level: debug diff --git a/t/164-say.t b/t/164-say.t new file mode 100644 index 0000000000..6114d2459e --- /dev/null +++ b/t/164-say.t @@ -0,0 +1,55 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +repeat_each(2); + +plan tests => blocks() * repeat_each() * 2; + +run_tests(); + +__DATA__ + +=== TEST 1: ngx.say (integer) +--- config + location /lua { + content_by_lua_block { + ngx.say(2) + } + } +--- request +GET /lua +--- response_body +2 + + + +=== TEST 2: ngx.say (floating point number) +the maximum number of significant digits is 14 in lua +--- config + location /lua { + content_by_lua_block { + ngx.say(3.1415926) + ngx.say(3.14159265357939723846) + } + } +--- request +GET /lua +--- response_body +3.1415926 +3.1415926535794 + + + +=== TEST 3: ngx.say (table with number) +--- config + location /lua { + content_by_lua_block { + local data = {123," ", 3.1415926} + ngx.say(data) + } + } +--- request +GET /lua +--- response_body +123 3.1415926 From 7c8dca93ee23095ba6fd41d6d0424146e9f4864c Mon Sep 17 00:00:00 2001 From: rainingmaster <312841925@qq.com> Date: Thu, 25 Jun 2020 17:30:36 +0800 Subject: [PATCH 6/7] feature(socket.tcp): allow second parameter is nil and default zero --- README.markdown | 4 ++- doc/HttpLuaModule.wiki | 4 ++- src/ngx_http_lua_socket_tcp.c | 4 +-- t/058-tcp-socket.t | 35 +++++++++++++++++- t/059-unix-socket.t | 68 +++++++++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 5 deletions(-) diff --git a/README.markdown b/README.markdown index 4046dfe15f..445f792368 100644 --- a/README.markdown +++ b/README.markdown @@ -7269,7 +7269,7 @@ See also [ngx.socket.udp](#ngxsocketudp). tcpsock:connect --------------- -**syntax:** *ok, err = tcpsock:connect(host, port, options_table?)* +**syntax:** *ok, err = tcpsock:connect(host, port?, options_table?)* **syntax:** *ok, err = tcpsock:connect("unix:/path/to/unix-domain.socket", options_table?)* @@ -7288,6 +7288,8 @@ Both IP addresses and domain names can be specified as the `host` argument. In c If the nameserver returns multiple IP addresses for the host name, this method will pick up one randomly. +`port` is optional and accept nil(for compatibility with host is a unix domain), and it default value is zero. + In case of error, the method returns `nil` followed by a string describing the error. In case of success, the method returns `1`. Here is an example for connecting to a TCP server: diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 37d8a1367b..58d0ee1bb5 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -6157,7 +6157,7 @@ See also [[#ngx.socket.udp|ngx.socket.udp]]. == tcpsock:connect == -'''syntax:''' ''ok, err = tcpsock:connect(host, port, options_table?)'' +'''syntax:''' ''ok, err = tcpsock:connect(host, port?, options_table?)'' '''syntax:''' ''ok, err = tcpsock:connect("unix:/path/to/unix-domain.socket", options_table?)'' @@ -6175,6 +6175,8 @@ Both IP addresses and domain names can be specified as the host arg If the nameserver returns multiple IP addresses for the host name, this method will pick up one randomly. +port is optional and accept nil(for compatibility with host is a unix domain), and it default value is zero. + In case of error, the method returns nil followed by a string describing the error. In case of success, the method returns 1. Here is an example for connecting to a TCP server: diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 964774041e..f289dc19b4 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -970,7 +970,7 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) n--; } - if (n == 3) { + if (n == 3 && !lua_isnil(L, 3)) { port = luaL_checkinteger(L, 3); if (port < 0 || port > 65535) { @@ -987,7 +987,7 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) dd("socket key: %s", lua_tostring(L, -1)); - } else { /* n == 2 */ + } else { /* n == 2 || lua_isnil(L, 3) */ port = 0; } diff --git a/t/058-tcp-socket.t b/t/058-tcp-socket.t index cac464d830..a7fc07ad72 100644 --- a/t/058-tcp-socket.t +++ b/t/058-tcp-socket.t @@ -4,7 +4,7 @@ use Test::Nginx::Socket::Lua; repeat_each(2); -plan tests => repeat_each() * 228; +plan tests => repeat_each() * 231; our $HtmlDir = html_dir; @@ -4327,3 +4327,36 @@ failed to receive a line: closed [] close: 1 nil --- no_error_log [error] + + + +=== TEST 72: port is optional and accept nil, default is 0 +--- config + server_tokens off; + location = /t { + set $port $TEST_NGINX_SERVER_PORT; + content_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(500) + local ok, err = sock:connect("127.0.0.1") + if ok then + ngx.say("connect success") + return + end + + local ok, err = sock:connect("127.0.0.1", nil) + if ok then + ngx.say("connect success") + return + end + + ngx.say("ok") + } + } + +--- request +GET /t +--- response_body +ok +--- error_log +connect to 127.0.0.1:0 diff --git a/t/059-unix-socket.t b/t/059-unix-socket.t index b06ba6e2ee..c9d8d9b7e9 100644 --- a/t/059-unix-socket.t +++ b/t/059-unix-socket.t @@ -201,3 +201,71 @@ received: received: foo failed to receive a line: closed close: 1 nil + + + +=== TEST 5: second parameter is nil +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + default_type 'text/plain'; + + server_tokens off; + location /foo { + content_by_lua 'ngx.say("foo")'; + more_clear_headers Date; + } + } +--- config + location /test { + content_by_lua ' + local sock = ngx.socket.tcp() + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock", nil) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local req = "GET /foo HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send request: ", err) + return + end + + ngx.say("request sent: ", bytes) + + while true do + print("calling receive") + local line, err = sock:receive() + if line then + ngx.say("received: ", line) + + else + ngx.say("failed to receive a line: ", err) + break + end + end + + ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + '; + } +--- request + GET /test +--- response_body +connected: 1 +request sent: 57 +received: HTTP/1.1 200 OK +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +failed to receive a line: closed +close: 1 nil From 047e4a785e9213bc8fd43d547cca88977121f321 Mon Sep 17 00:00:00 2001 From: rainingmaster <312841925@qq.com> Date: Wed, 15 Jul 2020 10:19:44 +0800 Subject: [PATCH 7/7] style: add a space before ( --- README.markdown | 2 +- doc/HttpLuaModule.wiki | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 445f792368..4bac43760d 100644 --- a/README.markdown +++ b/README.markdown @@ -7288,7 +7288,7 @@ Both IP addresses and domain names can be specified as the `host` argument. In c If the nameserver returns multiple IP addresses for the host name, this method will pick up one randomly. -`port` is optional and accept nil(for compatibility with host is a unix domain), and it default value is zero. +`port` is optional and accept nil (for compatibility with host is a unix domain), and it default value is zero. In case of error, the method returns `nil` followed by a string describing the error. In case of success, the method returns `1`. diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 58d0ee1bb5..ae8c41af9a 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -6175,7 +6175,7 @@ Both IP addresses and domain names can be specified as the host arg If the nameserver returns multiple IP addresses for the host name, this method will pick up one randomly. -port is optional and accept nil(for compatibility with host is a unix domain), and it default value is zero. +port is optional and accept nil (for compatibility with host is a unix domain), and it default value is zero. In case of error, the method returns nil followed by a string describing the error. In case of success, the method returns 1.