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