]> granicus.if.org Git - apache/blob - modules/http2/h2_io.c
rework of output handling on stream/session close, rework of cleartext (http:) output...
[apache] / modules / http2 / h2_io.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 <httpd.h>
19 #include <http_core.h>
20 #include <http_log.h>
21 #include <http_connection.h>
22
23 #include "h2_private.h"
24 #include "h2_io.h"
25 #include "h2_response.h"
26 #include "h2_task.h"
27 #include "h2_util.h"
28
29 h2_io *h2_io_create(int id, apr_pool_t *pool, apr_bucket_alloc_t *bucket_alloc)
30 {
31     h2_io *io = apr_pcalloc(pool, sizeof(*io));
32     if (io) {
33         io->id = id;
34         io->pool = pool;
35         io->bbin = NULL;
36         io->bbout = apr_brigade_create(pool, bucket_alloc);
37     }
38     return io;
39 }
40
41 static void h2_io_cleanup(h2_io *io)
42 {
43     if (io->task) {
44         h2_task_destroy(io->task);
45         io->task = NULL;
46     }
47 }
48
49 void h2_io_destroy(h2_io *io)
50 {
51     h2_io_cleanup(io);
52 }
53
54 void h2_io_set_response(h2_io *io, h2_response *response) 
55 {
56     AP_DEBUG_ASSERT(response);
57     AP_DEBUG_ASSERT(!io->response);
58     io->response = h2_response_copy(io->pool, response);
59     if (response->rst_error) {
60         h2_io_rst(io, response->rst_error);
61     }
62 }
63
64
65 void h2_io_rst(h2_io *io, int error)
66 {
67     io->rst_error = error;
68     io->eos_in = 1;
69 }
70
71 int h2_io_in_has_eos_for(h2_io *io)
72 {
73     return io->eos_in || (io->bbin && h2_util_has_eos(io->bbin, 0));
74 }
75
76 int h2_io_out_has_data(h2_io *io)
77 {
78     return h2_util_bb_has_data_or_eos(io->bbout);
79 }
80
81 apr_size_t h2_io_out_length(h2_io *io)
82 {
83     if (io->bbout) {
84         apr_off_t len = 0;
85         apr_brigade_length(io->bbout, 0, &len);
86         return (len > 0)? len : 0;
87     }
88     return 0;
89 }
90
91 apr_status_t h2_io_in_read(h2_io *io, apr_bucket_brigade *bb, 
92                            apr_size_t maxlen)
93 {
94     apr_off_t start_len = 0;
95     apr_bucket *last;
96     apr_status_t status;
97
98     if (io->rst_error) {
99         return APR_ECONNABORTED;
100     }
101     
102     if (!io->bbin || APR_BRIGADE_EMPTY(io->bbin)) {
103         return io->eos_in? APR_EOF : APR_EAGAIN;
104     }
105     
106     apr_brigade_length(bb, 1, &start_len);
107     last = APR_BRIGADE_LAST(bb);
108     status = h2_util_move(bb, io->bbin, maxlen, NULL, "h2_io_in_read");
109     if (status == APR_SUCCESS) {
110         apr_bucket *nlast = APR_BRIGADE_LAST(bb);
111         apr_off_t end_len = 0;
112         apr_brigade_length(bb, 1, &end_len);
113         if (last == nlast) {
114             return APR_EAGAIN;
115         }
116         io->input_consumed += (end_len - start_len);
117     }
118     return status;
119 }
120
121 apr_status_t h2_io_in_write(h2_io *io, apr_bucket_brigade *bb)
122 {
123     if (io->rst_error) {
124         return APR_ECONNABORTED;
125     }
126     
127     if (io->eos_in) {
128         return APR_EOF;
129     }
130     io->eos_in = h2_util_has_eos(bb, 0);
131     if (!APR_BRIGADE_EMPTY(bb)) {
132         if (!io->bbin) {
133             io->bbin = apr_brigade_create(io->bbout->p, 
134                                           io->bbout->bucket_alloc);
135         }
136         return h2_util_move(io->bbin, bb, 0, NULL, "h2_io_in_write");
137     }
138     return APR_SUCCESS;
139 }
140
141 apr_status_t h2_io_in_close(h2_io *io)
142 {
143     if (io->rst_error) {
144         return APR_ECONNABORTED;
145     }
146     
147     if (io->bbin) {
148         APR_BRIGADE_INSERT_TAIL(io->bbin, 
149                                 apr_bucket_eos_create(io->bbin->bucket_alloc));
150     }
151     io->eos_in = 1;
152     return APR_SUCCESS;
153 }
154
155 apr_status_t h2_io_out_readx(h2_io *io,  
156                              h2_io_data_cb *cb, void *ctx, 
157                              apr_size_t *plen, int *peos)
158 {
159     apr_status_t status;
160     
161     if (io->rst_error) {
162         return APR_ECONNABORTED;
163     }
164     
165     if (io->eos_out) {
166         *plen = 0;
167         *peos = 1;
168         return APR_SUCCESS;
169     }
170     
171     if (cb == NULL) {
172         /* just checking length available */
173         status = h2_util_bb_avail(io->bbout, plen, peos);
174     }
175     else {
176         status = h2_util_bb_readx(io->bbout, cb, ctx, plen, peos);
177         if (status == APR_SUCCESS) {
178             io->eos_out = *peos;
179         }
180     }
181     
182     return status;
183 }
184
185 apr_status_t h2_io_out_read_to(h2_io *io, apr_bucket_brigade *bb, 
186                                apr_size_t *plen, int *peos)
187 {
188     if (io->rst_error) {
189         return APR_ECONNABORTED;
190     }
191     
192     if (io->eos_out) {
193         *plen = 0;
194         *peos = 1;
195         return APR_SUCCESS;
196     }
197     
198
199     io->eos_out = *peos = h2_util_has_eos(io->bbout, *plen);
200     return h2_util_move(bb, io->bbout, *plen, NULL, "h2_io_read_to");
201 }
202
203 apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, 
204                              apr_size_t maxlen, int *pfile_handles_allowed)
205 {
206     apr_status_t status;
207     int start_allowed;
208     
209     if (io->rst_error) {
210         return APR_ECONNABORTED;
211     }
212
213     if (io->eos_out) {
214         apr_off_t len;
215         /* We have already delivered an EOS bucket to a reader, no
216          * sense in storing anything more here.
217          */
218         status = apr_brigade_length(bb, 1, &len);
219         if (status == APR_SUCCESS) {
220             if (len > 0) {
221                 /* someone tries to write real data after EOS, that
222                  * does not look right. */
223                 status = APR_EOF;
224             }
225             /* cleanup, as if we had moved the data */
226             apr_brigade_cleanup(bb);
227         }
228         return status;
229     }
230     
231     /* Let's move the buckets from the request processing in here, so
232      * that the main thread can read them when it has time/capacity.
233      *
234      * Move at most "maxlen" memory bytes. If buckets remain, it is
235      * the caller's responsibility to take care of this.
236      *
237      * We allow passing of file buckets as long as we do not have too
238      * many open files already buffered. Otherwise we will run out of
239      * file handles.
240      */
241     start_allowed = *pfile_handles_allowed;
242
243     status = h2_util_move(io->bbout, bb, maxlen, pfile_handles_allowed, 
244                           "h2_io_out_write");
245     /* track # file buckets moved into our pool */
246     if (start_allowed != *pfile_handles_allowed) {
247         io->files_handles_owned += (start_allowed - *pfile_handles_allowed);
248     }
249     return status;
250 }
251
252
253 apr_status_t h2_io_out_close(h2_io *io)
254 {
255     if (io->rst_error) {
256         return APR_ECONNABORTED;
257     }
258     if (!io->eos_out && !h2_util_has_eos(io->bbout, 0)) {
259         APR_BRIGADE_INSERT_TAIL(io->bbout, 
260                                 apr_bucket_eos_create(io->bbout->bucket_alloc));
261     }
262     return APR_SUCCESS;
263 }