From b29a5848270115ba7927d3081b6fb54de3e41a8d Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 23 Nov 2015 19:08:19 +0000 Subject: [PATCH] sending trailers out, when seeing EOR bucket and r->trailers_out is non-empty git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1715919 13f79535-47bb-0310-9956-ffa450edef68 --- modules/http2/h2_from_h1.c | 35 +++++++++++++++++++++++++++++ modules/http2/h2_from_h1.h | 2 ++ modules/http2/h2_h2.c | 5 +++-- modules/http2/h2_io.c | 29 +++++++++++++++++------- modules/http2/h2_io.h | 5 +++-- modules/http2/h2_mplx.c | 29 +++++++++++++++++------- modules/http2/h2_mplx.h | 14 +++++++----- modules/http2/h2_push.c | 4 ++-- modules/http2/h2_response.c | 23 ++++++++++++------- modules/http2/h2_response.h | 13 +++++++++-- modules/http2/h2_session.c | 8 +++++-- modules/http2/h2_stream.c | 41 ++++++++++++++++++++++++++-------- modules/http2/h2_task.c | 4 +++- modules/http2/h2_task_output.c | 20 +++++++++++++++-- modules/http2/h2_task_output.h | 1 + 15 files changed, 182 insertions(+), 51 deletions(-) diff --git a/modules/http2/h2_from_h1.c b/modules/http2/h2_from_h1.c index aa900b98be..db0c80db05 100644 --- a/modules/http2/h2_from_h1.c +++ b/modules/http2/h2_from_h1.c @@ -555,3 +555,38 @@ apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) } return ap_pass_brigade(f->next, bb); } + +apr_status_t h2_response_trailers_filter(ap_filter_t *f, apr_bucket_brigade *bb) +{ + h2_task *task = f->ctx; + h2_from_h1 *from_h1 = task->output? task->output->from_h1 : NULL; + request_rec *r = f->r; + apr_bucket *b; + + if (from_h1 && from_h1->response) { + /* Detect the EOR bucket and forward any trailers that may have + * been set to our h2_response. + */ + for (b = APR_BRIGADE_FIRST(bb); + b != APR_BRIGADE_SENTINEL(bb); + b = APR_BUCKET_NEXT(b)) + { + if (AP_BUCKET_IS_EOR(b)) { + /* FIXME: need a better test case than this. + apr_table_setn(r->trailers_out, "X", "1"); */ + if (r->trailers_out && !apr_is_empty_table(r->trailers_out)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, + "h2_from_h1(%d): trailers filter, saving trailers", + from_h1->stream_id); + h2_response_set_trailers(from_h1->response, + apr_table_clone(from_h1->pool, + r->trailers_out)); + } + break; + } + } + } + + return ap_pass_brigade(f->next, bb); +} + diff --git a/modules/http2/h2_from_h1.h b/modules/http2/h2_from_h1.h index 4f5ebad618..cdd38ca605 100644 --- a/modules/http2/h2_from_h1.h +++ b/modules/http2/h2_from_h1.h @@ -69,4 +69,6 @@ struct h2_response *h2_from_h1_get_response(h2_from_h1 *from_h1); apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb); +apr_status_t h2_response_trailers_filter(ap_filter_t *f, apr_bucket_brigade *bb); + #endif /* defined(__mod_h2__h2_from_h1__) */ diff --git a/modules/http2/h2_h2.c b/modules/http2/h2_h2.c index 54fe9e0fa0..e48e64e8a4 100644 --- a/modules/http2/h2_h2.c +++ b/modules/http2/h2_h2.c @@ -667,16 +667,17 @@ static int h2_h2_post_read_req(request_rec *r) ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "adding h1_to_h2_resp output filter"); if (task->serialize_headers) { - ap_remove_output_filter_byhandle(r->output_filters, "H1_TO_H2_RESP"); +/* ap_remove_output_filter_byhandle(r->output_filters, "H1_TO_H2_RESP");*/ ap_add_output_filter("H1_TO_H2_RESP", task, r, r->connection); } else { /* replace the core http filter that formats response headers * in HTTP/1 with our own that collects status and headers */ ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER"); - ap_remove_output_filter_byhandle(r->output_filters, "H2_RESPONSE"); +/* ap_remove_output_filter_byhandle(r->output_filters, "H2_RESPONSE");*/ ap_add_output_filter("H2_RESPONSE", task, r, r->connection); } + ap_add_output_filter("H2_TRAILERS", task, r, r->connection); } return DECLINED; } diff --git a/modules/http2/h2_io.c b/modules/http2/h2_io.c index b33faee1f3..a1d9eff317 100644 --- a/modules/http2/h2_io.c +++ b/modules/http2/h2_io.c @@ -205,8 +205,17 @@ apr_status_t h2_io_out_read_to(h2_io *io, apr_bucket_brigade *bb, return h2_util_move(bb, io->bbout, *plen, NULL, "h2_io_read_to"); } +static void process_trailers(h2_io *io, apr_table_t *trailers) +{ + if (trailers && io->response) { + h2_response_set_trailers(io->response, + apr_table_clone(io->pool, trailers)); + } +} + apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, - apr_size_t maxlen, int *pfile_handles_allowed) + apr_size_t maxlen, apr_table_t *trailers, + int *pfile_handles_allowed) { apr_status_t status; int start_allowed; @@ -233,6 +242,7 @@ apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, return status; } + process_trailers(io, trailers); if (!io->bbout) { io->bbout = apr_brigade_create(io->pool, io->bucket_alloc); } @@ -258,17 +268,20 @@ apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, } -apr_status_t h2_io_out_close(h2_io *io) +apr_status_t h2_io_out_close(h2_io *io, apr_table_t *trailers) { if (io->rst_error) { return APR_ECONNABORTED; } - if (!io->bbout) { - io->bbout = apr_brigade_create(io->pool, io->bucket_alloc); - } - if (!io->eos_out && !h2_util_has_eos(io->bbout, -1)) { - APR_BRIGADE_INSERT_TAIL(io->bbout, - apr_bucket_eos_create(io->bbout->bucket_alloc)); + if (!io->eos_out) { /* EOS has not been read yet */ + process_trailers(io, trailers); + if (!io->bbout) { + io->bbout = apr_brigade_create(io->pool, io->bucket_alloc); + } + if (!h2_util_has_eos(io->bbout, -1)) { + APR_BRIGADE_INSERT_TAIL(io->bbout, + apr_bucket_eos_create(io->bbout->bucket_alloc)); + } } return APR_SUCCESS; } diff --git a/modules/http2/h2_io.h b/modules/http2/h2_io.h index 1fd1167747..dcf493539b 100644 --- a/modules/http2/h2_io.h +++ b/modules/http2/h2_io.h @@ -130,13 +130,14 @@ apr_status_t h2_io_out_read_to(h2_io *io, apr_off_t *plen, int *peos); apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, - apr_size_t maxlen, int *pfile_buckets_allowed); + apr_size_t maxlen, apr_table_t *trailers, + int *pfile_buckets_allowed); /** * Closes the input. After existing data has been read, APR_EOF will * be returned. */ -apr_status_t h2_io_out_close(h2_io *io); +apr_status_t h2_io_out_close(h2_io *io, apr_table_t *trailers); /** * Gives the overall length of the data that is currently queued for diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c index 1257ec79a3..3890a5a064 100644 --- a/modules/http2/h2_mplx.c +++ b/modules/http2/h2_mplx.c @@ -450,7 +450,8 @@ apr_status_t h2_mplx_in_update_windows(h2_mplx *m, apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, h2_io_data_cb *cb, void *ctx, - apr_off_t *plen, int *peos) + apr_off_t *plen, int *peos, + apr_table_t **ptrailers) { apr_status_t status; AP_DEBUG_ASSERT(m); @@ -464,7 +465,6 @@ apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_pre"); status = h2_io_out_readx(io, cb, ctx, plen, peos); - H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_post"); if (status == APR_SUCCESS && cb && io->output_drained) { apr_thread_cond_signal(io->output_drained); @@ -473,6 +473,8 @@ apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, else { status = APR_ECONNABORTED; } + + *ptrailers = (*peos && io->response)? io->response->trailers : NULL; apr_thread_mutex_unlock(m->lock); } return status; @@ -480,7 +482,8 @@ apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id, apr_bucket_brigade *bb, - apr_off_t *plen, int *peos) + apr_off_t *plen, int *peos, + apr_table_t **ptrailers) { apr_status_t status; AP_DEBUG_ASSERT(m); @@ -503,6 +506,7 @@ apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id, else { status = APR_ECONNABORTED; } + *ptrailers = (*peos && io->response)? io->response->trailers : NULL; apr_thread_mutex_unlock(m->lock); } return status; @@ -563,6 +567,7 @@ h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_stream_set *streams) static apr_status_t out_write(h2_mplx *m, h2_io *io, ap_filter_t* f, apr_bucket_brigade *bb, + apr_table_t *trailers, struct apr_thread_cond_t *iowait) { apr_status_t status = APR_SUCCESS; @@ -575,7 +580,7 @@ static apr_status_t out_write(h2_mplx *m, h2_io *io, && (status == APR_SUCCESS) && !is_aborted(m, &status)) { - status = h2_io_out_write(io, bb, m->stream_max_mem, + status = h2_io_out_write(io, bb, m->stream_max_mem, trailers, &m->file_handles_allowed); /* Wait for data to drain until there is room again */ while (!APR_BRIGADE_EMPTY(bb) @@ -583,6 +588,7 @@ static apr_status_t out_write(h2_mplx *m, h2_io *io, && status == APR_SUCCESS && (m->stream_max_mem <= h2_io_out_length(io)) && !is_aborted(m, &status)) { + trailers = NULL; io->output_drained = iowait; if (f) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, @@ -616,7 +622,7 @@ static apr_status_t out_open(h2_mplx *m, int stream_id, h2_response *response, h2_io_set_response(io, response); h2_io_set_add(m->ready_ios, io); if (bb) { - status = out_write(m, io, f, bb, iowait); + status = out_write(m, io, f, bb, response->trailers, iowait); } have_out_data_for(m, stream_id); } @@ -652,6 +658,7 @@ apr_status_t h2_mplx_out_open(h2_mplx *m, int stream_id, h2_response *response, apr_status_t h2_mplx_out_write(h2_mplx *m, int stream_id, ap_filter_t* f, apr_bucket_brigade *bb, + apr_table_t *trailers, struct apr_thread_cond_t *iowait) { apr_status_t status; @@ -664,7 +671,10 @@ apr_status_t h2_mplx_out_write(h2_mplx *m, int stream_id, if (!m->aborted) { h2_io *io = h2_io_set_get(m->stream_ios, stream_id); if (io && !io->orphaned) { - status = out_write(m, io, f, bb, iowait); + status = out_write(m, io, f, bb, trailers, iowait); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c, + "h2_mplx(%ld-%d): write with trailers=%s", + m->id, io->id, trailers? "yes" : "no"); H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_write"); have_out_data_for(m, stream_id); @@ -684,7 +694,7 @@ apr_status_t h2_mplx_out_write(h2_mplx *m, int stream_id, return status; } -apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id) +apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id, apr_table_t *trailers) { apr_status_t status; AP_DEBUG_ASSERT(m); @@ -708,7 +718,10 @@ apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id) "h2_mplx(%ld-%d): close, no response, no rst", m->id, io->id); } - status = h2_io_out_close(io); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c, + "h2_mplx(%ld-%d): close with trailers=%s", + m->id, io->id, trailers? "yes" : "no"); + status = h2_io_out_close(io, trailers); H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_close"); have_out_data_for(m, stream_id); diff --git a/modules/http2/h2_mplx.h b/modules/http2/h2_mplx.h index 5c950b9c27..c570e91fd4 100644 --- a/modules/http2/h2_mplx.h +++ b/modules/http2/h2_mplx.h @@ -240,7 +240,8 @@ struct h2_stream *h2_mplx_next_submit(h2_mplx *m, */ apr_status_t h2_mplx_out_readx(h2_mplx *mplx, int stream_id, h2_io_data_cb *cb, void *ctx, - apr_off_t *plen, int *peos); + apr_off_t *plen, int *peos, + apr_table_t **ptrailers); /** * Reads output data into the given brigade. Will never block, but @@ -248,7 +249,8 @@ apr_status_t h2_mplx_out_readx(h2_mplx *mplx, int stream_id, */ apr_status_t h2_mplx_out_read_to(h2_mplx *mplx, int stream_id, apr_bucket_brigade *bb, - apr_off_t *plen, int *peos); + apr_off_t *plen, int *peos, + apr_table_t **ptrailers); /** * Opens the output for the given stream with the specified response. @@ -264,17 +266,19 @@ apr_status_t h2_mplx_out_open(h2_mplx *mplx, int stream_id, * @param stream_id the stream identifier * @param filter the apache filter context of the data * @param bb the bucket brigade to append + * @param trailers optional trailers for response, maybe NULL * @param iowait a conditional used for block/signalling in h2_mplx */ apr_status_t h2_mplx_out_write(h2_mplx *mplx, int stream_id, ap_filter_t* filter, apr_bucket_brigade *bb, + apr_table_t *trailers, struct apr_thread_cond_t *iowait); /** - * Closes the output stream. Readers of this stream will get all pending - * data and then only APR_EOF as result. + * Closes the output for stream stream_id. Optionally forwards trailers + * fromt the processed stream. */ -apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id); +apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id, apr_table_t *trailers); apr_status_t h2_mplx_out_rst(h2_mplx *m, int stream_id, int error); diff --git a/modules/http2/h2_push.c b/modules/http2/h2_push.c index 703ea761a0..dd972cf8e2 100644 --- a/modules/http2/h2_push.c +++ b/modules/http2/h2_push.c @@ -386,14 +386,14 @@ apr_array_header_t *h2_push_collect(apr_pool_t *p, const h2_request *req, * TODO: This may be extended in the future by hooks or callbacks * where other modules can provide push information directly. */ - if (res->header) { + if (res->headers) { link_ctx ctx; memset(&ctx, 0, sizeof(ctx)); ctx.req = req; ctx.pool = p; - apr_table_do(head_iter, &ctx, res->header, NULL); + apr_table_do(head_iter, &ctx, res->headers, NULL); return ctx.pushes; } return NULL; diff --git a/modules/http2/h2_response.c b/modules/http2/h2_response.c index 2751f2d377..505f2451d6 100644 --- a/modules/http2/h2_response.c +++ b/modules/http2/h2_response.c @@ -36,7 +36,7 @@ h2_response *h2_response_create(int stream_id, apr_array_header_t *hlines, apr_pool_t *pool) { - apr_table_t *header; + apr_table_t *headers; h2_response *response = apr_pcalloc(pool, sizeof(h2_response)); int i; if (response == NULL) { @@ -49,7 +49,7 @@ h2_response *h2_response_create(int stream_id, response->content_length = -1; if (hlines) { - header = apr_table_make(pool, hlines->nelts); + headers = apr_table_make(pool, hlines->nelts); for (i = 0; i < hlines->nelts; ++i) { char *hline = ((char **)hlines->elts)[i]; char *sep = ap_strchr(hline, ':'); @@ -66,7 +66,7 @@ h2_response *h2_response_create(int stream_id, } if (!h2_util_ignore_header(hline)) { - apr_table_merge(header, hline, sep); + apr_table_merge(headers, hline, sep); if (*sep && H2_HD_MATCH_LIT_CS("content-length", hline)) { char *end; response->content_length = apr_strtoi64(sep, &end, 10); @@ -83,10 +83,10 @@ h2_response *h2_response_create(int stream_id, } } else { - header = apr_table_make(pool, 0); + headers = apr_table_make(pool, 0); } - response->header = header; + response->headers = headers; return response; } @@ -101,7 +101,7 @@ h2_response *h2_response_rcreate(int stream_id, request_rec *r, response->stream_id = stream_id; response->http_status = r->status; response->content_length = -1; - response->header = header; + response->headers = header; if (response->http_status == HTTP_FORBIDDEN) { const char *cause = apr_table_get(r->notes, "ssl-renegotiate-forbidden"); @@ -130,10 +130,17 @@ h2_response *h2_response_copy(apr_pool_t *pool, h2_response *from) to->stream_id = from->stream_id; to->http_status = from->http_status; to->content_length = from->content_length; - if (from->header) { - to->header = apr_table_clone(pool, from->header); + if (from->headers) { + to->headers = apr_table_clone(pool, from->headers); + } + if (from->trailers) { + to->trailers = apr_table_clone(pool, from->trailers); } return to; } +void h2_response_set_trailers(h2_response *response, apr_table_t *trailers) +{ + response->trailers = trailers; +} diff --git a/modules/http2/h2_response.h b/modules/http2/h2_response.h index 4085a41bdf..518ef2e3c1 100644 --- a/modules/http2/h2_response.h +++ b/modules/http2/h2_response.h @@ -23,8 +23,8 @@ typedef struct h2_response { int rst_error; int http_status; apr_off_t content_length; - apr_table_t *header; - apr_table_t *trailer; + apr_table_t *headers; + apr_table_t *trailers; } h2_response; h2_response *h2_response_create(int stream_id, @@ -40,4 +40,13 @@ void h2_response_destroy(h2_response *response); h2_response *h2_response_copy(apr_pool_t *pool, h2_response *from); +/** + * Set the trailers in the reponse. Will replace any existing trailers. Will + * *not* clone the table. + * + * @param response the repsone to set the trailers for + * @param trailers the trailers to set + */ +void h2_response_set_trailers(h2_response *response, apr_table_t *trailers); + #endif /* defined(__mod_h2__h2_response__) */ diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c index a98224be27..720a1eb30d 100644 --- a/modules/http2/h2_session.c +++ b/modules/http2/h2_session.c @@ -1124,10 +1124,14 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s, int rv; nh = h2_util_ngheader_make(stream->pool, trailers); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_stream(%ld-%d): submit %d trailers", + session->id, (int)stream_id,(int) nh->nvlen); rv = nghttp2_submit_trailer(ng2s, stream->id, nh->nv, nh->nvlen); if (rv < 0) { nread = rv; } + *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; } *data_flags |= NGHTTP2_DATA_FLAG_EOF; @@ -1158,7 +1162,7 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream) if (stream->submitted) { rv = NGHTTP2_PROTOCOL_ERROR; } - else if (stream->response && stream->response->header) { + else if (stream->response && stream->response->headers) { nghttp2_data_provider provider; h2_response *response = stream->response; h2_ngheader *ngh; @@ -1172,7 +1176,7 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream) session->id, stream->id, response->http_status); ngh = h2_util_ngheader_make_res(stream->pool, response->http_status, - response->header); + response->headers); rv = nghttp2_submit_response(session->ngh2, response->stream_id, ngh->nv, ngh->nvlen, &provider); diff --git a/modules/http2/h2_stream.c b/modules/http2/h2_stream.c index ad7f5df102..a091e4258b 100644 --- a/modules/http2/h2_stream.c +++ b/modules/http2/h2_stream.c @@ -223,7 +223,7 @@ apr_status_t h2_stream_set_response(h2_stream *stream, h2_response *response, * as we want, since the lifetimes are the same and we are not freeing * the ones in h2_mplx->io before this stream is done. */ H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream set_response_pre"); - status = h2_util_move(stream->bbout, bb, -1, &move_all, + status = h2_util_move(stream->bbout, bb, 16 * 1024, &move_all, "h2_stream_set_response"); H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream set_response_post"); } @@ -476,6 +476,7 @@ apr_status_t h2_stream_prep_read(h2_stream *stream, { apr_status_t status = APR_SUCCESS; const char *src; + apr_table_t *trailers = NULL; int test_read = (*plen == 0); if (stream->rst_error) { @@ -490,19 +491,26 @@ apr_status_t h2_stream_prep_read(h2_stream *stream, apr_brigade_cleanup(stream->bbout); return h2_stream_prep_read(stream, plen, peos); } + trailers = stream->response? stream->response->trailers : NULL; } else { src = "mplx"; status = h2_mplx_out_readx(stream->session->mplx, stream->id, - NULL, NULL, plen, peos); + NULL, NULL, plen, peos, &trailers); + if (trailers && stream->response) { + h2_response_set_trailers(stream->response, trailers); + } } + if (!test_read && status == APR_SUCCESS && !*peos && !*plen) { status = APR_EAGAIN; } + H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream prep_read_post"); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c, - "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d", - stream->session->id, stream->id, src, (long)*plen, *peos); + "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d, trailers=%s", + stream->session->id, stream->id, src, (long)*plen, *peos, + trailers? "yes" : "no"); return status; } @@ -511,6 +519,7 @@ apr_status_t h2_stream_readx(h2_stream *stream, apr_off_t *plen, int *peos) { apr_status_t status = APR_SUCCESS; + apr_table_t *trailers = NULL; const char *src; H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream readx_pre"); @@ -532,14 +541,21 @@ apr_status_t h2_stream_readx(h2_stream *stream, else { src = "mplx"; status = h2_mplx_out_readx(stream->session->mplx, stream->id, - cb, ctx, plen, peos); + cb, ctx, plen, peos, &trailers); + } + + if (trailers && stream->response) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c, + "h2_stream(%ld-%d): readx, saving trailers", + stream->session->id, stream->id); + h2_response_set_trailers(stream->response, trailers); } if (status == APR_SUCCESS && !*peos && !*plen) { status = APR_EAGAIN; } - H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream prep_readx_post"); + H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream readx_post"); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c, "h2_stream(%ld-%d): readx %s, len=%ld eos=%d", stream->session->id, stream->id, src, (long)*plen, *peos); @@ -552,6 +568,7 @@ apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb, apr_off_t *plen, int *peos) { apr_status_t status = APR_SUCCESS; + apr_table_t *trailers = NULL; H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream read_to_pre"); if (stream->rst_error) { @@ -562,7 +579,7 @@ apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb, apr_off_t tlen = *plen; int eos; status = h2_mplx_out_read_to(stream->session->mplx, stream->id, - stream->bbout, &tlen, &eos); + stream->bbout, &tlen, &eos, &trailers); } if (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(stream->bbout)) { @@ -574,6 +591,13 @@ apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb, *peos = 0; } + if (trailers && stream->response) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c, + "h2_stream(%ld-%d): read_to, saving trailers", + stream->session->id, stream->id); + h2_response_set_trailers(stream->response, trailers); + } + if (status == APR_SUCCESS && !*peos && !*plen) { status = APR_EAGAIN; } @@ -642,6 +666,5 @@ apr_status_t h2_stream_submit_pushes(h2_stream *stream) apr_table_t *h2_stream_get_trailers(h2_stream *stream) { - /* TODO */ - return NULL; + return stream->response? stream->response->trailers : NULL; } diff --git a/modules/http2/h2_task.c b/modules/http2/h2_task.c index b529db199c..3702fa88f2 100644 --- a/modules/http2/h2_task.c +++ b/modules/http2/h2_task.c @@ -113,6 +113,8 @@ void h2_task_register_hooks(void) NULL, AP_FTYPE_NETWORK); ap_register_output_filter("H1_TO_H2_RESP", h2_filter_read_response, NULL, AP_FTYPE_PROTOCOL); + ap_register_output_filter("H2_TRAILERS", h2_response_trailers_filter, + NULL, AP_FTYPE_PROTOCOL); } static int h2_task_pre_conn(conn_rec* c, void *arg) @@ -161,7 +163,7 @@ h2_task *h2_task_create(long session_id, const h2_request *req, ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool, APLOGNO(02941) "h2_task(%ld-%d): create stream task", session_id, req->id); - h2_mplx_out_close(mplx, req->id); + h2_mplx_out_close(mplx, req->id, NULL); return NULL; } diff --git a/modules/http2/h2_task_output.c b/modules/http2/h2_task_output.c index 06a5d7aafb..71fefdec05 100644 --- a/modules/http2/h2_task_output.c +++ b/modules/http2/h2_task_output.c @@ -83,17 +83,31 @@ static apr_status_t open_if_needed(h2_task_output *output, ap_filter_t *f, return APR_ECONNABORTED; } + output->trailers_passed = !!response->trailers; return h2_mplx_out_open(output->task->mplx, output->task->stream_id, response, f, bb, output->task->io); } return APR_EOF; } +static apr_table_t *get_trailers(h2_task_output *output) +{ + if (!output->trailers_passed) { + h2_response *response = h2_from_h1_get_response(output->from_h1); + if (response->trailers) { + output->trailers_passed = 1; + return response->trailers; + } + } + return NULL; +} + void h2_task_output_close(h2_task_output *output) { open_if_needed(output, NULL, NULL); if (output->state != H2_TASK_OUT_DONE) { - h2_mplx_out_close(output->task->mplx, output->task->stream_id); + h2_mplx_out_close(output->task->mplx, output->task->stream_id, + get_trailers(output)); output->state = H2_TASK_OUT_DONE; } } @@ -111,6 +125,7 @@ apr_status_t h2_task_output_write(h2_task_output *output, ap_filter_t* f, apr_bucket_brigade* bb) { apr_status_t status; + if (APR_BRIGADE_EMPTY(bb)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, "h2_task_output(%s): empty write", output->task->id); @@ -124,9 +139,10 @@ apr_status_t h2_task_output_write(h2_task_output *output, output->task->id); return status; } + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, "h2_task_output(%s): write brigade", output->task->id); return h2_mplx_out_write(output->task->mplx, output->task->stream_id, - f, bb, output->task->io); + f, bb, get_trailers(output), output->task->io); } diff --git a/modules/http2/h2_task_output.h b/modules/http2/h2_task_output.h index a326c49096..de03890ed6 100644 --- a/modules/http2/h2_task_output.h +++ b/modules/http2/h2_task_output.h @@ -38,6 +38,7 @@ struct h2_task_output { struct h2_task *task; h2_task_output_state_t state; struct h2_from_h1 *from_h1; + int trailers_passed; }; h2_task_output *h2_task_output_create(struct h2_task *task, apr_pool_t *pool); -- 2.49.0