Skip to content

Commit

Permalink
Add http2 support
Browse files Browse the repository at this point in the history
  • Loading branch information
fdintino committed Mar 9, 2018
1 parent 3481680 commit eec19e8
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 16 deletions.
62 changes: 51 additions & 11 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sudo: false
sudo: required
language: c
compiler: gcc
dist: trusty
Expand All @@ -8,28 +8,68 @@ cache:
- perl5
- dl

addons:
apt:
packages:
- libssl-dev

env:
global:
- TESTNGINX_VER=12152a5
- PATH=$TRAVIS_BUILD_DIR/bin:$TRAVIS_BUILD_DIR/nginx/objs:$PATH
- PATH=/usr/local/bin:$TRAVIS_BUILD_DIR/nginx/objs:$PATH
- CURL=7.58.0
- NGHTTP2=1.24.0
matrix:
- NGINX_VERSION=1.9.15
- NGINX_VERSION=1.11.13
- NGINX_VERSION=1.12.2
- NGINX_VERSION=1.13.8

before_install:
- curl --version
- mkdir -p {bin,dl}
- if [ ! -f dl/nginx-${NGINX_VERSION}.tar.gz ]; then curl -o dl/nginx-${NGINX_VERSION}.tar.gz -L http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz; fi
- if [ ! -f dl/test-nginx-${TESTNGINX_VER}.tar.gz ]; then curl -o dl/test-nginx-${TESTNGINX_VER}.tar.gz -L "https://github.com/openresty/test-nginx/archive/${TESTNGINX_VER}.tar.gz"; fi
- mkdir -p dl
- |
if [ ! -f dl/nghttp2-${NGHTTP2}.tar.gz ]; then
(cd dl && curl -O -L https://github.com/nghttp2/nghttp2/releases/download/v${NGHTTP2}/nghttp2-${NGHTTP2}.tar.gz)
fi
- |
if [ ! -f dl/curl-${CURL}.tar.gz ]; then
(cd dl && curl -O -L https://curl.haxx.se/download/curl-${CURL}.tar.gz)
fi
- |
if [ ! -f dl/nginx-${NGINX_VERSION}.tar.gz ]; then
curl -o dl/nginx-${NGINX_VERSION}.tar.gz -L http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz;
fi
- |
if [ ! -f dl/test-nginx-${TESTNGINX_VER}.tar.gz ]; then
curl -o dl/test-nginx-${TESTNGINX_VER}.tar.gz -L "https://github.com/openresty/test-nginx/archive/${TESTNGINX_VER}.tar.gz";
fi
- if [ ! -f dl/cpanm ]; then curl -o dl/cpanm https://cpanmin.us/; chmod +x dl/cpanm; fi
- cp dl/cpanm bin/cpanm
- tar -zxvf dl/nginx-${NGINX_VERSION}.tar.gz && mv nginx-${NGINX_VERSION} nginx
- |
if [ ! -e dl/nghttp2-${NGHTTP2} ]; then
(cd dl && tar -zxf nghttp2-${NGHTTP2}.tar.gz)
fi
(cd dl/nghttp2-${NGHTTP2} &&
./configure --prefix=/usr --disable-threads &&
make && sudo make install)
- |
if [ ! -e dl/curl-${CURL} ]; then
(cd dl && tar -zxf curl-${CURL}.tar.gz)
fi
(cd dl/curl-${CURL} && ./configure --with-nghttp2 --prefix=/usr/local && make && sudo make install)
- sudo ldconfig
- sudo cp dl/cpanm /usr/local/bin/cpanm
- tar -zxf dl/nginx-${NGINX_VERSION}.tar.gz && mv nginx-${NGINX_VERSION} nginx
- cpanm --notest --local-lib=perl5 local::lib && eval $(perl -I perl5/lib/perl5/ -Mlocal::lib)
- cpanm --notest dl/test-nginx-${TESTNGINX_VER}.tar.gz
- cpanm --notest Test::File
- cpanm --notest URI::Query
- |
if [ ! -f perl5/lib/perl5/Test/Nginx.pm ]; then
cpanm --notest dl/test-nginx-${TESTNGINX_VER}.tar.gz
fi
- |
if [ ! -f perl5/lib/perl5/Test/File.pm ]; then
cpanm --notest Test::File
fi
install:
- cd nginx
- ./configure --with-http_v2_module --with-http_ssl_module --add-module=..
- make
Expand Down
120 changes: 115 additions & 5 deletions ngx_http_upload_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,15 @@ typedef ngx_md5_t MD5_CTX;
#define FIELDNAME_STRING "name=\""
#define BYTES_UNIT_STRING "bytes "

#define NGX_UPLOAD_MALFORMED -1
#define NGX_UPLOAD_NOMEM -2
#define NGX_UPLOAD_IOERROR -3
#define NGX_UPLOAD_SCRIPTERROR -4
#define NGX_UPLOAD_TOOLARGE -5
#define NGX_UPLOAD_MALFORMED -11
#define NGX_UPLOAD_NOMEM -12
#define NGX_UPLOAD_IOERROR -13
#define NGX_UPLOAD_SCRIPTERROR -14
#define NGX_UPLOAD_TOOLARGE -15

#ifndef NGX_HTTP_V2
#define NGX_HTTP_V2 0
#endif

/*
* State of multipart/form-data parser
Expand Down Expand Up @@ -287,6 +291,9 @@ typedef struct ngx_http_upload_ctx_s {

static ngx_int_t ngx_http_upload_test_expect(ngx_http_request_t *r);

#if (NGX_HTTP_V2)
static void ngx_http_upload_read_event_handler(ngx_http_request_t *r);
#endif
static ngx_int_t ngx_http_upload_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_upload_options_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r);
Expand Down Expand Up @@ -896,6 +903,21 @@ ngx_http_upload_handler(ngx_http_request_t *r)
if(upload_start(u, ulcf) != NGX_OK)
return NGX_HTTP_INTERNAL_SERVER_ERROR;

#if (NGX_HTTP_V2)
if (r->stream) {
r->request_body_no_buffering = 1;

rc = ngx_http_read_client_request_body(r, ngx_http_upload_read_event_handler);

if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
upload_shutdown_ctx(u);
return rc;
}

return NGX_DONE;
}
#endif

rc = ngx_http_read_upload_client_request_body(r);

if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
Expand All @@ -905,6 +927,94 @@ ngx_http_upload_handler(ngx_http_request_t *r)
return NGX_DONE;
} /* }}} */

#if (NGX_HTTP_V2)
static void
ngx_http_upload_read_event_handler(ngx_http_request_t *r)
{
ngx_http_upload_ctx_t *u;
ngx_http_request_body_t *rb;
ngx_int_t rc;
ngx_chain_t *in;

if (ngx_exiting || ngx_terminate) {
ngx_http_finalize_request(r, NGX_HTTP_CLOSE);
return;
}

rb = r->request_body;

if (rb == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}

u = ngx_http_get_module_ctx(r, ngx_http_upload_module);

rc = NGX_OK;

in = rb->bufs;

for ( ;; ) {
while (in) {
rc = u->data_handler(u, in->buf->pos, in->buf->last);
if (rc != NGX_OK) {
goto err;
}
in->buf->pos = in->buf->last;
in = in->next;
}

// We're done reading the request body, break out of loop
if (!r->reading_body) {
rc = u->data_handler(u, NULL, NULL);
if (rc == NGX_OK) {
break;
} else {
goto err;
}
}

rc = ngx_http_read_unbuffered_request_body(r);

if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
goto err;
}

in = rb->bufs;
rb->bufs = NULL;

if (in == NULL) {
r->read_event_handler = ngx_http_upload_read_event_handler;
return;
}
}

// Finally, send the response
rc = ngx_http_upload_body_handler(r);

err:
switch(rc) {
case NGX_UPLOAD_MALFORMED:
rc = NGX_HTTP_BAD_REQUEST;
break;
case NGX_UPLOAD_TOOLARGE:
rc = NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
break;
case NGX_UPLOAD_IOERROR:
rc = NGX_HTTP_SERVICE_UNAVAILABLE;
break;
case NGX_UPLOAD_NOMEM:
case NGX_UPLOAD_SCRIPTERROR:
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
break;
}
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
upload_shutdown_ctx(u);
ngx_http_finalize_request(r, rc);
}
}
#endif

