HTTP/2: emit trailers in HTTP/2 responses without message body.
Previously, trailers were not emitted in HTTP/2 responses that cannot
have message body (responses to HEAD requests, and responses with 204
and 304 status codes).
Change-Id: Iaee5942c93b7d9ea848fd6e9b6eb7b88a874a695
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
Reviewed-on: https://nginx-review.googlesource.com/2804
Reviewed-by: Lizan Zhou <zlizan@google.com>
diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c
index 2432918..d7138ee 100644
--- a/src/http/v2/ngx_http_v2_filter_module.c
+++ b/src/http/v2/ngx_http_v2_filter_module.c
@@ -50,6 +50,8 @@
#define NGX_HTTP_V2_SERVER_INDEX 54
#define NGX_HTTP_V2_VARY_INDEX 59
+#define NGX_HTTP_V2_FRAME_ERROR (ngx_http_v2_out_frame_t *) -1
+
static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,
u_char *tmp, ngx_uint_t lower);
@@ -131,12 +133,12 @@
u_char status, *pos, *start, *p, *tmp;
size_t len, tmp_len;
ngx_str_t host, location;
- ngx_uint_t i, port;
+ ngx_uint_t i, port, fin;
ngx_list_part_t *part;
ngx_table_elt_t *header;
ngx_connection_t *fc;
ngx_http_cleanup_t *cln;
- ngx_http_v2_out_frame_t *frame;
+ ngx_http_v2_out_frame_t *headers, *trailers;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t *cscf;
u_char addr[NGX_SOCKADDR_STRLEN];
@@ -623,13 +625,6 @@
header[i].value.len, tmp);
}
- frame = ngx_http_v2_create_headers_frame(r, start, pos, r->header_only);
- if (frame == NULL) {
- return NGX_ERROR;
- }
-
- ngx_http_v2_queue_blocked_frame(r->stream->connection, frame);
-
cln = ngx_http_cleanup_add(r, 0);
if (cln == NULL) {
return NGX_ERROR;
@@ -638,8 +633,32 @@
cln->handler = ngx_http_v2_filter_cleanup;
cln->data = r->stream;
+ if (r->header_only && r->allow_trailers && r->expect_trailers) {
+ trailers = ngx_http_v2_create_trailers_frame(r);
+ if (trailers == NGX_HTTP_V2_FRAME_ERROR) {
+ return NGX_ERROR;
+ }
+
+ fin = trailers ? 0 : 1;
+
+ } else {
+ trailers = NULL;
+ fin = r->header_only;
+ }
+
+ headers = ngx_http_v2_create_headers_frame(r, start, pos, fin);
+ if (headers == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_v2_queue_blocked_frame(r->stream->connection, headers);
r->stream->queued = 1;
+ if (trailers) {
+ ngx_http_v2_queue_blocked_frame(r->stream->connection, trailers);
+ r->stream->queued++;
+ }
+
fc->send_chain = ngx_http_v2_send_chain;
fc->need_last_buf = 1;
@@ -650,14 +669,15 @@
static ngx_http_v2_out_frame_t *
ngx_http_v2_create_trailers_frame(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;
+ 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_http_v2_out_frame_t *frame;
if (ngx_http_eval_trailers(r) != NGX_OK) {
- return NULL;
+ return NGX_HTTP_V2_FRAME_ERROR;
}
len = 0;
@@ -684,16 +704,18 @@
if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
- "ignoring too long response trailer name: \"%V\"",
+ "too long response trailer name: \"%V\"",
&header[i].key);
- continue;
+
+ return NGX_HTTP_V2_FRAME_ERROR;
}
if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
- "ignoring too long response trailer value: "
- "\"%V: %V\"", &header[i].key, &header[i].value);
- continue;
+ "too long response trailer value: \"%V: %V\"",
+ &header[i].key, &header[i].value);
+
+ return NGX_HTTP_V2_FRAME_ERROR;
}
len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
@@ -716,7 +738,7 @@
pos = ngx_pnalloc(r->pool, len);
if (pos == NULL || tmp == NULL) {
- return NULL;
+ return NGX_HTTP_V2_FRAME_ERROR;
}
start = pos;
@@ -762,7 +784,12 @@
header[i].value.len, tmp);
}
- return ngx_http_v2_create_headers_frame(r, start, pos, 1);
+ frame = ngx_http_v2_create_headers_frame(r, start, pos, 1);
+ if (frame == NULL) {
+ return NGX_HTTP_V2_FRAME_ERROR;
+ }
+
+ return frame;
}
@@ -1066,6 +1093,10 @@
if (cl->buf->last_buf && r->allow_trailers && r->expect_trailers) {
trailers = ngx_http_v2_create_trailers_frame(r);
+ if (trailers == NGX_HTTP_V2_FRAME_ERROR) {
+ return NGX_CHAIN_ERROR;
+ }
+
if (trailers) {
cl->buf->last_buf = 0;
}