Skip to content

Commit

Permalink
HTTP/2: added support for trailers in HTTP responses.
Browse files Browse the repository at this point in the history
Signed-off-by: Piotr Sikora <[email protected]>
  • Loading branch information
PiotrSikora committed Mar 24, 2017
1 parent cfdce50 commit d85f2f9
Showing 1 changed file with 152 additions and 14 deletions.
166 changes: 152 additions & 14 deletions src/http/v2/ngx_http_v2_filter_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,17 @@
#define NGX_HTTP_V2_SERVER_INDEX 54
#define NGX_HTTP_V2_VARY_INDEX 59

#define NGX_HTTP_V2_NO_TRAILERS (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);
static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,
ngx_uint_t value);
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_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(
ngx_http_request_t *r);

static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc,
ngx_chain_t *in, off_t limit);
Expand Down Expand Up @@ -612,7 +616,7 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
header[i].value.len, tmp);
}

frame = ngx_http_v2_create_headers_frame(r, start, pos);
frame = ngx_http_v2_create_headers_frame(r, start, pos, r->header_only);
if (frame == NULL) {
return NGX_ERROR;
}
Expand All @@ -636,6 +640,118 @@ ngx_http_v2_header_filter(ngx_http_request_t *r)
}


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;

len = 0;
tmp_len = 0;

part = &r->headers_out.trailers.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, r->connection->log, 0,
"too long response trailer name: \"%V\"",
&header[i].key);
return NULL;
}

if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
"too long response trailer value: \"%V: %V\"",
&header[i].key, &header[i].value);
return NULL;
}

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_HTTP_V2_NO_TRAILERS;
}

tmp = ngx_palloc(r->pool, tmp_len);
pos = ngx_pnalloc(r->pool, len);

if (pos == NULL || tmp == NULL) {
return NULL;
}

start = pos;

part = &r->headers_out.trailers.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 (r->connection->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, r->connection->log, 0,
"http2 output trailer: \"%*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);
}

return ngx_http_v2_create_headers_frame(r, start, pos, 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)
Expand Down Expand Up @@ -686,7 +802,7 @@ ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)

static ngx_http_v2_out_frame_t *
ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
u_char *end)
u_char *end, ngx_uint_t fin)
{
u_char type, flags;
size_t rest, frame_size;
Expand All @@ -707,12 +823,12 @@ ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
frame->stream = stream;
frame->length = rest;
frame->blocked = 1;
frame->fin = r->header_only;
frame->fin = fin;

ll = &frame->first;

type = NGX_HTTP_V2_HEADERS_FRAME;
flags = r->header_only ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG;
flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG;
frame_size = stream->connection->frame_size;

for ( ;; ) {
Expand Down Expand Up @@ -776,7 +892,7 @@ ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
continue;
}

b->last_buf = r->header_only;
b->last_buf = fin;
cl->next = NULL;
frame->last = cl;

Expand All @@ -798,7 +914,7 @@ ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
ngx_http_request_t *r;
ngx_http_v2_stream_t *stream;
ngx_http_v2_loc_conf_t *h2lcf;
ngx_http_v2_out_frame_t *frame;
ngx_http_v2_out_frame_t *frame, *trailers;
ngx_http_v2_connection_t *h2c;

r = fc->data;
Expand Down Expand Up @@ -872,6 +988,8 @@ ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
frame_size = (h2lcf->chunk_size < h2c->frame_size)
? h2lcf->chunk_size : h2c->frame_size;

trailers = NGX_HTTP_V2_NO_TRAILERS;

#if (NGX_SUPPRESS_WARN)
cl = NULL;
#endif
Expand Down Expand Up @@ -934,19 +1052,39 @@ ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
size -= rest;
}

frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, cl);
if (frame == NULL) {
return NGX_CHAIN_ERROR;
if (cl->buf->last_buf) {
trailers = ngx_http_v2_create_trailers_frame(r);
if (trailers == NULL) {
return NGX_CHAIN_ERROR;
}

if (trailers != NGX_HTTP_V2_NO_TRAILERS) {
cl->buf->last_buf = 0;
}
}

ngx_http_v2_queue_frame(h2c, frame);
if (frame_size || cl->buf->last_buf) {
frame = ngx_http_v2_filter_get_data_frame(stream, frame_size,
out, cl);
if (frame == NULL) {
return NGX_CHAIN_ERROR;
}

ngx_http_v2_queue_frame(h2c, frame);

h2c->send_window -= frame_size;
h2c->send_window -= frame_size;

stream->send_window -= frame_size;
stream->queued++;
stream->send_window -= frame_size;
stream->queued++;
}

if (in == NULL) {

if (trailers != NGX_HTTP_V2_NO_TRAILERS) {
ngx_http_v2_queue_frame(h2c, trailers);
stream->queued++;
}

break;
}

Expand Down

0 comments on commit d85f2f9

Please sign in to comment.