diff options
Diffstat (limited to 'src/http/v2')
-rw-r--r-- | src/http/v2/ngx_http_v2.h | 1 | ||||
-rw-r--r-- | src/http/v2/ngx_http_v2_filter_module.c | 202 |
2 files changed, 200 insertions, 3 deletions
diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h index 6751b3026..9605c8a23 100644 --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -213,6 +213,7 @@ struct ngx_http_v2_stream_s { ngx_pool_t *pool; + unsigned initialized:1; unsigned waiting:1; unsigned blocked:1; unsigned exhausted:1; diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c index b63e343a0..907906a88 100644 --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -27,6 +27,10 @@ #define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 +static ngx_int_t ngx_http_v2_header_filter(ngx_http_request_t *r); +static ngx_int_t ngx_http_v2_early_hints_filter(ngx_http_request_t *r); +static ngx_int_t ngx_http_v2_init_stream(ngx_http_request_t *r); + static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( @@ -95,6 +99,7 @@ ngx_module_t ngx_http_v2_filter_module = { static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_header_filter_pt ngx_http_next_early_hints_filter; static ngx_int_t @@ -107,7 +112,6 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) ngx_list_part_t *part; ngx_table_elt_t *header; ngx_connection_t *fc; - ngx_http_cleanup_t *cln; ngx_http_v2_stream_t *stream; ngx_http_v2_out_frame_t *frame; ngx_http_v2_connection_t *h2c; @@ -612,7 +616,196 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) ngx_http_v2_queue_blocked_frame(h2c, frame); - stream->queued = 1; + stream->queued++; + + if (ngx_http_v2_init_stream(r) != NGX_OK) { + return NGX_ERROR; + } + + return ngx_http_v2_filter_send(fc, stream); +} + + +static ngx_int_t +ngx_http_v2_early_hints_filter(ngx_http_request_t *r) +{ + u_char *pos, *start, *tmp; + size_t len, tmp_len; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_connection_t *fc; + ngx_http_v2_stream_t *stream; + ngx_http_v2_out_frame_t *frame; + ngx_http_v2_connection_t *h2c; + + stream = r->stream; + + if (!stream) { + return ngx_http_next_early_hints_filter(r); + } + + if (r != r->main) { + return NGX_OK; + } + + fc = r->connection; + + if (fc->error) { + return NGX_ERROR; + } + + len = 0; + tmp_len = 0; + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_CRIT, fc->log, 0, + "too long response header name: \"%V\"", + &header[i].key); + return NGX_ERROR; + } + + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_CRIT, fc->log, 0, + "too long response header value: \"%V: %V\"", + &header[i].key, &header[i].value); + return NGX_ERROR; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (header[i].key.len > tmp_len) { + tmp_len = header[i].key.len; + } + + if (header[i].value.len > tmp_len) { + tmp_len = header[i].value.len; + } + } + + if (len == 0) { + return NGX_OK; + } + + h2c = stream->connection; + + len += h2c->table_update ? 1 : 0; + len += 1 + ngx_http_v2_literal_size("418"); + + tmp = ngx_palloc(r->pool, tmp_len); + pos = ngx_pnalloc(r->pool, len); + + if (pos == NULL || tmp == NULL) { + return NGX_ERROR; + } + + start = pos; + + if (h2c->table_update) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 table size update: 0"); + *pos++ = (1 << 5) | 0; + h2c->table_update = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \":status: %03ui\"", + (ngx_uint_t) NGX_HTTP_EARLY_HINTS); + + *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX); + *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3; + pos = ngx_sprintf(pos, "%03ui", (ngx_uint_t) NGX_HTTP_EARLY_HINTS); + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + +#if (NGX_DEBUG) + if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) { + ngx_strlow(tmp, header[i].key.data, header[i].key.len); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"%*s: %V\"", + header[i].key.len, tmp, &header[i].value); + } +#endif + + *pos++ = 0; + + pos = ngx_http_v2_write_name(pos, header[i].key.data, + header[i].key.len, tmp); + + pos = ngx_http_v2_write_value(pos, header[i].value.data, + header[i].value.len, tmp); + } + + frame = ngx_http_v2_create_headers_frame(r, start, pos, 0); + if (frame == NULL) { + return NGX_ERROR; + } + + ngx_http_v2_queue_blocked_frame(h2c, frame); + + stream->queued++; + + if (ngx_http_v2_init_stream(r) != NGX_OK) { + return NGX_ERROR; + } + + return ngx_http_v2_filter_send(fc, stream); +} + + +static ngx_int_t +ngx_http_v2_init_stream(ngx_http_request_t *r) +{ + ngx_connection_t *fc; + ngx_http_cleanup_t *cln; + ngx_http_v2_stream_t *stream; + + stream = r->stream; + fc = r->connection; + + if (stream->initialized) { + return NGX_OK; + } + + stream->initialized = 1; cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { @@ -626,7 +819,7 @@ ngx_http_v2_header_filter(ngx_http_request_t *r) fc->need_last_buf = 1; fc->need_flush_buf = 1; - return ngx_http_v2_filter_send(fc, stream); + return NGX_OK; } @@ -1567,5 +1760,8 @@ ngx_http_v2_filter_init(ngx_conf_t *cf) ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_v2_header_filter; + ngx_http_next_early_hints_filter = ngx_http_top_early_hints_filter; + ngx_http_top_early_hints_filter = ngx_http_v2_early_hints_filter; + return NGX_OK; } |