Skip to content

Commit f424d25

Browse files
bugfix: buffer bloat and CPU 100% when download large file was filtered by body_filter_by_lua.
1 parent 263bd0c commit f424d25

File tree

2 files changed

+70
-6
lines changed

2 files changed

+70
-6
lines changed

src/ngx_http_lua_bodyfilterby.c

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,12 @@ ngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
235235
uint16_t old_context;
236236
ngx_http_cleanup_t *cln;
237237
ngx_chain_t *out;
238+
ngx_chain_t *cl, *ln, **ll;
238239
ngx_http_lua_main_conf_t *lmcf;
239240

240241
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
241242
"lua body filter for user lua code, uri \"%V\"", &r->uri);
242243

243-
if (in == NULL) {
244-
return ngx_http_next_body_filter(r, in);
245-
}
246-
247244
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
248245

249246
if (llcf->body_filter_handler == NULL) {
@@ -272,6 +269,50 @@ ngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
272269
return NGX_OK;
273270
}
274271

272+
if (in != NULL) {
273+
ll = &ctx->in_bufs;
274+
while (*ll != NULL) {
275+
ll = &(*ll)->next;
276+
}
277+
278+
for (ln = in; ln != NULL; ln = ln->next) {
279+
cl = ngx_alloc_chain_link(r->pool);
280+
if (cl == NULL) {
281+
return NGX_ERROR;
282+
}
283+
284+
cl->buf = ln->buf;
285+
cl->next = NULL;
286+
*ll = cl;
287+
ll = &cl->next;
288+
}
289+
}
290+
291+
if (ctx->last_body_filter_rc == NGX_AGAIN && ctx->busy_bufs != NULL) {
292+
/* Socket write buffer was full on last write.
293+
* Try to write the remain data, if still can not write
294+
* do not execute body_filter_by_lua otherwise the `in` chain will be
295+
* replaced by new content from lua and buf of `in` mark as consumed.
296+
* And then ngx_output_chain will call the filter chain again which
297+
* make all the data cached in the memory and long ngx_chain_t link
298+
* cause CPU 100%.
299+
*/
300+
rc = ngx_http_next_body_filter(r, NULL);
301+
ctx->last_body_filter_rc = rc;
302+
303+
if (rc == NGX_ERROR) {
304+
return rc;
305+
}
306+
307+
out = NULL;
308+
ngx_chain_update_chains(r->pool,
309+
&ctx->free_bufs, &ctx->busy_bufs, &out,
310+
(ngx_buf_tag_t) &ngx_http_lua_module);
311+
if (rc != NGX_OK) {
312+
return rc;
313+
}
314+
}
315+
275316
if (ctx->cleanup == NULL) {
276317
cln = ngx_http_cleanup_add(r, 0);
277318
if (cln == NULL) {
@@ -286,6 +327,9 @@ ngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
286327
old_context = ctx->context;
287328
ctx->context = NGX_HTTP_LUA_CONTEXT_BODY_FILTER;
288329

330+
in = ctx->in_bufs;
331+
ctx->in_bufs = NULL;
332+
289333
dd("calling body filter handler");
290334
rc = llcf->body_filter_handler(r, in);
291335

@@ -300,8 +344,21 @@ ngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
300344
lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);
301345
out = lmcf->body_filter_chain;
302346

303-
if (in == out) {
304-
return ngx_http_next_body_filter(r, in);
347+
if (in != out) {
348+
/* content of body was replaced in ngx_http_lua_body_filter_param_set
349+
* and the buffer was marked as consumed.
350+
*/
351+
for (cl = in; cl != NULL; cl = ln) {
352+
ln = cl->next;
353+
if (ngx_buf_size(cl->buf) == 0) {
354+
ngx_free_chain(r->pool, cl);
355+
356+
} else {
357+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
358+
"chain link was not marked as consumed");
359+
return NGX_ERROR;
360+
}
361+
}
305362
}
306363

307364
if (out == NULL) {
@@ -316,6 +373,8 @@ ngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
316373
return NGX_ERROR;
317374
}
318375

376+
ctx->last_body_filter_rc = rc;
377+
319378
ngx_chain_update_chains(r->pool,
320379
&ctx->free_bufs, &ctx->busy_bufs, &out,
321380
(ngx_buf_tag_t) &ngx_http_lua_module);
@@ -622,3 +681,5 @@ ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r,
622681
}
623682

624683
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
684+
685+

src/ngx_http_lua_common.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@ typedef struct ngx_http_lua_ctx_s {
545545
ngx_chain_t *free_bufs;
546546
ngx_chain_t *busy_bufs;
547547
ngx_chain_t *free_recv_bufs;
548+
ngx_chain_t *in_bufs; /* for the body filter */
548549

549550
ngx_http_cleanup_pt *cleanup;
550551

@@ -560,6 +561,8 @@ typedef struct ngx_http_lua_ctx_s {
560561

561562
ngx_int_t exit_code;
562563

564+
ngx_int_t last_body_filter_rc;
565+
563566
void *downstream; /* can be either
564567
ngx_http_lua_socket_tcp_upstream_t
565568
or ngx_http_lua_co_ctx_t */

0 commit comments

Comments
 (0)