Skip to content

Commit 04332d9

Browse files
committed
optimize: now we recycle the special flush buf and chain link for ngx.flush() to prevent request-scoped memory leaks when emitting long data streams to the downstream.
1 parent 58cd7f0 commit 04332d9

File tree

5 files changed

+72
-18
lines changed

5 files changed

+72
-18
lines changed

src/ngx_http_lua_common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ typedef struct {
153153
ngx_chain_t *free_bufs;
154154
ngx_chain_t *busy_bufs;
155155
ngx_chain_t *free_recv_bufs;
156+
ngx_chain_t *flush_buf;
156157

157158
ngx_http_cleanup_pt *cleanup;
158159

@@ -215,6 +216,7 @@ typedef struct {
215216
unsigned socket_ready:1;
216217

217218
unsigned aborted:1;
219+
unsigned buffering:1;
218220

219221
} ngx_http_lua_ctx_t;
220222

src/ngx_http_lua_output.c

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -464,20 +464,36 @@ ngx_http_lua_ngx_flush(lua_State *L)
464464
return luaL_error(L, "already seen eof");
465465
}
466466

467-
buf = ngx_calloc_buf(r->pool);
468-
if (buf == NULL) {
469-
return luaL_error(L, "memory allocation error");
467+
if (ctx->buffering) {
468+
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
469+
"lua http 1.0 buffering makes ngx.flush() a no-op");
470+
471+
return 0;
470472
}
471473

472-
buf->flush = 1;
474+
if (ctx->flush_buf) {
475+
cl = ctx->flush_buf;
473476

474-
cl = ngx_alloc_chain_link(r->pool);
475-
if (cl == NULL) {
476-
return luaL_error(L, "out of memory");
477-
}
477+
} else {
478+
dd("allocating new flush buf");
479+
buf = ngx_calloc_buf(r->pool);
480+
if (buf == NULL) {
481+
return luaL_error(L, "memory allocation error");
482+
}
478483

479-
cl->next = NULL;
480-
cl->buf = buf;
484+
buf->flush = 1;
485+
486+
dd("allocating new flush chain");
487+
cl = ngx_alloc_chain_link(r->pool);
488+
if (cl == NULL) {
489+
return luaL_error(L, "out of memory");
490+
}
491+
492+
cl->next = NULL;
493+
cl->buf = buf;
494+
495+
ctx->flush_buf = cl;
496+
}
481497

482498
rc = ngx_http_lua_send_chain_link(r, ctx, cl);
483499

src/ngx_http_lua_util.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,19 +373,24 @@ ngx_http_lua_send_chain_link(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,
373373
return rc;
374374
}
375375

376+
if (!ctx->buffering && !ctx->headers_sent
377+
&& r->http_version < NGX_HTTP_VERSION_11)
378+
{
379+
ctx->buffering = 1;
380+
}
381+
376382
if (r->header_only) {
377383
ctx->eof = 1;
378384

379-
if (!ctx->headers_sent && r->http_version < NGX_HTTP_VERSION_11)
380-
{
385+
if (ctx->buffering) {
381386
return ngx_http_lua_send_http10_headers(r, ctx);
382387
}
383388

384389
return rc;
385390
}
386391

387392
if (in == NULL) {
388-
if (!ctx->headers_sent && r->http_version < NGX_HTTP_VERSION_11) {
393+
if (ctx->buffering) {
389394
rc = ngx_http_lua_send_http10_headers(r, ctx);
390395
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
391396
return rc;
@@ -427,7 +432,7 @@ ngx_http_lua_send_chain_link(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,
427432

428433
/* in != NULL */
429434

430-
if (r->http_version < NGX_HTTP_VERSION_11 && !ctx->headers_sent) {
435+
if (ctx->buffering) {
431436
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
432437
"lua buffering output bufs for the HTTP 1.0 request");
433438

t/056-flush.t

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use Test::Nginx::Socket;
1414

1515
repeat_each(2);
1616

17-
plan tests => repeat_each() * (blocks() * 2 + 1);
17+
plan tests => repeat_each() * (blocks() * 2 + 5);
1818

1919
#no_diff();
2020
#no_long_string();
@@ -95,25 +95,55 @@ hiya
9595

9696

9797

98-
=== TEST 5: http 1.0
98+
=== TEST 5: http 1.0 (sync)
9999
--- config
100100
location /test {
101101
content_by_lua '
102102
ngx.say("hello, world")
103103
ngx.flush(true)
104104
ngx.say("hiya")
105+
ngx.flush(true)
106+
ngx.say("blah")
105107
';
106108
}
107109
--- request
108110
GET /test HTTP/1.0
109111
--- response_body
110112
hello, world
111113
hiya
114+
blah
115+
--- timeout: 5
116+
--- error_log
117+
lua buffering output bufs for the HTTP 1.0 request
118+
lua http 1.0 buffering makes ngx.flush() a no-op
119+
120+
121+
122+
=== TEST 6: http 1.0 (async)
123+
--- config
124+
location /test {
125+
content_by_lua '
126+
ngx.say("hello, world")
127+
ngx.flush(false)
128+
ngx.say("hiya")
129+
ngx.flush(false)
130+
ngx.say("blah")
131+
';
132+
}
133+
--- request
134+
GET /test HTTP/1.0
135+
--- response_body
136+
hello, world
137+
hiya
138+
blah
139+
--- error_log
140+
lua buffering output bufs for the HTTP 1.0 request
141+
lua http 1.0 buffering makes ngx.flush() a no-op
112142
--- timeout: 5
113143

114144

115145

116-
=== TEST 6: flush wait - big data
146+
=== TEST 7: flush wait - big data
117147
--- config
118148
location /test {
119149
content_by_lua '
@@ -131,7 +161,7 @@ hiya
131161

132162

133163

134-
=== TEST 7: flush wait - content
164+
=== TEST 8: flush wait - content
135165
--- config
136166
location /test {
137167
content_by_lua '

util/build2.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ force=$2
1919

2020
time ngx-build $force $version \
2121
--with-cc-opt=$'-I/opt/pcre821jit/include' \
22+
--with-http_realip_module \
2223
--with-http_ssl_module \
2324
--add-module=$root/../ndk-nginx-module \
2425
--add-module=$root/../set-misc-nginx-module \

0 commit comments

Comments
 (0)