aboutsummaryrefslogtreecommitdiff
path: root/src/http/v2
diff options
context:
space:
mode:
Diffstat (limited to 'src/http/v2')
-rw-r--r--src/http/v2/ngx_http_v2.h1
-rw-r--r--src/http/v2/ngx_http_v2_filter_module.c202
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;
}