Brotli: fix handling of upstream read timeout. Output filters receive only special buffer with "flush" flag set in case when upstream sent headers, but timed out before sending request body. Previously, this case wasn't handled correctly, because of the wrong assumption that Brotli compressor always produces output when called with "flush" parameter, which isn't the case if there is no input to compress. Reported by Tomas Kvasnicka on GitHub (issue #20). Change-Id: I96dacc625c9c2509276800d7ac80a2d622a95463 Signed-off-by: Piotr Sikora <piotrsikora@google.com> Reviewed-on: https://nginx-review.googlesource.com/1110 Reviewed-by: Martin Maly <mmaly@google.com>
diff --git a/src/ngx_http_brotli_filter_module.cc b/src/ngx_http_brotli_filter_module.cc index 43d0a2b..02280a1 100644 --- a/src/ngx_http_brotli_filter_module.cc +++ b/src/ngx_http_brotli_filter_module.cc
@@ -69,6 +69,7 @@ ngx_http_brotli_ctx_t *ctx); static ngx_int_t ngx_http_brotli_filter_get_buf(ngx_http_request_t *r, ngx_http_brotli_ctx_t *ctx); +static void ngx_http_brotli_cleanup(void *data); static ngx_int_t ngx_http_brotli_ok(ngx_http_request_t *r); static ngx_int_t ngx_http_brotli_accept_encoding(ngx_str_t *ae); @@ -248,6 +249,7 @@ int rc; ngx_uint_t flush; ngx_chain_t *cl; + ngx_pool_cleanup_t *cln; ngx_http_brotli_ctx_t *ctx; ctx = reinterpret_cast<ngx_http_brotli_ctx_t *>( @@ -272,6 +274,14 @@ "brotli compressor: lvl:%d win:%d blk:%uz", ctx->params.quality, (1 << ctx->params.lgwin), ctx->brotli_ring); + + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->handler = ngx_http_brotli_cleanup; + cln->data = ctx; } if (in) { @@ -486,6 +496,8 @@ "brotli process: size:%uz l:%d f:%d", ctx->brotli_in, ctx->last, ctx->flush); + out = NULL; + if (!ctx->compressor->WriteBrotliData(ctx->last, ctx->flush, &size, &out)) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "brotli failed: size:%uz l:%d f:%d", @@ -498,7 +510,7 @@ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "brotli data: %p, size:%uz", out, size); - if (size == 0) { + if (size == 0 && !ctx->flush && !ctx->last) { return NGX_AGAIN; } @@ -517,13 +529,7 @@ size_t size; ngx_chain_t *cl; - if (ctx->brotli_out == NULL) { - return NGX_AGAIN; - } - -#if (NGX_SUPPRESS_WARN) cl = NULL; -#endif while (ctx->brotli_out < ctx->brotli_last) { @@ -567,11 +573,17 @@ if (ctx->last) { ctx->done = 1; - cl->buf->last_buf = 1; + + if (cl) { + cl->buf->last_buf = 1; + } } else if (ctx->flush) { ctx->flush = 0; - cl->buf->flush = 1; + + if (cl) { + cl->buf->flush = 1; + } } r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; @@ -617,6 +629,20 @@ } +static void +ngx_http_brotli_cleanup(void *data) +{ + ngx_http_brotli_ctx_t *ctx; + + ctx = reinterpret_cast<ngx_http_brotli_ctx_t *>(data); + + if (ctx->compressor) { + delete ctx->compressor; + ctx->compressor = NULL; + } +} + + static ngx_int_t ngx_http_brotli_ok(ngx_http_request_t *r) {