[PATCH 7 of 7] Added the "client_body_min_rate" configuration directive
Maxim Dounin
mdounin at mdounin.ru
Wed Jun 18 12:37:39 UTC 2025
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1750204984 -10800
# Wed Jun 18 03:03:04 2025 +0300
# Node ID 121d6685362076da8b261e92674b17c2a7ca145e
# Parent 422bc6906b928c937e053a44c9854815223ff609
Added the "client_body_min_rate" configuration directive.
Similarly to send_min_rate, this directive limits minimum allowed data rate
when reading the request body.
diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -167,6 +167,7 @@ void ngx_http_test_reading(ngx_http_requ
void ngx_http_send_timeout(ngx_http_request_t *r, off_t sent);
+void ngx_http_request_body_timeout(ngx_http_request_t *r, off_t bytes);
char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -373,6 +373,13 @@ static ngx_command_t ngx_http_core_comm
offsetof(ngx_http_core_loc_conf_t, client_body_timeout),
NULL },
+ { ngx_string("client_body_min_rate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, client_body_min_rate),
+ NULL },
+
{ ngx_string("client_body_temp_path"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
ngx_conf_set_path_slot,
@@ -3594,6 +3601,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t
clcf->client_max_body_size = NGX_CONF_UNSET;
clcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE;
clcf->client_body_timeout = NGX_CONF_UNSET_MSEC;
+ clcf->client_body_min_rate = NGX_CONF_UNSET_SIZE;
clcf->satisfy = NGX_CONF_UNSET_UINT;
clcf->auth_delay = NGX_CONF_UNSET_MSEC;
clcf->if_modified_since = NGX_CONF_UNSET_UINT;
diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -359,6 +359,7 @@ struct ngx_http_core_loc_conf_s {
off_t directio_alignment; /* directio_alignment */
size_t client_body_buffer_size; /* client_body_buffer_size */
+ size_t client_body_min_rate; /* client_body_min_rate */
size_t send_min_rate; /* send_min_rate */
size_t send_lowat; /* send_lowat */
size_t postpone_output; /* postpone_output */
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -303,6 +303,8 @@ typedef struct {
ngx_buf_t *buf;
off_t rest;
off_t received;
+ ngx_msec_t rate_last;
+ off_t rate_excess;
ngx_chain_t *free;
ngx_chain_t *busy;
ngx_http_chunked_t *chunked;
diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -12,6 +12,8 @@
static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
+static ngx_int_t ngx_http_request_body_min_rate(ngx_http_request_t *r,
+ off_t bytes);
static ngx_int_t ngx_http_copy_pipelined_header(ngx_http_request_t *r,
ngx_buf_t *buf);
static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);
@@ -84,6 +86,7 @@ ngx_http_read_client_request_body(ngx_ht
* rb->busy = NULL;
* rb->chunked = NULL;
* rb->received = 0;
+ * rb->rate_last = 0;
* rb->no_buffering = 0;
* rb->filter_need_buffering = 0;
* rb->last_sent = 0;
@@ -334,19 +337,19 @@ ngx_http_read_client_request_body_handle
static ngx_int_t
ngx_http_do_read_client_request_body(ngx_http_request_t *r)
{
- off_t rest;
- size_t size;
- ssize_t n;
- ngx_int_t rc;
- ngx_uint_t flush;
- ngx_chain_t out;
- ngx_connection_t *c;
- ngx_http_request_body_t *rb;
- ngx_http_core_loc_conf_t *clcf;
+ off_t rest, bytes;
+ size_t size;
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_uint_t flush;
+ ngx_chain_t out;
+ ngx_connection_t *c;
+ ngx_http_request_body_t *rb;
c = r->connection;
rb = r->request_body;
flush = 1;
+ bytes = 0;
n = NGX_AGAIN;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
@@ -382,9 +385,7 @@ ngx_http_do_read_client_request_body(ngx
}
if (rb->filter_need_buffering) {
- clcf = ngx_http_get_module_loc_conf(r,
- ngx_http_core_module);
- ngx_add_timer(c->read, clcf->client_body_timeout);
+ ngx_http_request_body_timeout(r, bytes);
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -436,6 +437,7 @@ ngx_http_do_read_client_request_body(ngx
rb->buf->last += n;
r->request_length += n;
+ bytes += n;
/* pass buffer to request body filter chain */
@@ -475,8 +477,7 @@ ngx_http_do_read_client_request_body(ngx
if (n == NGX_AGAIN || !c->read->ready || rb->rest == 0) {
- clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
- ngx_add_timer(c->read, clcf->client_body_timeout);
+ ngx_http_request_body_timeout(r, bytes);
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -504,6 +505,67 @@ ngx_http_do_read_client_request_body(ngx
}
+void
+ngx_http_request_body_timeout(ngx_http_request_t *r, off_t bytes)
+{
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (!ngx_http_request_body_min_rate(r, bytes)) {
+ return;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+ ngx_add_timer(r->connection->read, clcf->client_body_timeout);
+}
+
+
+static ngx_int_t
+ngx_http_request_body_min_rate(ngx_http_request_t *r, off_t bytes)
+{
+ ngx_msec_t now;
+ ngx_msec_int_t ms;
+ ngx_connection_t *c;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ rb = r->request_body;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->client_body_min_rate == 0) {
+ return (bytes > 0 || !c->read->timer_set);
+ }
+
+ now = ngx_current_msec;
+
+ if (rb->rate_last == 0 || !c->read->timer_set) {
+ rb->rate_last = now;
+ rb->rate_excess = 0;
+ return 1;
+ }
+
+ ms = (ngx_msec_int_t) (now - rb->rate_last);
+ ms = ngx_max(ms, 0);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http body min rate: %O, %O, %M",
+ bytes, rb->rate_excess, ms);
+
+ if (rb->rate_excess + bytes
+ > (off_t) clcf->client_body_min_rate * ms / 1000)
+ {
+ rb->rate_last = now;
+ rb->rate_excess = 0;
+ return 1;
+ }
+
+ rb->rate_excess += bytes;
+
+ return 0;
+}
+
+
static ngx_int_t
ngx_http_copy_pipelined_header(ngx_http_request_t *r, ngx_buf_t *buf)
{
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -3988,7 +3988,7 @@ ngx_http_v2_read_request_body(ngx_http_r
}
if (!buf) {
- ngx_add_timer(r->connection->read, clcf->client_body_timeout);
+ ngx_http_request_body_timeout(r, 0);
}
r->read_event_handler = ngx_http_v2_read_client_request_body_handler;
@@ -4002,11 +4002,11 @@ static ngx_int_t
ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos,
size_t size, ngx_uint_t last, ngx_uint_t flush)
{
- size_t n;
- ngx_int_t rc;
- ngx_connection_t *fc;
- ngx_http_request_body_t *rb;
- ngx_http_core_loc_conf_t *clcf;
+ off_t bytes;
+ size_t n;
+ ngx_int_t rc;
+ ngx_connection_t *fc;
+ ngx_http_request_body_t *rb;
fc = r->connection;
rb = r->request_body;
@@ -4018,6 +4018,8 @@ ngx_http_v2_process_request_body(ngx_htt
return NGX_AGAIN;
}
+ bytes = 0;
+
for ( ;; ) {
for ( ;; ) {
if (rb->buf->last == rb->buf->end && size) {
@@ -4070,6 +4072,7 @@ ngx_http_v2_process_request_body(ngx_htt
pos += n;
size -= n;
+ bytes += n;
if (size == 0 && last) {
rb->rest = 0;
@@ -4096,8 +4099,7 @@ ngx_http_v2_process_request_body(ngx_htt
}
if (size == 0) {
- clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
- ngx_add_timer(fc->read, clcf->client_body_timeout);
+ ngx_http_request_body_timeout(r, bytes);
if (!flush) {
ngx_post_event(fc->read, &ngx_posted_events);
diff --git a/src/http/v3/ngx_http_v3_request.c b/src/http/v3/ngx_http_v3_request.c
--- a/src/http/v3/ngx_http_v3_request.c
+++ b/src/http/v3/ngx_http_v3_request.c
@@ -1336,19 +1336,19 @@ ngx_http_v3_read_unbuffered_request_body
static ngx_int_t
ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r)
{
- off_t rest;
- size_t size;
- ssize_t n;
- ngx_int_t rc;
- ngx_uint_t flush;
- ngx_chain_t out;
- ngx_connection_t *c;
- ngx_http_request_body_t *rb;
- ngx_http_core_loc_conf_t *clcf;
+ off_t rest, bytes;
+ size_t size;
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_uint_t flush;
+ ngx_chain_t out;
+ ngx_connection_t *c;
+ ngx_http_request_body_t *rb;
c = r->connection;
rb = r->request_body;
flush = 1;
+ bytes = 0;
n = NGX_AGAIN;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
@@ -1384,9 +1384,7 @@ ngx_http_v3_do_read_client_request_body(
}
if (rb->filter_need_buffering) {
- clcf = ngx_http_get_module_loc_conf(r,
- ngx_http_core_module);
- ngx_add_timer(c->read, clcf->client_body_timeout);
+ ngx_http_request_body_timeout(r, bytes);
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -1436,6 +1434,7 @@ ngx_http_v3_do_read_client_request_body(
}
rb->buf->last += n;
+ bytes += n;
/* pass buffer to request body filter chain */
@@ -1475,8 +1474,7 @@ ngx_http_v3_do_read_client_request_body(
if (n == NGX_AGAIN || !c->read->ready || rb->rest == 0) {
- clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
- ngx_add_timer(c->read, clcf->client_body_timeout);
+ ngx_http_request_body_timeout(r, bytes);
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
More information about the nginx-devel
mailing list