]> granicus.if.org Git - apache/blob - modules/http2/h2_task_output.c
2bbac1c4ced50d4f96624c8b1fb2b2ed7586acb7
[apache] / modules / http2 / h2_task_output.c
1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
2  *
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
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  
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.
14  */
15
16 #include <assert.h>
17
18 #include <apr_thread_cond.h>
19 #include <httpd.h>
20 #include <http_core.h>
21 #include <http_log.h>
22 #include <http_connection.h>
23
24 #include "h2_private.h"
25 #include "h2_conn.h"
26 #include "h2_mplx.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"
33 #include "h2_task.h"
34 #include "h2_util.h"
35
36
37 h2_task_output *h2_task_output_create(h2_task *task, apr_pool_t *pool)
38 {
39     h2_task_output *output = apr_pcalloc(pool, sizeof(h2_task_output));
40     
41     if (output) {
42         output->task = task;
43         output->state = H2_TASK_OUT_INIT;
44         output->from_h1 = h2_from_h1_create(task->stream_id, pool);
45         if (!output->from_h1) {
46             return NULL;
47         }
48     }
49     return output;
50 }
51
52 void h2_task_output_destroy(h2_task_output *output)
53 {
54     if (output->from_h1) {
55         h2_from_h1_destroy(output->from_h1);
56         output->from_h1 = NULL;
57     }
58 }
59
60 static apr_status_t open_if_needed(h2_task_output *output, ap_filter_t *f,
61                                    apr_bucket_brigade *bb)
62 {
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);
67         if (!response) {
68             if (f) {
69                 /* This happens currently when ap_die(status, r) is invoked
70                  * by a read request filter.
71                  */
72                 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO(03204)
73                               "h2_task_output(%s): write without response "
74                               "for %s %s %s",
75                               output->task->id, output->task->request->method, 
76                               output->task->request->authority, 
77                               output->task->request->path);
78                 f->c->aborted = 1;
79             }
80             if (output->task->io) {
81                 apr_thread_cond_broadcast(output->task->io);
82             }
83             return APR_ECONNABORTED;
84         }
85         
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);
89     }
90     return APR_EOF;
91 }
92
93 static apr_table_t *get_trailers(h2_task_output *output)
94 {
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;
100         }
101     }
102     return NULL;
103 }
104
105 void h2_task_output_close(h2_task_output *output)
106 {
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;
112     }
113 }
114
115 int h2_task_output_has_started(h2_task_output *output)
116 {
117     return output->state >= H2_TASK_OUT_STARTED;
118 }
119
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. 
123  */
124 apr_status_t h2_task_output_write(h2_task_output *output,
125                                   ap_filter_t* f, apr_bucket_brigade* bb)
126 {
127     apr_status_t status;
128     
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);
132         return APR_SUCCESS;
133     }
134     
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", 
139                       output->task->id);
140         return status;
141     }
142     
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);
147 }
148