]> granicus.if.org Git - apache/blob - modules/http2/h2_io.c
mod_http2: some more code cleanup
[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 <apr_pools.h>
19 #include <apr_thread_mutex.h>
20 #include <apr_thread_cond.h>
21
22 #include <httpd.h>
23 #include <http_core.h>
24 #include <http_log.h>
25 #include <http_connection.h>
26
27 #include "h2_private.h"
28 #include "h2_h2.h"
29 #include "h2_io.h"
30 #include "h2_mplx.h"
31 #include "h2_response.h"
32 #include "h2_request.h"
33 #include "h2_task.h"
34 #include "h2_util.h"
35
36 h2_io *h2_io_create(int id, apr_pool_t *pool, const h2_request *request)
37 {
38     h2_io *io = apr_pcalloc(pool, sizeof(*io));
39     if (io) {
40         io->id = id;
41         io->pool = pool;
42         io->bucket_alloc = apr_bucket_alloc_create(pool);
43         io->request = h2_request_clone(pool, request);
44     }
45     return io;
46 }
47
48 static void check_bbin(h2_io *io)
49 {
50     if (!io->bbin) {
51         io->bbin = apr_brigade_create(io->pool, io->bucket_alloc);
52     }
53 }
54
55 static void check_bbout(h2_io *io)
56 {
57     if (!io->bbout) {
58         io->bbout = apr_brigade_create(io->pool, io->bucket_alloc);
59     }
60 }
61
62 static void check_bbtmp(h2_io *io)
63 {
64     if (!io->bbtmp) {
65         io->bbtmp = apr_brigade_create(io->pool, io->bucket_alloc);
66     }
67 }
68
69 static void append_eos(h2_io *io, apr_bucket_brigade *bb)
70 {
71     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(io->bucket_alloc));
72 }
73
74 void h2_io_redo(h2_io *io)
75 {
76     io->worker_started = 0;
77     io->response = NULL;
78     io->rst_error = 0;
79     if (io->bbin) {
80         apr_brigade_cleanup(io->bbin);
81     }
82     if (io->bbout) {
83         apr_brigade_cleanup(io->bbout);
84     }
85     if (io->bbtmp) {
86         apr_brigade_cleanup(io->bbtmp);
87     }
88     io->started_at = io->done_at = 0;
89 }
90
91 int h2_io_is_repeatable(h2_io *io) {
92     if (io->submitted
93         || io->input_consumed > 0 
94         || !io->request) {
95         /* cannot repeat that. */
96         return 0;
97     }
98     return (!strcmp("GET", io->request->method)
99             || !strcmp("HEAD", io->request->method)
100             || !strcmp("OPTIONS", io->request->method));
101 }
102
103 void h2_io_set_response(h2_io *io, h2_response *response) 
104 {
105     AP_DEBUG_ASSERT(io->pool);
106     AP_DEBUG_ASSERT(response);
107     AP_DEBUG_ASSERT(!io->response);
108     io->response = h2_response_clone(io->pool, response);
109     if (response->rst_error) {
110         h2_io_rst(io, response->rst_error);
111     }
112 }
113
114 void h2_io_rst(h2_io *io, int error)
115 {
116     io->rst_error = error;
117     io->eos_in = 1;
118 }
119
120 int h2_io_out_has_data(h2_io *io)
121 {
122     return io->bbout && h2_util_bb_has_data_or_eos(io->bbout);
123 }
124
125 apr_off_t h2_io_out_length(h2_io *io)
126 {
127     if (io->bbout) {
128         apr_off_t len = 0;
129         apr_brigade_length(io->bbout, 0, &len);
130         return (len > 0)? len : 0;
131     }
132     return 0;
133 }
134
135 apr_status_t h2_io_in_shutdown(h2_io *io)
136 {
137     if (io->bbin) {
138         apr_off_t end_len = 0;
139         apr_brigade_length(io->bbin, 1, &end_len);
140         io->input_consumed += end_len;
141         apr_brigade_cleanup(io->bbin);
142     }
143     return h2_io_in_close(io);
144 }
145
146
147 void h2_io_signal_init(h2_io *io, h2_io_op op, apr_interval_time_t timeout, 
148                        apr_thread_cond_t *cond)
149 {
150     io->timed_op = op;
151     io->timed_cond = cond;
152     if (timeout > 0) {
153         io->timeout_at = apr_time_now() + timeout;
154     }
155     else {
156         io->timeout_at = 0; 
157     }
158 }
159
160 void h2_io_signal_exit(h2_io *io)
161 {
162     io->timed_cond = NULL;
163     io->timeout_at = 0; 
164 }
165
166 apr_status_t h2_io_signal_wait(h2_mplx *m, h2_io *io)
167 {
168     apr_status_t status;
169     
170     if (io->timeout_at != 0) {
171         status = apr_thread_cond_timedwait(io->timed_cond, m->lock, io->timeout_at);
172         if (APR_STATUS_IS_TIMEUP(status)) {
173             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c, APLOGNO(03055)  
174                           "h2_mplx(%ld-%d): stream timeout expired: %s",
175                           m->id, io->id, 
176                           (io->timed_op == H2_IO_READ)? "read" : "write");
177             h2_io_rst(io, H2_ERR_CANCEL);
178         }
179     }
180     else {
181         apr_thread_cond_wait(io->timed_cond, m->lock);
182         status = APR_SUCCESS;
183     }
184     if (io->orphaned && status == APR_SUCCESS) {
185         return APR_ECONNABORTED;
186     }
187     return status;
188 }
189
190 void h2_io_signal(h2_io *io, h2_io_op op)
191 {
192     if (io->timed_cond && (io->timed_op == op || H2_IO_ANY == op)) {
193         apr_thread_cond_signal(io->timed_cond);
194     }
195 }
196
197 void h2_io_make_orphaned(h2_io *io, int error)
198 {
199     io->orphaned = 1;
200     if (error) {
201         h2_io_rst(io, error);
202     }
203     /* if someone is waiting, wake him up */
204     h2_io_signal(io, H2_IO_ANY);
205 }
206
207 static int add_trailer(void *ctx, const char *key, const char *value)
208 {
209     apr_bucket_brigade *bb = ctx;
210     apr_status_t status;
211     
212     status = apr_brigade_printf(bb, NULL, NULL, "%s: %s\r\n", 
213                                 key, value);
214     return (status == APR_SUCCESS);
215 }
216
217 static apr_status_t in_append_eos(h2_io *io, apr_bucket_brigade *bb, 
218                                   apr_table_t *trailers)
219 {
220     apr_status_t status = APR_SUCCESS;
221     apr_table_t *t = io->request->trailers;
222
223     if (trailers && t && !apr_is_empty_table(trailers)) {
224         /* trailers passed in, transfer directly. */
225         apr_table_overlap(trailers, t, APR_OVERLAP_TABLES_SET);
226         t = NULL;
227     }
228     
229     if (io->request->chunked) {
230         if (t && !apr_is_empty_table(t)) {
231             /* no trailers passed in, transfer via chunked */
232             status = apr_brigade_puts(bb, NULL, NULL, "0\r\n");
233             apr_table_do(add_trailer, bb, t, NULL);
234             status = apr_brigade_puts(bb, NULL, NULL, "\r\n");
235         }
236         else {
237             status = apr_brigade_puts(bb, NULL, NULL, "0\r\n\r\n");
238         }
239     }
240     append_eos(io, bb);
241     return status;
242 }
243
244 apr_status_t h2_io_in_read(h2_io *io, apr_bucket_brigade *bb, 
245                            apr_size_t maxlen, apr_table_t *trailers)
246 {
247     apr_off_t start_len = 0;
248     apr_status_t status;
249
250     if (io->rst_error) {
251         return APR_ECONNABORTED;
252     }
253     
254     if (!io->bbin || APR_BRIGADE_EMPTY(io->bbin)) {
255         if (io->eos_in) {
256             if (!io->eos_in_written) {
257                 status = in_append_eos(io, bb, trailers);
258                 io->eos_in_written = 1;
259                 return status;
260             }
261             return APR_EOF;
262         }
263         return APR_EAGAIN;
264     }
265     
266     if (io->request->chunked) {
267         /* the reader expects HTTP/1.1 chunked encoding */
268         check_bbtmp(io);
269         status = h2_util_move(io->bbtmp, io->bbin, maxlen, NULL, "h2_io_in_read_chunk");
270         if (status == APR_SUCCESS) {
271             apr_off_t tmp_len = 0;
272             
273             apr_brigade_length(io->bbtmp, 1, &tmp_len);
274             if (tmp_len > 0) {
275                 io->input_consumed += tmp_len;
276                 status = apr_brigade_printf(bb, NULL, NULL, "%lx\r\n", 
277                                             (unsigned long)tmp_len);
278                 if (status == APR_SUCCESS) {
279                     status = h2_util_move(bb, io->bbtmp, -1, NULL, "h2_io_in_read_tmp1");
280                     if (status == APR_SUCCESS) {
281                         status = apr_brigade_puts(bb, NULL, NULL, "\r\n");
282                     }
283                 }
284             }
285             else {
286                 status = h2_util_move(bb, io->bbtmp, -1, NULL, "h2_io_in_read_tmp2");
287             }
288             apr_brigade_cleanup(io->bbtmp);
289         }
290     }
291     else {
292         apr_brigade_length(bb, 1, &start_len);
293         
294         status = h2_util_move(bb, io->bbin, maxlen, NULL, "h2_io_in_read");
295         if (status == APR_SUCCESS) {
296             apr_off_t end_len = 0;
297             apr_brigade_length(bb, 1, &end_len);
298             io->input_consumed += (end_len - start_len);
299         }
300     }
301     
302     if (status == APR_SUCCESS && (!io->bbin || APR_BRIGADE_EMPTY(io->bbin))) {
303         if (io->eos_in) {
304             if (!io->eos_in_written) {
305                 status = in_append_eos(io, bb, trailers);
306                 io->eos_in_written = 1;
307             }
308         }
309     }
310     
311     if (status == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) {
312         return APR_EAGAIN;
313     }
314     return status;
315 }
316
317 apr_status_t h2_io_in_write(h2_io *io, const char *d, apr_size_t len, int eos)
318 {
319     if (io->rst_error) {
320         return APR_ECONNABORTED;
321     }
322     
323     if (io->eos_in) {
324         return APR_EOF;
325     }
326     if (eos) {
327         io->eos_in = 1;
328     }
329     if (len > 0) {
330         check_bbin(io);
331         return apr_brigade_write(io->bbin, NULL, NULL, d, len);
332     }
333     return APR_SUCCESS;
334 }
335
336 apr_status_t h2_io_in_close(h2_io *io)
337 {
338     if (io->rst_error) {
339         return APR_ECONNABORTED;
340     }
341     
342     io->eos_in = 1;
343     return APR_SUCCESS;
344 }
345
346 static int is_out_readable(h2_io *io, apr_off_t *plen, int *peos, 
347                            apr_status_t *ps)
348 {
349     if (io->rst_error) {
350         *ps = APR_ECONNABORTED;
351         return 0;
352     }
353     if (io->eos_out_read) {
354         *plen = 0;
355         *peos = 1;
356         *ps = APR_SUCCESS;
357         return 0;
358     }
359     else if (!io->bbout) {
360         *plen = 0;
361         *peos = 0;
362         *ps = APR_EAGAIN;
363         return 0;
364     }
365     return 1;
366 }
367
368 apr_status_t h2_io_out_readx(h2_io *io,  
369                              h2_io_data_cb *cb, void *ctx, 
370                              apr_off_t *plen, int *peos)
371 {
372     apr_status_t status;
373     if (!is_out_readable(io, plen, peos, &status)) {
374         return status;
375     }
376     if (cb == NULL) {
377         /* just checking length available */
378         status = h2_util_bb_avail(io->bbout, plen, peos);
379     }
380     else {
381         status = h2_util_bb_readx(io->bbout, cb, ctx, plen, peos);
382         if (status == APR_SUCCESS) {
383             io->eos_out_read = *peos;
384             io->output_consumed += *plen;
385         }
386     }
387     return status;
388 }
389
390 apr_status_t h2_io_out_read_to(h2_io *io, apr_bucket_brigade *bb, 
391                                apr_off_t *plen, int *peos)
392 {
393     apr_status_t status;
394     if (!is_out_readable(io, plen, peos, &status)) {
395         return status;
396     }
397     io->eos_out_read = *peos = h2_util_has_eos(io->bbout, *plen);
398     status = h2_util_move(bb, io->bbout, *plen, NULL, "h2_io_read_to");
399     io->output_consumed += *plen;
400     return status;
401 }
402
403 static void process_trailers(h2_io *io, apr_table_t *trailers)
404 {
405     if (trailers && io->response) {
406         h2_response_set_trailers(io->response, 
407                                  apr_table_clone(io->pool, trailers));
408     }
409 }
410
411 apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, 
412                              apr_size_t maxlen, apr_table_t *trailers,
413                              apr_size_t *pfile_buckets_allowed)
414 {
415     apr_status_t status;
416     int start_allowed;
417     
418     if (io->rst_error) {
419         return APR_ECONNABORTED;
420     }
421
422     if (io->eos_out) {
423         apr_off_t len;
424         /* We have already delivered an EOS bucket to a reader, no
425          * sense in storing anything more here.
426          */
427         status = apr_brigade_length(bb, 1, &len);
428         if (status == APR_SUCCESS) {
429             if (len > 0) {
430                 /* someone tries to write real data after EOS, that
431                  * does not look right. */
432                 status = APR_EOF;
433             }
434             /* cleanup, as if we had moved the data */
435             apr_brigade_cleanup(bb);
436         }
437         return status;
438     }
439
440     process_trailers(io, trailers);
441     
442     /* Let's move the buckets from the request processing in here, so
443      * that the main thread can read them when it has time/capacity.
444      *
445      * Move at most "maxlen" memory bytes. If buckets remain, it is
446      * the caller's responsibility to take care of this.
447      *
448      * We allow passing of file buckets as long as we do not have too
449      * many open files already buffered. Otherwise we will run out of
450      * file handles.
451      */
452     check_bbout(io);
453     start_allowed = *pfile_buckets_allowed;
454     status = h2_util_move(io->bbout, bb, maxlen, pfile_buckets_allowed, 
455                           "h2_io_out_write");
456     /* track # file buckets moved into our pool */
457     if (start_allowed != *pfile_buckets_allowed) {
458         io->files_handles_owned += (start_allowed - *pfile_buckets_allowed);
459     }
460     return status;
461 }
462
463
464 apr_status_t h2_io_out_close(h2_io *io, apr_table_t *trailers)
465 {
466     if (io->rst_error) {
467         return APR_ECONNABORTED;
468     }
469     if (!io->eos_out_read) { /* EOS has not been read yet */
470         process_trailers(io, trailers);
471         if (!io->eos_out) {
472             check_bbout(io);
473             io->eos_out = 1;
474             if (!h2_util_has_eos(io->bbout, -1)) {
475                 append_eos(io, io->bbout);
476             }
477         }
478     }
479     return APR_SUCCESS;
480 }