static ngx_int_t ngx_http_upload_add_headers(ngx_http_request_t *r, ngx_http_upload_loc_conf_t *ulcf) { /* {{{ */
ngx_str_t name;
ngx_str_t value;
Expand Down
129 changes: 129 additions & 0 deletions t/http2.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use strict;
use warnings;

use File::Basename qw(dirname);
use lib dirname(__FILE__) . "/lib";
use Cwd qw(abs_path);

use Test::Nginx::Socket tests => 11;
use Test::Nginx::UploadModule;

$ENV{TEST_DIR} = abs_path(dirname(__FILE__));


our $config = <<'_EOC_';
location = /upload/ {
upload_pass @upstream;
upload_resumable on;
upload_set_form_field upload_file_name $upload_file_name;
upload_set_form_field upload_file_number $upload_file_number;
upload_set_form_field "upload_field_name" "$upload_field_name";
upload_set_form_field "upload_content_type" "$upload_content_type";
upload_set_form_field "upload_tmp_path" "$upload_tmp_path";
upload_set_form_field "upload_content_range" "$upload_content_range";
upload_aggregate_form_field "upload_file_size" "$upload_file_size";
upload_max_file_size 0;
upload_pass_args on;
upload_cleanup 400 404 499 500-505;
}
_EOC_

no_long_string();
no_shuffle();
run_tests();

__DATA__
=== TEST 1: http2 simple upload
--- config eval: $::config
--- http2
--- skip_nginx
3: < 1.10.0
--- more_headers
X-Content-Range: bytes 0-3/4
Session-ID: 0000000001
Content-Type: text/plain
Content-Disposition: form-data; name="file"; filename="test.txt"
--- request eval
qq{POST /upload/
test}
--- error_code: 200
--- response_body eval
qq{upload_content_range = bytes 0-3/4
upload_content_type = text/plain
upload_field_name = file
upload_file_name = test.txt
upload_file_number = 1
upload_file_size = 4
upload_tmp_path = $ENV{TEST_NGINX_UPLOAD_PATH}/store/1/0000000001
}
--- upload_file eval
"test"
=== TEST 2: http2 multiple chunk uploads
--- http_config eval: $::http_config
--- config eval: $::config
--- http2
--- skip_nginx
3: < 1.10.0
--- more_headers eval
[qq{X-Content-Range: bytes 0-1/4
Session-ID: 0000000002
Content-Type: text/plain
Content-Disposition: form-data; name="file"; filename="test.txt"},
qq{X-Content-Range: bytes 2-3/4
Session-ID: 0000000002
Content-Type: text/plain
Content-Disposition: form-data; name="file"; filename="test.txt"}]
--- request eval
[["POST /upload/\r\n",
"te"],
["POST /upload/\r\n",
"st"]]
--- error_code eval
[201, 200]
--- response_body eval
["0-1/4", qq{upload_content_range = bytes 2-3/4
upload_content_type = text/plain
upload_field_name = file
upload_file_name = test.txt
upload_file_number = 1
upload_file_size = 4
upload_tmp_path = $ENV{TEST_NGINX_UPLOAD_PATH}/store/2/0000000002
}]
--- upload_file eval
"test"
=== Test 3: http2 large multiple chunk uploads
--- http_config eval: $::http_config
--- skip_nginx
5: < 1.10.0
--- http2
--- config eval: $::config
--- more_headers eval
[qq{X-Content-Range: bytes 0-131071/262144
Session-ID: 0000000003
Content-Type: text/plain
Content-Disposition: form-data; name="file"; filename="test.txt"},
qq{X-Content-Range: bytes 131072-262143/262144
Session-ID: 0000000003
Content-Type: text/plain
Content-Disposition: form-data; name="file"; filename="test.txt"}]
--- request eval
[["POST /upload/\r\n",
"@" . $ENV{TEST_NGINX_UPLOAD_FILE}],
["POST /upload/\r\n",
"@" . $ENV{TEST_NGINX_UPLOAD_FILE}]]
--- error_code eval
[201, 200]
--- response_body eval
["0-131071/262144", qq{upload_content_range = bytes 131072-262143/262144
upload_content_type = text/plain
upload_field_name = file
upload_file_name = test.txt
upload_file_number = 1
upload_file_size = 262144
upload_tmp_path = ${ENV{TEST_NGINX_UPLOAD_PATH}}/store/3/0000000003
}]
--- upload_file_like eval
qr/^(??{'x' x 262144})$/
4 changes: 4 additions & 0 deletions t/lib/Test/Nginx/UploadModule.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use warnings;

my $PORT = $ENV{TEST_NGINX_UPSTREAM_PORT} ||= 12345;
$ENV{TEST_NGINX_UPLOAD_PATH} ||= '/tmp/upload';
$ENV{TEST_NGINX_UPLOAD_FILE} = $ENV{TEST_NGINX_UPLOAD_PATH} . "/test_data.txt";


use base 'Exporter';
Expand Down Expand Up @@ -32,6 +33,9 @@ sub make_upload_paths {
for (my $i = 0; $i < 10; $i++) {
mkpath("${ENV{TEST_NGINX_UPLOAD_PATH}}/store/$i");
}
open(my $fh, ">", $ENV{TEST_NGINX_UPLOAD_FILE});
print $fh ('x' x 131072);
close($fh);
}

add_cleanup_handler(sub {
Expand Down

0 comments on commit eec19e8

Please sign in to comment.