* that we manipulate filters only once. */
/* our slave connection? */
if (task && !task->filters_set) {
+ ap_filter_t *f;
+
/* setup the correct output filters to process the response
* on the proper mod_http2 way. */
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "adding task output filter");
ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
ap_add_output_filter("H2_RESPONSE", task, r, r->connection);
}
+
+ /* trailers processing. Incoming trailers are added to this
+ * request via our h2 input filter, outgoing trailers
+ * in a special h2 out filter. */
+ for (f = r->input_filters; f; f = f->next) {
+ if (!strcmp("H2_TO_H1", f->frec->name)) {
+ f->r = r;
+ break;
+ }
+ }
ap_add_output_filter("H2_TRAILERS", task, r, r->connection);
task->filters_set = 1;
}
return (status == APR_SUCCESS);
}
-static apr_status_t append_eos(h2_io *io, apr_bucket_brigade *bb)
+static apr_status_t append_eos(h2_io *io, apr_bucket_brigade *bb,
+ apr_table_t *trailers)
{
apr_status_t status = APR_SUCCESS;
+ apr_table_t *t = io->request->trailers;
+
+ if (trailers && t && !apr_is_empty_table(trailers)) {
+ /* trailers passed in, transfer directly. */
+ apr_table_overlap(trailers, t, APR_OVERLAP_TABLES_SET);
+ t = NULL;
+ }
if (io->request->chunked) {
- apr_table_t *trailers = io->request->trailers;
- if (trailers && !apr_is_empty_table(trailers)) {
+ if (t && !apr_is_empty_table(t)) {
+ /* no trailers passed in, transfer via chunked */
status = apr_brigade_puts(bb, NULL, NULL, "0\r\n");
- apr_table_do(add_trailer, bb, trailers, NULL);
+ apr_table_do(add_trailer, bb, t, NULL);
status = apr_brigade_puts(bb, NULL, NULL, "\r\n");
}
else {
}
apr_status_t h2_io_in_read(h2_io *io, apr_bucket_brigade *bb,
- apr_size_t maxlen)
+ apr_size_t maxlen, apr_table_t *trailers)
{
apr_off_t start_len = 0;
apr_status_t status;
if (!io->bbin || APR_BRIGADE_EMPTY(io->bbin)) {
if (io->eos_in) {
if (!io->eos_in_written) {
- status = append_eos(io, bb);
+ status = append_eos(io, bb, trailers);
io->eos_in_written = 1;
return status;
}
* is currently available, APR_EOF if end of input has been reached.
*/
apr_status_t h2_io_in_read(h2_io *io, apr_bucket_brigade *bb,
- apr_size_t maxlen);
+ apr_size_t maxlen, apr_table_t *trailers);
/**
* Appends given bucket to the input.
}
apr_status_t h2_mplx_in_read(h2_mplx *m, apr_read_type_e block,
- int stream_id, apr_bucket_brigade *bb,
+ int stream_id, apr_bucket_brigade *bb,
+ apr_table_t *trailers,
struct apr_thread_cond_t *iowait)
{
apr_status_t status;
if (io && !io->orphaned) {
io->input_arrived = iowait;
H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_read_pre");
- status = h2_io_in_read(io, bb, -1);
+ status = h2_io_in_read(io, bb, -1, trailers);
while (APR_STATUS_IS_EAGAIN(status)
&& !is_aborted(m, &status)
&& block == APR_BLOCK_READ) {
"h2_mplx(%ld-%d): wait on in data (BLOCK_READ)",
m->id, stream_id);
apr_thread_cond_wait(io->input_arrived, m->lock);
- status = h2_io_in_read(io, bb, -1);
+ status = h2_io_in_read(io, bb, -1, trailers);
}
H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_read_post");
io->input_arrived = NULL;
*/
apr_status_t h2_mplx_in_read(h2_mplx *m, apr_read_type_e block,
int stream_id, apr_bucket_brigade *bb,
+ apr_table_t *trailers,
struct apr_thread_cond_t *iowait);
/**
never calling us again. */
status = h2_mplx_in_read(input->task->mplx, APR_BLOCK_READ,
input->task->stream_id, input->bb,
+ f->r? f->r->trailers_in : NULL,
input->task->io);
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
"h2_task_input(%s): mplx in read returned",