git: e7df2a613c40 - main - www/{nginx,nginx-devel,freenginx}: 3rd-party modules management
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 04 Oct 2026 14:41:04 UTC
The branch main has been updated by joneum:
URL: https://cgit.FreeBSD.org/ports/commit/?id=e7df2a613c408a27dbceddb620a4f10dcc62113b
commit e7df2a613c408a27dbceddb620a4f10dcc62113b
Author: Igor Valkov <viaprog@gmail.com>
AuthorDate: 2026-10-04 14:36:48 +0000
Commit: Jochen Neumeister <joneum@FreeBSD.org>
CommitDate: 2026-10-04 14:40:40 +0000
www/{nginx,nginx-devel,freenginx}: 3rd-party modules management
Fix three defects in the body filter of the zstd module. A worker spins
at 100% CPU forever when an empty flush buffer arrives and the compressor
has nothing left to flush. A response is truncated at 131072 bytes when
a single input buffer larger than that carries last_buf. An output
buffer that is already full when ZSTD_endStream() is due gets dropped.
Upstream has not taken a code change since April 2024.
PR: 299100
Sponsored by: Netzkommune GmbH
---
www/freenginx/Makefile | 2 +-
www/freenginx/Makefile.extmod | 3 ++
www/freenginx/files/extra-patch-zstd-empty-flush | 41 +++++++++++++++
www/freenginx/files/extra-patch-zstd-full-outbuf | 18 +++++++
.../files/extra-patch-zstd-last-truncation | 60 ++++++++++++++++++++++
www/nginx-devel/Makefile | 2 +-
www/nginx-devel/Makefile.extmod | 3 ++
www/nginx-devel/files/extra-patch-zstd-empty-flush | 41 +++++++++++++++
www/nginx-devel/files/extra-patch-zstd-full-outbuf | 18 +++++++
.../files/extra-patch-zstd-last-truncation | 60 ++++++++++++++++++++++
www/nginx/Makefile | 2 +-
www/nginx/Makefile.extmod | 3 ++
www/nginx/files/extra-patch-zstd-empty-flush | 41 +++++++++++++++
www/nginx/files/extra-patch-zstd-full-outbuf | 18 +++++++
www/nginx/files/extra-patch-zstd-last-truncation | 60 ++++++++++++++++++++++
15 files changed, 369 insertions(+), 3 deletions(-)
diff --git a/www/freenginx/Makefile b/www/freenginx/Makefile
index 9d2753616a7d..b106b905fea6 100644
--- a/www/freenginx/Makefile
+++ b/www/freenginx/Makefile
@@ -1,7 +1,7 @@
PORTNAME= freenginx
PORTVERSION= ${NGINX_VERSION}
.include "version.mk"
-PORTREVISION?= 17
+PORTREVISION?= 18
CATEGORIES= www
MASTER_SITES= https://freenginx.org/download/ \
LOCAL/joneum
diff --git a/www/freenginx/Makefile.extmod b/www/freenginx/Makefile.extmod
index ffc95be60609..523624f3b3b7 100644
--- a/www/freenginx/Makefile.extmod
+++ b/www/freenginx/Makefile.extmod
@@ -356,3 +356,6 @@ ZSTD_CONFIGURE_ENV= ZSTD_INC=${LOCALBASE}/include \
ZSTD_LIB=${LOCALBASE}/lib
ZSTD_GH_TUPLE= tokers:zstd-nginx-module:f4ba115:zstd
ZSTD_VARS= DSO_EXTMODS+=zstd
+ZSTD_EXTRA_PATCHES= ${PATCHDIR}/extra-patch-zstd-empty-flush \
+ ${PATCHDIR}/extra-patch-zstd-last-truncation \
+ ${PATCHDIR}/extra-patch-zstd-full-outbuf
diff --git a/www/freenginx/files/extra-patch-zstd-empty-flush b/www/freenginx/files/extra-patch-zstd-empty-flush
new file mode 100644
index 000000000000..58d96477d8a4
--- /dev/null
+++ b/www/freenginx/files/extra-patch-zstd-empty-flush
@@ -0,0 +1,41 @@
+--- ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c.orig
++++ ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c
+@@ -460,6 +460,38 @@
+ }
+
+ if (ngx_buf_size(ctx->out_buf) == 0) {
++
++ if (rc == 0 && ctx->flush) {
++
++ /*
++ * nothing to flush: pass an empty flush buf downstream,
++ * as the gzip filter does; otherwise ctx->flush is never
++ * reset and the body filter loops forever
++ */
++
++ b = ngx_calloc_buf(r->pool);
++ if (b == NULL) {
++ return NGX_ERROR;
++ }
++
++ b->flush = 1;
++
++ cl = ngx_alloc_chain_link(r->pool);
++ if (cl == NULL) {
++ return NGX_ERROR;
++ }
++
++ cl->buf = b;
++ cl->next = NULL;
++
++ *ctx->last_out = cl;
++ ctx->last_out = &cl->next;
++
++ ctx->flush = 0;
++
++ r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
++ }
++
+ return NGX_AGAIN;
+ }
+
diff --git a/www/freenginx/files/extra-patch-zstd-full-outbuf b/www/freenginx/files/extra-patch-zstd-full-outbuf
new file mode 100644
index 000000000000..9e6a83e87cfb
--- /dev/null
+++ b/www/freenginx/files/extra-patch-zstd-full-outbuf
@@ -0,0 +1,18 @@
+--- ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c.orig
++++ ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c
+@@ -462,7 +462,14 @@
+
+ /* pending to call the ZSTD_endStream() */
+
+- return NGX_AGAIN;
++ if (ctx->buffer_out.pos < ctx->buffer_out.size) {
++ return NGX_AGAIN;
++ }
++
++ /*
++ * the output buffer is full: pass it downstream now, otherwise
++ * ngx_http_zstd_filter_get_buf() replaces it and the data is lost
++ */
+
+ } else {
+ ctx->action = NGX_HTTP_ZSTD_FILTER_COMPRESS; /* restore */
diff --git a/www/freenginx/files/extra-patch-zstd-last-truncation b/www/freenginx/files/extra-patch-zstd-last-truncation
new file mode 100644
index 000000000000..bb3f8fc34004
--- /dev/null
+++ b/www/freenginx/files/extra-patch-zstd-last-truncation
@@ -0,0 +1,60 @@
+--- ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c.orig
++++ ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c
+@@ -390,6 +390,7 @@
+ {
+ size_t rc, pos_in, pos_out;
+ char *hint;
++ ngx_uint_t end;
+ ngx_chain_t *cl;
+ ngx_buf_t *b;
+
+@@ -440,6 +441,10 @@
+ ctx->out_buf->last += ctx->buffer_out.pos - pos_out;
+ ctx->redo = 0;
+
++ /* the frame is complete only when ZSTD_endStream() has flushed it all */
++
++ end = (rc == 0 && ctx->action == NGX_HTTP_ZSTD_FILTER_END);
++
+ if (rc > 0) {
+ if (ctx->action == NGX_HTTP_ZSTD_FILTER_COMPRESS) {
+ ctx->action = NGX_HTTP_ZSTD_FILTER_FLUSH;
+@@ -447,7 +452,11 @@
+
+ ctx->redo = 1;
+
+- } else if (ctx->last && ctx->action != NGX_HTTP_ZSTD_FILTER_END) {
++ } else if (ctx->last && ctx->action != NGX_HTTP_ZSTD_FILTER_END
++ && ctx->buffer_in.pos == ctx->buffer_in.size)
++ {
++ /* end the frame only after the whole input has been consumed */
++
+ ctx->redo = 1;
+ ctx->action = NGX_HTTP_ZSTD_FILTER_END;
+
+@@ -502,13 +511,13 @@
+
+ b = ctx->out_buf;
+
+- if (rc == 0 && (ctx->flush || ctx->last)) {
++ if (rc == 0 && (ctx->flush || end)) {
+ r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
+
+ b->flush = ctx->flush;
+- b->last_buf = ctx->last;
++ b->last_buf = end;
+
+- ctx->done = ctx->last;
++ ctx->done = end;
+ ctx->flush = 0;
+ }
+
+@@ -522,7 +531,7 @@
+
+ ngx_memzero(&ctx->buffer_out, sizeof(ZSTD_outBuffer));
+
+- return ctx->last && rc == 0 ? NGX_OK : NGX_AGAIN;
++ return end ? NGX_OK : NGX_AGAIN;
+ }
+
+
diff --git a/www/nginx-devel/Makefile b/www/nginx-devel/Makefile
index 754aab62c1e4..1004fdadea24 100644
--- a/www/nginx-devel/Makefile
+++ b/www/nginx-devel/Makefile
@@ -1,7 +1,7 @@
PORTNAME?= nginx
PORTVERSION= ${NGINX_VERSION}
.include "version.mk"
-PORTREVISION= 1
+PORTREVISION= 2
CATEGORIES= www
MASTER_SITES= https://nginx.org/download/
PKGNAMESUFFIX?= -devel
diff --git a/www/nginx-devel/Makefile.extmod b/www/nginx-devel/Makefile.extmod
index 5c96c2682f76..8a402b1548b6 100644
--- a/www/nginx-devel/Makefile.extmod
+++ b/www/nginx-devel/Makefile.extmod
@@ -353,3 +353,6 @@ ZSTD_CONFIGURE_ENV= ZSTD_INC=${LOCALBASE}/include \
ZSTD_LIB=${LOCALBASE}/lib
ZSTD_GH_TUPLE= tokers:zstd-nginx-module:f4ba115:zstd
ZSTD_VARS= DSO_EXTMODS+=zstd
+ZSTD_EXTRA_PATCHES= ${PATCHDIR}/extra-patch-zstd-empty-flush \
+ ${PATCHDIR}/extra-patch-zstd-last-truncation \
+ ${PATCHDIR}/extra-patch-zstd-full-outbuf
diff --git a/www/nginx-devel/files/extra-patch-zstd-empty-flush b/www/nginx-devel/files/extra-patch-zstd-empty-flush
new file mode 100644
index 000000000000..58d96477d8a4
--- /dev/null
+++ b/www/nginx-devel/files/extra-patch-zstd-empty-flush
@@ -0,0 +1,41 @@
+--- ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c.orig
++++ ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c
+@@ -460,6 +460,38 @@
+ }
+
+ if (ngx_buf_size(ctx->out_buf) == 0) {
++
++ if (rc == 0 && ctx->flush) {
++
++ /*
++ * nothing to flush: pass an empty flush buf downstream,
++ * as the gzip filter does; otherwise ctx->flush is never
++ * reset and the body filter loops forever
++ */
++
++ b = ngx_calloc_buf(r->pool);
++ if (b == NULL) {
++ return NGX_ERROR;
++ }
++
++ b->flush = 1;
++
++ cl = ngx_alloc_chain_link(r->pool);
++ if (cl == NULL) {
++ return NGX_ERROR;
++ }
++
++ cl->buf = b;
++ cl->next = NULL;
++
++ *ctx->last_out = cl;
++ ctx->last_out = &cl->next;
++
++ ctx->flush = 0;
++
++ r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
++ }
++
+ return NGX_AGAIN;
+ }
+
diff --git a/www/nginx-devel/files/extra-patch-zstd-full-outbuf b/www/nginx-devel/files/extra-patch-zstd-full-outbuf
new file mode 100644
index 000000000000..9e6a83e87cfb
--- /dev/null
+++ b/www/nginx-devel/files/extra-patch-zstd-full-outbuf
@@ -0,0 +1,18 @@
+--- ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c.orig
++++ ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c
+@@ -462,7 +462,14 @@
+
+ /* pending to call the ZSTD_endStream() */
+
+- return NGX_AGAIN;
++ if (ctx->buffer_out.pos < ctx->buffer_out.size) {
++ return NGX_AGAIN;
++ }
++
++ /*
++ * the output buffer is full: pass it downstream now, otherwise
++ * ngx_http_zstd_filter_get_buf() replaces it and the data is lost
++ */
+
+ } else {
+ ctx->action = NGX_HTTP_ZSTD_FILTER_COMPRESS; /* restore */
diff --git a/www/nginx-devel/files/extra-patch-zstd-last-truncation b/www/nginx-devel/files/extra-patch-zstd-last-truncation
new file mode 100644
index 000000000000..bb3f8fc34004
--- /dev/null
+++ b/www/nginx-devel/files/extra-patch-zstd-last-truncation
@@ -0,0 +1,60 @@
+--- ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c.orig
++++ ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c
+@@ -390,6 +390,7 @@
+ {
+ size_t rc, pos_in, pos_out;
+ char *hint;
++ ngx_uint_t end;
+ ngx_chain_t *cl;
+ ngx_buf_t *b;
+
+@@ -440,6 +441,10 @@
+ ctx->out_buf->last += ctx->buffer_out.pos - pos_out;
+ ctx->redo = 0;
+
++ /* the frame is complete only when ZSTD_endStream() has flushed it all */
++
++ end = (rc == 0 && ctx->action == NGX_HTTP_ZSTD_FILTER_END);
++
+ if (rc > 0) {
+ if (ctx->action == NGX_HTTP_ZSTD_FILTER_COMPRESS) {
+ ctx->action = NGX_HTTP_ZSTD_FILTER_FLUSH;
+@@ -447,7 +452,11 @@
+
+ ctx->redo = 1;
+
+- } else if (ctx->last && ctx->action != NGX_HTTP_ZSTD_FILTER_END) {
++ } else if (ctx->last && ctx->action != NGX_HTTP_ZSTD_FILTER_END
++ && ctx->buffer_in.pos == ctx->buffer_in.size)
++ {
++ /* end the frame only after the whole input has been consumed */
++
+ ctx->redo = 1;
+ ctx->action = NGX_HTTP_ZSTD_FILTER_END;
+
+@@ -502,13 +511,13 @@
+
+ b = ctx->out_buf;
+
+- if (rc == 0 && (ctx->flush || ctx->last)) {
++ if (rc == 0 && (ctx->flush || end)) {
+ r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
+
+ b->flush = ctx->flush;
+- b->last_buf = ctx->last;
++ b->last_buf = end;
+
+- ctx->done = ctx->last;
++ ctx->done = end;
+ ctx->flush = 0;
+ }
+
+@@ -522,7 +531,7 @@
+
+ ngx_memzero(&ctx->buffer_out, sizeof(ZSTD_outBuffer));
+
+- return ctx->last && rc == 0 ? NGX_OK : NGX_AGAIN;
++ return end ? NGX_OK : NGX_AGAIN;
+ }
+
+
diff --git a/www/nginx/Makefile b/www/nginx/Makefile
index 54b8dcb1993f..debf4ac3fed0 100644
--- a/www/nginx/Makefile
+++ b/www/nginx/Makefile
@@ -1,7 +1,7 @@
PORTNAME= nginx
PORTVERSION= ${NGINX_VERSION}
.include "version.mk"
-PORTREVISION= 1
+PORTREVISION= 2
PORTEPOCH= 3
CATEGORIES= www
MASTER_SITES= https://nginx.org/download/ \
diff --git a/www/nginx/Makefile.extmod b/www/nginx/Makefile.extmod
index e201f8dc6eeb..07291ae59dbf 100644
--- a/www/nginx/Makefile.extmod
+++ b/www/nginx/Makefile.extmod
@@ -348,3 +348,6 @@ ZSTD_CONFIGURE_ENV= ZSTD_INC=${LOCALBASE}/include \
ZSTD_LIB=${LOCALBASE}/lib
ZSTD_GH_TUPLE= tokers:zstd-nginx-module:f4ba115:zstd
ZSTD_VARS= DSO_EXTMODS+=zstd
+ZSTD_EXTRA_PATCHES= ${PATCHDIR}/extra-patch-zstd-empty-flush \
+ ${PATCHDIR}/extra-patch-zstd-last-truncation \
+ ${PATCHDIR}/extra-patch-zstd-full-outbuf
diff --git a/www/nginx/files/extra-patch-zstd-empty-flush b/www/nginx/files/extra-patch-zstd-empty-flush
new file mode 100644
index 000000000000..58d96477d8a4
--- /dev/null
+++ b/www/nginx/files/extra-patch-zstd-empty-flush
@@ -0,0 +1,41 @@
+--- ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c.orig
++++ ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c
+@@ -460,6 +460,38 @@
+ }
+
+ if (ngx_buf_size(ctx->out_buf) == 0) {
++
++ if (rc == 0 && ctx->flush) {
++
++ /*
++ * nothing to flush: pass an empty flush buf downstream,
++ * as the gzip filter does; otherwise ctx->flush is never
++ * reset and the body filter loops forever
++ */
++
++ b = ngx_calloc_buf(r->pool);
++ if (b == NULL) {
++ return NGX_ERROR;
++ }
++
++ b->flush = 1;
++
++ cl = ngx_alloc_chain_link(r->pool);
++ if (cl == NULL) {
++ return NGX_ERROR;
++ }
++
++ cl->buf = b;
++ cl->next = NULL;
++
++ *ctx->last_out = cl;
++ ctx->last_out = &cl->next;
++
++ ctx->flush = 0;
++
++ r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
++ }
++
+ return NGX_AGAIN;
+ }
+
diff --git a/www/nginx/files/extra-patch-zstd-full-outbuf b/www/nginx/files/extra-patch-zstd-full-outbuf
new file mode 100644
index 000000000000..9e6a83e87cfb
--- /dev/null
+++ b/www/nginx/files/extra-patch-zstd-full-outbuf
@@ -0,0 +1,18 @@
+--- ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c.orig
++++ ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c
+@@ -462,7 +462,14 @@
+
+ /* pending to call the ZSTD_endStream() */
+
+- return NGX_AGAIN;
++ if (ctx->buffer_out.pos < ctx->buffer_out.size) {
++ return NGX_AGAIN;
++ }
++
++ /*
++ * the output buffer is full: pass it downstream now, otherwise
++ * ngx_http_zstd_filter_get_buf() replaces it and the data is lost
++ */
+
+ } else {
+ ctx->action = NGX_HTTP_ZSTD_FILTER_COMPRESS; /* restore */
diff --git a/www/nginx/files/extra-patch-zstd-last-truncation b/www/nginx/files/extra-patch-zstd-last-truncation
new file mode 100644
index 000000000000..bb3f8fc34004
--- /dev/null
+++ b/www/nginx/files/extra-patch-zstd-last-truncation
@@ -0,0 +1,60 @@
+--- ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c.orig
++++ ../zstd-nginx-module-f4ba115/filter/ngx_http_zstd_filter_module.c
+@@ -390,6 +390,7 @@
+ {
+ size_t rc, pos_in, pos_out;
+ char *hint;
++ ngx_uint_t end;
+ ngx_chain_t *cl;
+ ngx_buf_t *b;
+
+@@ -440,6 +441,10 @@
+ ctx->out_buf->last += ctx->buffer_out.pos - pos_out;
+ ctx->redo = 0;
+
++ /* the frame is complete only when ZSTD_endStream() has flushed it all */
++
++ end = (rc == 0 && ctx->action == NGX_HTTP_ZSTD_FILTER_END);
++
+ if (rc > 0) {
+ if (ctx->action == NGX_HTTP_ZSTD_FILTER_COMPRESS) {
+ ctx->action = NGX_HTTP_ZSTD_FILTER_FLUSH;
+@@ -447,7 +452,11 @@
+
+ ctx->redo = 1;
+
+- } else if (ctx->last && ctx->action != NGX_HTTP_ZSTD_FILTER_END) {
++ } else if (ctx->last && ctx->action != NGX_HTTP_ZSTD_FILTER_END
++ && ctx->buffer_in.pos == ctx->buffer_in.size)
++ {
++ /* end the frame only after the whole input has been consumed */
++
+ ctx->redo = 1;
+ ctx->action = NGX_HTTP_ZSTD_FILTER_END;
+
+@@ -502,13 +511,13 @@
+
+ b = ctx->out_buf;
+
+- if (rc == 0 && (ctx->flush || ctx->last)) {
++ if (rc == 0 && (ctx->flush || end)) {
+ r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
+
+ b->flush = ctx->flush;
+- b->last_buf = ctx->last;
++ b->last_buf = end;
+
+- ctx->done = ctx->last;
++ ctx->done = end;
+ ctx->flush = 0;
+ }
+
+@@ -522,7 +531,7 @@
+
+ ngx_memzero(&ctx->buffer_out, sizeof(ZSTD_outBuffer));
+
+- return ctx->last && rc == 0 ? NGX_OK : NGX_AGAIN;
++ return end ? NGX_OK : NGX_AGAIN;
+ }
+
+