[PATCH] HTTP/2: make http2 server support http1
Haitao Lv
i at lvht.net
Mon Mar 5 03:11:08 UTC 2018
@wbr
> On Mar 4, 2018, at 13:49, Haitao Lv <i at lvht.net> wrote:
>
> Here is the new patch
>
> diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
> index 9d8b6d79..c06ef7c7 100644
> --- a/src/http/ngx_http.c
> +++ b/src/http/ngx_http.c
> @@ -1197,6 +1197,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
> ngx_uint_t ssl;
> #endif
> #if (NGX_HTTP_V2)
> + ngx_uint_t http1;
> ngx_uint_t http2;
> #endif
>
> @@ -1232,6 +1233,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
> ssl = lsopt->ssl || addr[i].opt.ssl;
> #endif
> #if (NGX_HTTP_V2)
> + http1 = lsopt->http1 || addr[i].opt.http1;
> http2 = lsopt->http2 || addr[i].opt.http2;
> #endif
>
> @@ -1266,6 +1268,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
> addr[i].opt.ssl = ssl;
> #endif
> #if (NGX_HTTP_V2)
> + addr[i].opt.http1 = http1;
> addr[i].opt.http2 = http2;
> #endif
>
> @@ -1802,6 +1805,7 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
> addrs[i].conf.ssl = addr[i].opt.ssl;
> #endif
> #if (NGX_HTTP_V2)
> + addrs[i].conf.http1 = addr[i].opt.http1;
> addrs[i].conf.http2 = addr[i].opt.http2;
> #endif
> addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
> @@ -1867,6 +1871,7 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,
> addrs6[i].conf.ssl = addr[i].opt.ssl;
> #endif
> #if (NGX_HTTP_V2)
> + addrs6[i].conf.http1 = addr[i].opt.http1;
> addrs6[i].conf.http2 = addr[i].opt.http2;
> #endif
> addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
> diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
> index 6b318dd0..741a4dd9 100644
> --- a/src/http/ngx_http_core_module.c
> +++ b/src/http/ngx_http_core_module.c
> @@ -3985,6 +3985,18 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
> #endif
> }
>
> + if (ngx_strcmp(value[n].data, "http1") == 0) {
> +#if (NGX_HTTP_V2)
> + lsopt.http1 = 1;
> + continue;
> +#else
> + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> + "the \"http1\" parameter requires "
> + "ngx_http_v2_module");
> + return NGX_CONF_ERROR;
> +#endif
> + }
> +
> if (ngx_strcmp(value[n].data, "spdy") == 0) {
> ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
> "invalid parameter \"spdy\": "
> diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
> index d7985049..68b3e7d9 100644
> --- a/src/http/ngx_http_core_module.h
> +++ b/src/http/ngx_http_core_module.h
> @@ -73,6 +73,7 @@ typedef struct {
> unsigned bind:1;
> unsigned wildcard:1;
> unsigned ssl:1;
> + unsigned http1:1;
> unsigned http2:1;
> #if (NGX_HAVE_INET6)
> unsigned ipv6only:1;
> @@ -234,6 +235,7 @@ struct ngx_http_addr_conf_s {
> ngx_http_virtual_names_t *virtual_names;
>
> unsigned ssl:1;
> + unsigned http1:1;
> unsigned http2:1;
> unsigned proxy_protocol:1;
> };
> diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c
> index 844054c9..9eb713b7 100644
> --- a/src/http/ngx_http_parse.c
> +++ b/src/http/ngx_http_parse.c
> @@ -117,6 +117,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
> sw_host_ip_literal,
> sw_port,
> sw_host_http_09,
> + sw_h2_preface,
> sw_after_slash_in_uri,
> sw_check_uri,
> sw_check_uri_http_09,
> @@ -134,6 +135,12 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
> sw_almost_done
> } state;
>
> +#if (NGX_HTTP_V2)
> + static u_char h2_preface[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
> + u_char *h2_preface_end = h2_preface + 24;
> + u_char *n = h2_preface + 4;
> +#endif
> +
> state = r->state;
>
> for (p = b->pos; p < b->last; p++) {
> @@ -145,10 +152,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
> case sw_start:
> r->request_start = p;
>
> - if (ch == CR || ch == LF) {
> - break;
> - }
> -
I think Nginx should not allow any leading \r or \n. HTTP client should never send this chars
before the request line. Support this feature makes the buffer management more harder.
> if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
> return NGX_HTTP_PARSE_INVALID_METHOD;
> }
> @@ -174,6 +177,13 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
> break;
> }
>
> +#if (NGX_HTTP_V2)
> + if (ngx_str3_cmp(m, 'P', 'R', 'I', ' ')) {
> + r->method = NGX_HTTP_PRI;
> + break;
> + }
> +#endif
> +
> break;
>
> case 4:
> @@ -267,6 +277,14 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
> }
>
> state = sw_spaces_before_uri;
> +
> +#if (NGX_HTTP_V2)
> + if (r->method & NGX_HTTP_PRI) {
> + r->uri_start = p;
> + state = sw_h2_preface;
> + }
> +#endif
> +
> break;
> }
>
> @@ -276,6 +294,29 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
>
> break;
>
> +#if (NGX_HTTP_V2)
> + case sw_h2_preface:
> +
> + if (ch == *n++) {
I introduce a new sw_h2_preface state. In this state, the parser will check the h2 preface char by char.
The parser only check at most 20 chars "* HTTP/2.0\r\n\r\nSM\r\n\r\n". So the buffer will not be fulfilled.
> + if (n == h2_preface_end) {
> + r->request_end = r->request_start + 14;
> + r->uri_start = r->request_start + 4;
> + r->uri_end = r->request_start + 5;
> + r->http_protocol.data = r->request_start + 6;
> + r->http_major = 2;
> + r->http_minor = 0;
> +
> + goto done;
> + }
> +
> + break;
> + }
> +
> + return NGX_HTTP_PARSE_INVALID_METHOD;
> +
> + break;
> +#endif
> +
> /* space* before URI */
> case sw_spaces_before_uri:
>
> @@ -724,9 +765,11 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
>
> r->http_major = ch - '0';
>
> +#if !(NGX_HTTP_V2)
> if (r->http_major > 1) {
> return NGX_HTTP_PARSE_INVALID_VERSION;
> }
> +#endif
>
> state = sw_major_digit;
> break;
> @@ -744,9 +787,11 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
>
> r->http_major = r->http_major * 10 + (ch - '0');
>
> +#if !(NGX_HTTP_V2)
> if (r->http_major > 1) {
> return NGX_HTTP_PARSE_INVALID_VERSION;
> }
> +#endif
>
> break;
>
> diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
> index 89cfe77a..3ae64569 100644
> --- a/src/http/ngx_http_request.c
> +++ b/src/http/ngx_http_request.c
> @@ -8,6 +8,9 @@
> #include <ngx_config.h>
> #include <ngx_core.h>
> #include <ngx_http.h>
> +#if (NGX_HTTP_V2)
> +#include <ngx_http_v2_module.h>
> +#endif
>
>
> static void ngx_http_wait_request_handler(ngx_event_t *ev);
> @@ -17,6 +20,10 @@ static ssize_t ngx_http_read_request_header(ngx_http_request_t *r);
> static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
> ngx_uint_t request_line);
>
> +#if (NGX_HTTP_V2)
> +static void ngx_http_process_h2_preface(ngx_event_t *rev);
> +#endif
> +
> static ngx_int_t ngx_http_process_header_line(ngx_http_request_t *r,
> ngx_table_elt_t *h, ngx_uint_t offset);
> static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r,
> @@ -320,7 +327,7 @@ ngx_http_init_connection(ngx_connection_t *c)
> c->write->handler = ngx_http_empty_handler;
>
> #if (NGX_HTTP_V2)
> - if (hc->addr_conf->http2) {
> + if (hc->addr_conf->http2 && !hc->addr_conf->http1) {
> rev->handler = ngx_http_v2_init;
> }
> #endif
> @@ -939,14 +946,16 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
> static void
> ngx_http_process_request_line(ngx_event_t *rev)
> {
> - ssize_t n;
> - ngx_int_t rc, rv;
> - ngx_str_t host;
> - ngx_connection_t *c;
> - ngx_http_request_t *r;
> + ssize_t n;
> + ngx_int_t rc, rv;
> + ngx_str_t host;
> + ngx_connection_t *c;
> + ngx_http_connection_t *hc;
> + ngx_http_request_t *r;
>
> c = rev->data;
> r = c->data;
> + hc = r->http_connection;
>
> ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
> "http process request line");
> @@ -1033,6 +1042,18 @@ ngx_http_process_request_line(ngx_event_t *rev)
> return;
> }
>
> +#if (NGX_HTTP_V2)
> + if (hc->addr_conf->http2 && (r->method & NGX_HTTP_PRI)) {
> +
> + c->log->action = "reading client h2 preface";
> +
> + ngx_http_process_h2_preface(rev);
> + return;
> + } else if (r->method & NGX_HTTP_PRI) {
> + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
> + return;
> + }
> +#endif
>
> if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
> sizeof(ngx_table_elt_t))
> @@ -1208,6 +1229,45 @@ ngx_http_process_request_uri(ngx_http_request_t *r)
> return NGX_OK;
> }
>
> +#if (NGX_HTTP_V2)
> +static void
> +ngx_http_process_h2_preface(ngx_event_t *rev)
> +{
> + size_t len;
> + ngx_connection_t *c;
> + ngx_http_request_t *r;
> + ngx_http_connection_t *hc;
> + ngx_http_v2_main_conf_t *h2mcf;
> +
> + c = rev->data;
> + r = c->data;
> + hc = r->http_connection;
> +
> + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
> + "http process h2 preface");
> +
> + r->header_in->pos = r->header_in->start + 24;
> +
> + len = r->header_in->last - r->header_in->pos;
> +
> + h2mcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_v2_module);
> +
> + if (len <= h2mcf->recv_buffer_size) {
We check the recv buffer size before do the ngx_http_v2_init_after_preface.
> + c->data = r->http_connection;
> +
> + ngx_http_free_request(r, -1);
> +
> + ngx_http_v2_init_after_preface(rev, r->header_in);
> + return;
> + }
> +
> + ngx_log_error(NGX_LOG_INFO, c->log, 0,
> + "client sent invalid h2 preface");
> +
> + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
> +}
> +#endif
> +
>
> static void
> ngx_http_process_request_headers(ngx_event_t *rev)
> @@ -1809,7 +1869,7 @@ ngx_http_process_request_header(ngx_http_request_t *r)
> return NGX_ERROR;
> }
>
> - if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) {
> + if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_11) {
> ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
> "client sent HTTP/1.1 request without \"Host\" header");
> ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
> @@ -3506,7 +3566,9 @@ ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc)
>
> log->action = "logging request";
>
> - ngx_http_log_request(r);
> + if (rc >= 0) {
> + ngx_http_log_request(r);
> + }
>
> log->action = "closing request";
>
> diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
> index 5d44c06e..34140655 100644
> --- a/src/http/ngx_http_request.h
> +++ b/src/http/ngx_http_request.h
> @@ -41,6 +41,7 @@
> #define NGX_HTTP_UNLOCK 0x2000
> #define NGX_HTTP_PATCH 0x4000
> #define NGX_HTTP_TRACE 0x8000
> +#define NGX_HTTP_PRI 0x010000
>
> #define NGX_HTTP_CONNECTION_CLOSE 1
> #define NGX_HTTP_CONNECTION_KEEP_ALIVE 2
> diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
> index d9df0f90..ea72f129 100644
> --- a/src/http/v2/ngx_http_v2.c
> +++ b/src/http/v2/ngx_http_v2.c
> @@ -230,6 +230,13 @@ static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = {
>
> void
> ngx_http_v2_init(ngx_event_t *rev)
> +{
> + ngx_http_v2_init_after_preface(rev, NULL);
> +}
> +
> +
> +void
> +ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf)
> {
> ngx_connection_t *c;
> ngx_pool_cleanup_t *cln;
> @@ -316,6 +323,12 @@ ngx_http_v2_init(ngx_event_t *rev)
> h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol
> : ngx_http_v2_state_preface;
>
> + if (buf != NULL) {
> + ngx_memcpy(h2mcf->recv_buffer, buf->pos, buf->last - buf->pos);
> + h2c->state.buffer_used = buf->last - buf->pos;
> + h2c->state.handler = ngx_http_v2_state_head;
> + }
> +
> ngx_queue_init(&h2c->waiting);
> ngx_queue_init(&h2c->dependencies);
> ngx_queue_init(&h2c->closed);
> @@ -381,13 +394,17 @@ ngx_http_v2_read_handler(ngx_event_t *rev)
> h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx,
> ngx_http_v2_module);
>
> - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE;
> + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE;
>
> do {
> p = h2mcf->recv_buffer;
>
> - ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE);
> end = p + h2c->state.buffer_used;
> + if (h2c->state.buffer_used) {
> + goto do_state_handler;
> + }
> +
> + ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE);
>
> n = c->recv(c, end, available);
>
> @@ -410,6 +427,8 @@ ngx_http_v2_read_handler(ngx_event_t *rev)
>
> end += n;
>
> +do_state_handler:
> +
> h2c->state.buffer_used = 0;
> h2c->state.incomplete = 0;
>
> diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h
> index d89e8fef..cc9251f0 100644
> --- a/src/http/v2/ngx_http_v2.h
> +++ b/src/http/v2/ngx_http_v2.h
> @@ -279,6 +279,7 @@ ngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c,
>
>
> void ngx_http_v2_init(ngx_event_t *rev);
> +void ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf);
>
> ngx_int_t ngx_http_v2_read_request_body(ngx_http_request_t *r);
> ngx_int_t ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r);
>
>
>> On Mar 4, 2018, at 10:53, Haitao Lv <i at lvht.net> wrote:
>>
>> Hi, wbr,
>>
>> Thanks for your review. I don't know why I can't receive your email.
>> Let me reply directly.
>>
>>> It doesn't look like a useful feature.
>>> Could you please explain the use cases?
>>
>> The current implementation support both http/1 and http/2 over the
>> same TLS listen port by the ALPN Extension.
>>
>> Nginx also support listening http/2 over plain tcp port, which is
>> perfect for development and inner production environment.
>>
>> However, the current implementation cannot support http/1.1 and http/2
>> simultaneously. We have no choice but listen on two different ports.
>> As a result, one same service has two ports and different clients
>> should choose their suitable port.
>>
>> Besides, the http/2 is efficient, but http/1 is simple. So I see no
>> chance that the http/2 will replace http/1 totally in the production
>> inner environment. Will call the inner API by both http/1 and http/2.
>>
>> So I think support http/1 and http/2 on the plain tcp port will simplify
>> both the production and development environment.
>>
>>
>>> What if the received data is bigger than h2mcf->recv_buffer?
>>
>> Yes, it is a problem. However, it can be fixed easily.
>>
>> As we know, the http/2 preface is PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n.
>> We could modify the ngx_http_parse_request_line and when we got A PRI
>> method, we need to get a single * uri. If we got things other than *,
>> we just return a invalid request response. By this, we will never got
>> a PRI to_much_long_uri HTTP/2.0 request line, and the buffer will not
>> be exhausted. So the ngx_http_alloc_large_header_buffer will not be called
>> during the handshake. After the ngx_http_parse_request_line, we will
>> ensure we got a PRI request, and the buffer size is client_header_buffer_size.
>>
>> And then we should the compare the r->header_in buffer's length and the
>> h2mcf->recv_buffer_size. If we got a buffered data larger than the
>> h2mcf->recv_buffer_size, we should send a bad request response.
>>
>> If this feature can be accepted, I will send a new patch.
>>
>> Thanks.
>>
>>
>>> On Mar 2, 2018, at 15:53, Haitao Lv <i at lvht.net> wrote:
>>>
>>> # HG changeset patch
>>> # User 吕海涛 <i at lvht.net>
>>> # Date 1519976498 -28800
>>> # Fri Mar 02 15:41:38 2018 +0800
>>> # Node ID 200955343460c4726015180f20c03e31c0b35ff6
>>> # Parent 81fae70d6cb81c67607931ec3ecc585a609c97e0
>>> make http2 server support http1
>>>
>>> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http.c
>>> --- a/src/http/ngx_http.c Thu Mar 01 20:25:50 2018 +0300
>>> +++ b/src/http/ngx_http.c Fri Mar 02 15:41:38 2018 +0800
>>> @@ -1197,6 +1197,7 @@
>>> ngx_uint_t ssl;
>>> #endif
>>> #if (NGX_HTTP_V2)
>>> + ngx_uint_t http1;
>>> ngx_uint_t http2;
>>> #endif
>>>
>>> @@ -1232,6 +1233,7 @@
>>> ssl = lsopt->ssl || addr[i].opt.ssl;
>>> #endif
>>> #if (NGX_HTTP_V2)
>>> + http1 = lsopt->http1 || addr[i].opt.http1;
>>> http2 = lsopt->http2 || addr[i].opt.http2;
>>> #endif
>>>
>>> @@ -1266,6 +1268,7 @@
>>> addr[i].opt.ssl = ssl;
>>> #endif
>>> #if (NGX_HTTP_V2)
>>> + addr[i].opt.http1 = http1;
>>> addr[i].opt.http2 = http2;
>>> #endif
>>>
>>> @@ -1802,6 +1805,7 @@
>>> addrs[i].conf.ssl = addr[i].opt.ssl;
>>> #endif
>>> #if (NGX_HTTP_V2)
>>> + addrs[i].conf.http1 = addr[i].opt.http1;
>>> addrs[i].conf.http2 = addr[i].opt.http2;
>>> #endif
>>> addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
>>> @@ -1867,6 +1871,7 @@
>>> addrs6[i].conf.ssl = addr[i].opt.ssl;
>>> #endif
>>> #if (NGX_HTTP_V2)
>>> + addrs6[i].conf.http1 = addr[i].opt.http1;
>>> addrs6[i].conf.http2 = addr[i].opt.http2;
>>> #endif
>>> addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
>>> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_core_module.c
>>> --- a/src/http/ngx_http_core_module.c Thu Mar 01 20:25:50 2018 +0300
>>> +++ b/src/http/ngx_http_core_module.c Fri Mar 02 15:41:38 2018 +0800
>>> @@ -3985,6 +3985,18 @@
>>> #endif
>>> }
>>>
>>> + if (ngx_strcmp(value[n].data, "http1") == 0) {
>>> +#if (NGX_HTTP_V2)
>>> + lsopt.http1 = 1;
>>> + continue;
>>> +#else
>>> + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
>>> + "the \"http1\" parameter requires "
>>> + "ngx_http_v2_module");
>>> + return NGX_CONF_ERROR;
>>> +#endif
>>> + }
>>> +
>>> if (ngx_strcmp(value[n].data, "spdy") == 0) {
>>> ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
>>> "invalid parameter \"spdy\": "
>>> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_core_module.h
>>> --- a/src/http/ngx_http_core_module.h Thu Mar 01 20:25:50 2018 +0300
>>> +++ b/src/http/ngx_http_core_module.h Fri Mar 02 15:41:38 2018 +0800
>>> @@ -73,6 +73,7 @@
>>> unsigned bind:1;
>>> unsigned wildcard:1;
>>> unsigned ssl:1;
>>> + unsigned http1:1;
>>> unsigned http2:1;
>>> #if (NGX_HAVE_INET6)
>>> unsigned ipv6only:1;
>>> @@ -234,6 +235,7 @@
>>> ngx_http_virtual_names_t *virtual_names;
>>>
>>> unsigned ssl:1;
>>> + unsigned http1:1;
>>> unsigned http2:1;
>>> unsigned proxy_protocol:1;
>>> };
>>> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_parse.c
>>> --- a/src/http/ngx_http_parse.c Thu Mar 01 20:25:50 2018 +0300
>>> +++ b/src/http/ngx_http_parse.c Fri Mar 02 15:41:38 2018 +0800
>>> @@ -285,6 +285,14 @@
>>> break;
>>> }
>>>
>>> +#if (NGX_HTTP_V2)
>>> + if (ch == '*') {
>>> + r->uri_start = p;
>>> + state = sw_uri;
>>> + break;
>>> + }
>>> +#endif
>>> +
>>> c = (u_char) (ch | 0x20);
>>> if (c >= 'a' && c <= 'z') {
>>> r->schema_start = p;
>>> @@ -724,9 +732,11 @@
>>>
>>> r->http_major = ch - '0';
>>>
>>> +#if !(NGX_HTTP_V2)
>>> if (r->http_major > 1) {
>>> return NGX_HTTP_PARSE_INVALID_VERSION;
>>> }
>>> +#endif
>>>
>>> state = sw_major_digit;
>>> break;
>>> @@ -744,9 +754,11 @@
>>>
>>> r->http_major = r->http_major * 10 + (ch - '0');
>>>
>>> +#if !(NGX_HTTP_V2)
>>> if (r->http_major > 1) {
>>> return NGX_HTTP_PARSE_INVALID_VERSION;
>>> }
>>> +#endif
>>>
>>> break;
>>>
>>> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_request.c
>>> --- a/src/http/ngx_http_request.c Thu Mar 01 20:25:50 2018 +0300
>>> +++ b/src/http/ngx_http_request.c Fri Mar 02 15:41:38 2018 +0800
>>> @@ -17,6 +17,10 @@
>>> static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
>>> ngx_uint_t request_line);
>>>
>>> +#if (NGX_HTTP_V2)
>>> +static void ngx_http_process_h2_preface(ngx_event_t *rev);
>>> +#endif
>>> +
>>> static ngx_int_t ngx_http_process_header_line(ngx_http_request_t *r,
>>> ngx_table_elt_t *h, ngx_uint_t offset);
>>> static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r,
>>> @@ -320,7 +324,7 @@
>>> c->write->handler = ngx_http_empty_handler;
>>>
>>> #if (NGX_HTTP_V2)
>>> - if (hc->addr_conf->http2) {
>>> + if (hc->addr_conf->http2 && !hc->addr_conf->http1) {
>>> rev->handler = ngx_http_v2_init;
>>> }
>>> #endif
>>> @@ -1033,6 +1037,20 @@
>>> return;
>>> }
>>>
>>> +#if (NGX_HTTP_V2)
>>> + if (r->http_connection->addr_conf->http2
>>> + && r->http_version >= NGX_HTTP_VERSION_20) {
>>> +
>>> + c->log->action = "reading client h2 preface";
>>> +
>>> + rev->handler = ngx_http_process_h2_preface;
>>> + ngx_http_process_h2_preface(rev);
>>> + return;
>>> + } else if (r->http_version >= NGX_HTTP_VERSION_20) {
>>> + ngx_http_finalize_request(r, NGX_HTTP_VERSION_NOT_SUPPORTED);
>>> + return;
>>> + }
>>> +#endif
>>>
>>> if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
>>> sizeof(ngx_table_elt_t))
>>> @@ -1208,6 +1226,64 @@
>>> return NGX_OK;
>>> }
>>>
>>> +#if (NGX_HTTP_V2)
>>> +static void
>>> +ngx_http_process_h2_preface(ngx_event_t *rev)
>>> +{
>>> + size_t len;
>>> + ssize_t n;
>>> + ngx_connection_t *c;
>>> + ngx_http_request_t *r;
>>> +
>>> + c = rev->data;
>>> + r = c->data;
>>> +
>>> + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
>>> + "http process h2 preface");
>>> +
>>> + if (rev->timedout) {
>>> + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
>>> + c->timedout = 1;
>>> + ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
>>> + return;
>>> + }
>>> +
>>> + n = ngx_http_read_request_header(r);
>>> +
>>> + if (n == NGX_AGAIN || n == NGX_ERROR) {
>>> + return;
>>> + }
>>> +
>>> + len = r->header_in->last - r->header_in->start;
>>> +
>>> + if (len < sizeof("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") - 1) {
>>> + r->header_in->pos = r->header_in->last;
>>> + return;
>>> + }
>>> +
>>> + if (ngx_strncmp(r->header_in->start,
>>> + "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24) == 0) {
>>> +
>>> + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
>>> + "h2 preface done");
>>> +
>>> + r->header_in->pos = r->header_in->start + 24;
>>> +
>>> + c->data = r->http_connection;
>>> +
>>> + r->http_connection = NULL;
>>> +
>>> + ngx_http_v2_init_after_preface(rev, r->header_in);
>>> + return;
>>> + }
>>> +
>>> + ngx_log_error(NGX_LOG_INFO, c->log, 0,
>>> + "client sent invalid h2 preface");
>>> +
>>> + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
>>> +}
>>> +#endif
>>> +
>>>
>>> static void
>>> ngx_http_process_request_headers(ngx_event_t *rev)
>>> @@ -1809,7 +1885,7 @@
>>> return NGX_ERROR;
>>> }
>>>
>>> - if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) {
>>> + if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_11) {
>>> ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
>>> "client sent HTTP/1.1 request without \"Host\" header");
>>> ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
>>> diff -r 81fae70d6cb8 -r 200955343460 src/http/v2/ngx_http_v2.c
>>> --- a/src/http/v2/ngx_http_v2.c Thu Mar 01 20:25:50 2018 +0300
>>> +++ b/src/http/v2/ngx_http_v2.c Fri Mar 02 15:41:38 2018 +0800
>>> @@ -231,6 +231,13 @@
>>> void
>>> ngx_http_v2_init(ngx_event_t *rev)
>>> {
>>> + ngx_http_v2_init_after_preface(rev, NULL);
>>> +}
>>> +
>>> +
>>> +void
>>> +ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf)
>>> +{
>>> ngx_connection_t *c;
>>> ngx_pool_cleanup_t *cln;
>>> ngx_http_connection_t *hc;
>>> @@ -316,6 +323,12 @@
>>> h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol
>>> : ngx_http_v2_state_preface;
>>>
>>> + if (buf != NULL) {
>>> + ngx_memcpy(h2mcf->recv_buffer, buf->pos, buf->last - buf->pos);
>>> + h2c->state.buffer_used = buf->last - buf->pos;
>>> + h2c->state.handler = ngx_http_v2_state_head;
>>> + }
>>> +
>>> ngx_queue_init(&h2c->waiting);
>>> ngx_queue_init(&h2c->dependencies);
>>> ngx_queue_init(&h2c->closed);
>>> @@ -381,13 +394,18 @@
>>> h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx,
>>> ngx_http_v2_module);
>>>
>>> - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE;
>>> + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE;
>>>
>>> do {
>>> p = h2mcf->recv_buffer;
>>>
>>> + end = p + h2c->state.buffer_used;
>>> +
>>> + if (h2c->state.buffer_used) {
>>> + goto do_state_handler;
>>> + }
>>> +
>>> ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE);
>>> - end = p + h2c->state.buffer_used;
>>>
>>> n = c->recv(c, end, available);
>>>
>>> @@ -410,6 +428,8 @@
>>>
>>> end += n;
>>>
>>> +do_state_handler:
>>> +
>>> h2c->state.buffer_used = 0;
>>> h2c->state.incomplete = 0;
>>>
>>> diff -r 81fae70d6cb8 -r 200955343460 src/http/v2/ngx_http_v2.h
>>> --- a/src/http/v2/ngx_http_v2.h Thu Mar 01 20:25:50 2018 +0300
>>> +++ b/src/http/v2/ngx_http_v2.h Fri Mar 02 15:41:38 2018 +0800
>>> @@ -279,6 +279,7 @@
>>>
>>>
>>> void ngx_http_v2_init(ngx_event_t *rev);
>>> +void ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf);
>>>
>>> ngx_int_t ngx_http_v2_read_request_body(ngx_http_request_t *r);
>>> ngx_int_t ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r);
>>>
>>
>
>
>
> _______________________________________________
> nginx-devel mailing list
> nginx-devel at nginx.org
> http://mailman.nginx.org/mailman/listinfo/nginx-devel
More information about the nginx-devel
mailing list