1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 #include <apr_thread_cond.h>
20 #include <http_core.h>
22 #include <http_connection.h>
24 #include "h2_private.h"
27 #include "h2_request.h"
28 #include "h2_session.h"
29 #include "h2_stream.h"
30 #include "h2_from_h1.h"
31 #include "h2_response.h"
32 #include "h2_task_output.h"
37 h2_task_output *h2_task_output_create(h2_task *task, apr_pool_t *pool)
39 h2_task_output *output = apr_pcalloc(pool, sizeof(h2_task_output));
43 output->state = H2_TASK_OUT_INIT;
44 output->from_h1 = h2_from_h1_create(task->stream_id, pool);
45 if (!output->from_h1) {
52 void h2_task_output_destroy(h2_task_output *output)
54 if (output->from_h1) {
55 h2_from_h1_destroy(output->from_h1);
56 output->from_h1 = NULL;
60 static apr_status_t open_if_needed(h2_task_output *output, ap_filter_t *f,
61 apr_bucket_brigade *bb)
63 if (output->state == H2_TASK_OUT_INIT) {
64 h2_response *response;
65 output->state = H2_TASK_OUT_STARTED;
66 response = h2_from_h1_get_response(output->from_h1);
69 /* This happens currently when ap_die(status, r) is invoked
70 * by a read request filter.
72 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO(03204)
73 "h2_task_output(%s): write without response "
75 output->task->id, output->task->request->method,
76 output->task->request->authority,
77 output->task->request->path);
80 if (output->task->io) {
81 apr_thread_cond_broadcast(output->task->io);
83 return APR_ECONNABORTED;
86 output->trailers_passed = !!response->trailers;
87 return h2_mplx_out_open(output->task->mplx, output->task->stream_id,
88 response, f, bb, output->task->io);
93 static apr_table_t *get_trailers(h2_task_output *output)
95 if (!output->trailers_passed) {
96 h2_response *response = h2_from_h1_get_response(output->from_h1);
97 if (response && response->trailers) {
98 output->trailers_passed = 1;
99 return response->trailers;
105 void h2_task_output_close(h2_task_output *output)
107 open_if_needed(output, NULL, NULL);
108 if (output->state != H2_TASK_OUT_DONE) {
109 h2_mplx_out_close(output->task->mplx, output->task->stream_id,
110 get_trailers(output));
111 output->state = H2_TASK_OUT_DONE;
115 int h2_task_output_has_started(h2_task_output *output)
117 return output->state >= H2_TASK_OUT_STARTED;
120 /* Bring the data from the brigade (which represents the result of the
121 * request_rec out filter chain) into the h2_mplx for further sending
122 * on the master connection.
124 apr_status_t h2_task_output_write(h2_task_output *output,
125 ap_filter_t* f, apr_bucket_brigade* bb)
129 if (APR_BRIGADE_EMPTY(bb)) {
130 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
131 "h2_task_output(%s): empty write", output->task->id);
135 status = open_if_needed(output, f, bb);
136 if (status != APR_EOF) {
137 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
138 "h2_task_output(%s): opened and passed brigade",
143 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
144 "h2_task_output(%s): write brigade", output->task->id);
145 return h2_mplx_out_write(output->task->mplx, output->task->stream_id,
146 f, bb, get_trailers(output), output->task->io);