Skip to content

Commit 9607f1d

Browse files
committed
http2: support upload_limit_rate directive
1 parent eec19e8 commit 9607f1d

File tree

2 files changed

+86
-13
lines changed

2 files changed

+86
-13
lines changed

ngx_http_upload_module.c

+46-12
Original file line numberDiff line numberDiff line change
@@ -935,34 +935,44 @@ ngx_http_upload_read_event_handler(ngx_http_request_t *r)
935935
ngx_http_request_body_t *rb;
936936
ngx_int_t rc;
937937
ngx_chain_t *in;
938+
ssize_t n, limit, buf_read_size, next_buf_size, remaining;
939+
ngx_msec_t delay;
940+
ngx_event_t *rev;
938941

939942
if (ngx_exiting || ngx_terminate) {
940943
ngx_http_finalize_request(r, NGX_HTTP_CLOSE);
941944
return;
942945
}
943946

947+
rev = r->connection->read;
944948
rb = r->request_body;
945949

946950
if (rb == NULL) {
947951
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
948952
return;
949953
}
950954

955+
r->read_event_handler = ngx_http_upload_read_event_handler;
956+
951957
u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
952958

953-
rc = NGX_OK;
959+
for ( ;; ) {
960+
buf_read_size = 0;
954961

955-
in = rb->bufs;
962+
for (in = rb->bufs ; in; in = in->next) {
963+
n = in->buf->last - in->buf->pos;
964+
965+
rc = u->data_handler(u, in->buf->pos, in->buf->pos + n);
966+
967+
in->buf->pos += n;
968+
u->received += n;
969+
buf_read_size += n;
956970

957-
for ( ;; ) {
958-
while (in) {
959-
rc = u->data_handler(u, in->buf->pos, in->buf->last);
960971
if (rc != NGX_OK) {
961972
goto err;
962973
}
963-
in->buf->pos = in->buf->last;
964-
in = in->next;
965974
}
975+
rb->bufs = NULL;
966976

967977
// We're done reading the request body, break out of loop
968978
if (!r->reading_body) {
@@ -974,19 +984,43 @@ ngx_http_upload_read_event_handler(ngx_http_request_t *r)
974984
}
975985
}
976986

987+
// Check whether we have exceeded limit_rate and should delay the next
988+
// buffer read
989+
if (u->limit_rate) {
990+
remaining = ((ssize_t) r->headers_in.content_length_n) - u->received;
991+
next_buf_size = (buf_read_size > remaining) ? remaining : buf_read_size;
992+
limit = u->limit_rate * (ngx_time() - r->start_sec + 1) - (u->received + next_buf_size);
993+
if (limit < 0) {
994+
rev->delayed = 1;
995+
ngx_add_timer(rev, (ngx_msec_t) ((limit * -1000 / u->limit_rate) + 1));
996+
return;
997+
}
998+
}
999+
9771000
rc = ngx_http_read_unbuffered_request_body(r);
9781001

9791002
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
9801003
goto err;
9811004
}
9821005

983-
in = rb->bufs;
984-
rb->bufs = NULL;
985-
986-
if (in == NULL) {
987-
r->read_event_handler = ngx_http_upload_read_event_handler;
1006+
if (rb->bufs == NULL) {
9881007
return;
9891008
}
1009+
1010+
// Check whether we should delay processing the latest request body
1011+
// buffers to stay within limit_rate
1012+
if (u->limit_rate) {
1013+
buf_read_size = 0;
1014+
for (in = rb->bufs ; in; in = in->next) {
1015+
buf_read_size += (in->buf->last - in->buf->pos);
1016+
}
1017+
delay = (ngx_msec_t) (buf_read_size * 1000 / u->limit_rate + 1);
1018+
if (delay > 0) {
1019+
rev->delayed = 1;
1020+
ngx_add_timer(rev, delay);
1021+
return;
1022+
}
1023+
}
9901024
}
9911025

9921026
// Finally, send the response

t/http2.t

+40-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use File::Basename qw(dirname);
55
use lib dirname(__FILE__) . "/lib";
66
use Cwd qw(abs_path);
77

8-
use Test::Nginx::Socket tests => 11;
8+
use Test::Nginx::Socket tests => 20;
99
use Test::Nginx::UploadModule;
1010

1111
$ENV{TEST_DIR} = abs_path(dirname(__FILE__));
@@ -127,3 +127,42 @@ upload_tmp_path = ${ENV{TEST_NGINX_UPLOAD_PATH}}/store/3/0000000003
127127
}]
128128
--- upload_file_like eval
129129
qr/^(??{'x' x 262144})$/
130+
131+
=== Test 4: http2 upload_limit_rate
132+
--- skip_nginx
133+
9: < 1.10.0
134+
--- http2
135+
--- config
136+
location = /upload/ {
137+
upload_pass @upstream;
138+
upload_resumable on;
139+
upload_set_form_field "upload_tmp_path" "$upload_tmp_path";
140+
upload_limit_rate 32768;
141+
}
142+
--- timeout: 5
143+
--- more_headers eval
144+
[qq{X-Content-Range: bytes 0-131071/262144
145+
Session-ID: 0000000004
146+
Content-Type: text/plain
147+
Content-Disposition: form-data; name="file"; filename="test.txt"},
148+
qq{X-Content-Range: bytes 131072-262143/262144
149+
Session-ID: 0000000004
150+
Content-Type: text/plain
151+
Content-Disposition: form-data; name="file"; filename="test.txt"}]
152+
--- request eval
153+
[["POST /upload/\r\n",
154+
"@" . $ENV{TEST_NGINX_UPLOAD_FILE}],
155+
["POST /upload/\r\n",
156+
"@" . $ENV{TEST_NGINX_UPLOAD_FILE}]]
157+
--- error_code eval
158+
[201, 200]
159+
--- response_body eval
160+
["0-131071/262144", qq{upload_tmp_path = ${ENV{TEST_NGINX_UPLOAD_PATH}}/store/4/0000000004
161+
}]
162+
--- upload_file_like eval
163+
qr/^(??{'x' x 262144})$/
164+
--- access_log eval
165+
# should have taken 4 seconds, with 1 second possible error
166+
# (Test::Nginx::UploadModule::http_config adds request time to the end of
167+
# the access log)
168+
[qr/[34]\.\d\d\d$/, qr/[34]\.\d\d\d$/]

0 commit comments

Comments
 (0)