diff options
Diffstat (limited to 'modules/http2/h2_io.c')
-rw-r--r-- | modules/http2/h2_io.c | 453 |
1 files changed, 0 insertions, 453 deletions
diff --git a/modules/http2/h2_io.c b/modules/http2/h2_io.c deleted file mode 100644 index 5bbf09e9..00000000 --- a/modules/http2/h2_io.c +++ /dev/null @@ -1,453 +0,0 @@ -/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <assert.h> - -#include <apr_pools.h> -#include <apr_thread_mutex.h> -#include <apr_thread_cond.h> - -#include <httpd.h> -#include <http_core.h> -#include <http_log.h> -#include <http_connection.h> -#include <http_request.h> - -#include "h2_private.h" -#include "h2_h2.h" -#include "h2_io.h" -#include "h2_mplx.h" -#include "h2_response.h" -#include "h2_request.h" -#include "h2_task.h" -#include "h2_util.h" - -h2_io *h2_io_create(int id, apr_pool_t *pool, - apr_bucket_alloc_t *bucket_alloc, - const h2_request *request) -{ - h2_io *io = apr_pcalloc(pool, sizeof(*io)); - if (io) { - io->id = id; - io->pool = pool; - io->bucket_alloc = bucket_alloc; - io->request = h2_request_clone(pool, request); - } - return io; -} - -static void check_bbin(h2_io *io) -{ - if (!io->bbin) { - io->bbin = apr_brigade_create(io->pool, io->bucket_alloc); - } -} - -static void check_bbout(h2_io *io) -{ - if (!io->bbout) { - io->bbout = apr_brigade_create(io->pool, io->bucket_alloc); - } -} - -static void check_bbtmp(h2_io *io) -{ - if (!io->bbtmp) { - io->bbtmp = apr_brigade_create(io->pool, io->bucket_alloc); - } -} - -static void append_eos(h2_io *io, apr_bucket_brigade *bb) -{ - APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(io->bucket_alloc)); -} - -void h2_io_redo(h2_io *io) -{ - io->worker_started = 0; - io->response = NULL; - io->rst_error = 0; - if (io->bbin) { - apr_brigade_cleanup(io->bbin); - } - if (io->bbout) { - apr_brigade_cleanup(io->bbout); - } - if (io->bbtmp) { - apr_brigade_cleanup(io->bbtmp); - } - io->started_at = io->done_at = 0; -} - -int h2_io_is_repeatable(h2_io *io) { - if (io->submitted - || io->input_consumed > 0 - || !io->request) { - /* cannot repeat that. */ - return 0; - } - return (!strcmp("GET", io->request->method) - || !strcmp("HEAD", io->request->method) - || !strcmp("OPTIONS", io->request->method)); -} - -void h2_io_set_response(h2_io *io, h2_response *response) -{ - AP_DEBUG_ASSERT(io->pool); - AP_DEBUG_ASSERT(response); - AP_DEBUG_ASSERT(!io->response); - /* we used to clone the response into the io->pool. But - * we have much tighter control over the EOR bucket nowadays, - * so just use the instance given */ - io->response = response; - if (response->rst_error) { - h2_io_rst(io, response->rst_error); - } - else if (response->content_length == 0) { - io->eos_out = 1; - } -} - -void h2_io_rst(h2_io *io, int error) -{ - io->rst_error = error; - io->eos_in = 1; -} - -int h2_io_out_has_data(h2_io *io) -{ - return io->bbout && h2_util_bb_has_data_or_eos(io->bbout); -} - -apr_off_t h2_io_out_length(h2_io *io) -{ - if (io->bbout) { - apr_off_t len = 0; - apr_brigade_length(io->bbout, 0, &len); - return (len > 0)? len : 0; - } - return 0; -} - -apr_status_t h2_io_in_shutdown(h2_io *io) -{ - if (io->bbin) { - apr_off_t end_len = 0; - apr_brigade_length(io->bbin, 1, &end_len); - io->input_consumed += end_len; - apr_brigade_cleanup(io->bbin); - } - return h2_io_in_close(io); -} - - -void h2_io_signal_init(h2_io *io, h2_io_op op, apr_interval_time_t timeout, - apr_thread_cond_t *cond) -{ - io->timed_op = op; - io->timed_cond = cond; - if (timeout > 0) { - io->timeout_at = apr_time_now() + timeout; - } - else { - io->timeout_at = 0; - } -} - -void h2_io_signal_exit(h2_io *io) -{ - io->timed_cond = NULL; - io->timeout_at = 0; -} - -apr_status_t h2_io_signal_wait(h2_mplx *m, h2_io *io) -{ - apr_status_t status; - - if (io->timeout_at != 0) { - status = apr_thread_cond_timedwait(io->timed_cond, m->lock, io->timeout_at); - if (APR_STATUS_IS_TIMEUP(status)) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c, APLOGNO(03055) - "h2_mplx(%ld-%d): stream timeout expired: %s", - m->id, io->id, - (io->timed_op == H2_IO_READ)? "read" : "write"); - h2_io_rst(io, H2_ERR_CANCEL); - } - } - else { - apr_thread_cond_wait(io->timed_cond, m->lock); - status = APR_SUCCESS; - } - if (io->orphaned && status == APR_SUCCESS) { - return APR_ECONNABORTED; - } - return status; -} - -void h2_io_signal(h2_io *io, h2_io_op op) -{ - if (io->timed_cond && (io->timed_op == op || H2_IO_ANY == op)) { - apr_thread_cond_signal(io->timed_cond); - } -} - -void h2_io_make_orphaned(h2_io *io, int error) -{ - io->orphaned = 1; - if (error) { - h2_io_rst(io, error); - } - /* if someone is waiting, wake him up */ - h2_io_signal(io, H2_IO_ANY); -} - -static int add_trailer(void *ctx, const char *key, const char *value) -{ - apr_bucket_brigade *bb = ctx; - apr_status_t status; - - status = apr_brigade_printf(bb, NULL, NULL, "%s: %s\r\n", - key, value); - return (status == APR_SUCCESS); -} - -static apr_status_t in_append_eos(h2_io *io, apr_bucket_brigade *bb, - apr_table_t *trailers) -{ - apr_status_t status = APR_SUCCESS; - apr_table_t *t = io->request->trailers; - - if (trailers && t && !apr_is_empty_table(trailers)) { - /* trailers passed in, transfer directly. */ - apr_table_overlap(trailers, t, APR_OVERLAP_TABLES_SET); - t = NULL; - } - - if (io->request->chunked) { - if (t && !apr_is_empty_table(t)) { - /* no trailers passed in, transfer via chunked */ - status = apr_brigade_puts(bb, NULL, NULL, "0\r\n"); - apr_table_do(add_trailer, bb, t, NULL); - status = apr_brigade_puts(bb, NULL, NULL, "\r\n"); - } - else { - status = apr_brigade_puts(bb, NULL, NULL, "0\r\n\r\n"); - } - } - append_eos(io, bb); - return status; -} - -apr_status_t h2_io_in_read(h2_io *io, apr_bucket_brigade *bb, - apr_size_t maxlen, apr_table_t *trailers) -{ - apr_off_t start_len = 0; - apr_status_t status; - - if (io->rst_error) { - return APR_ECONNABORTED; - } - - if (!io->bbin || APR_BRIGADE_EMPTY(io->bbin)) { - if (io->eos_in) { - if (!io->eos_in_written) { - status = in_append_eos(io, bb, trailers); - io->eos_in_written = 1; - return status; - } - return APR_EOF; - } - return APR_EAGAIN; - } - - if (io->request->chunked) { - /* the reader expects HTTP/1.1 chunked encoding */ - check_bbtmp(io); - status = h2_util_move(io->bbtmp, io->bbin, maxlen, NULL, "h2_io_in_read_chunk"); - if (status == APR_SUCCESS) { - apr_off_t tmp_len = 0; - - apr_brigade_length(io->bbtmp, 1, &tmp_len); - if (tmp_len > 0) { - io->input_consumed += tmp_len; - status = apr_brigade_printf(bb, NULL, NULL, "%lx\r\n", - (unsigned long)tmp_len); - if (status == APR_SUCCESS) { - status = h2_util_move(bb, io->bbtmp, -1, NULL, "h2_io_in_read_tmp1"); - if (status == APR_SUCCESS) { - status = apr_brigade_puts(bb, NULL, NULL, "\r\n"); - } - } - } - else { - status = h2_util_move(bb, io->bbtmp, -1, NULL, "h2_io_in_read_tmp2"); - } - apr_brigade_cleanup(io->bbtmp); - } - } - else { - apr_brigade_length(bb, 1, &start_len); - - status = h2_util_move(bb, io->bbin, maxlen, NULL, "h2_io_in_read"); - if (status == APR_SUCCESS) { - apr_off_t end_len = 0; - apr_brigade_length(bb, 1, &end_len); - io->input_consumed += (end_len - start_len); - } - } - - if (status == APR_SUCCESS && (!io->bbin || APR_BRIGADE_EMPTY(io->bbin))) { - if (io->eos_in) { - if (!io->eos_in_written) { - status = in_append_eos(io, bb, trailers); - io->eos_in_written = 1; - } - } - } - - if (status == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) { - return APR_EAGAIN; - } - return status; -} - -apr_status_t h2_io_in_write(h2_io *io, const char *d, apr_size_t len, int eos) -{ - if (io->rst_error) { - return APR_ECONNABORTED; - } - - if (io->eos_in) { - return APR_EOF; - } - if (eos) { - io->eos_in = 1; - } - if (len > 0) { - check_bbin(io); - return apr_brigade_write(io->bbin, NULL, NULL, d, len); - } - return APR_SUCCESS; -} - -apr_status_t h2_io_in_close(h2_io *io) -{ - if (io->rst_error) { - return APR_ECONNABORTED; - } - - io->eos_in = 1; - return APR_SUCCESS; -} - -apr_status_t h2_io_out_get_brigade(h2_io *io, apr_bucket_brigade *bb, - apr_off_t len) -{ - if (io->rst_error) { - return APR_ECONNABORTED; - } - if (io->eos_out_read) { - return APR_EOF; - } - else if (!io->bbout || APR_BRIGADE_EMPTY(io->bbout)) { - return APR_EAGAIN; - } - else { - apr_status_t status; - apr_off_t pre_len, post_len; - /* Allow file handles pass through without limits. If they - * already have the lifetime of this stream, we might as well - * pass them on to the master connection */ - apr_size_t files = INT_MAX; - - apr_brigade_length(bb, 0, &pre_len); - status = h2_util_move(bb, io->bbout, len, &files, "h2_io_read_to"); - if (status == APR_SUCCESS && io->eos_out - && APR_BRIGADE_EMPTY(io->bbout)) { - io->eos_out_read = 1; - } - apr_brigade_length(bb, 0, &post_len); - io->output_consumed += (post_len - pre_len); - return status; - } -} - -apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, - apr_size_t maxlen, - apr_size_t *pfile_buckets_allowed) -{ - apr_status_t status; - apr_bucket *b; - int start_allowed; - - if (io->rst_error) { - return APR_ECONNABORTED; - } - - /* Filter the EOR bucket and set it aside. We prefer to tear down - * the request when the whole h2 stream is done */ - for (b = APR_BRIGADE_FIRST(bb); - b != APR_BRIGADE_SENTINEL(bb); - b = APR_BUCKET_NEXT(b)) - { - if (AP_BUCKET_IS_EOR(b)) { - APR_BUCKET_REMOVE(b); - io->eor = b; - break; - } - else if (APR_BUCKET_IS_EOS(b)) { - io->eos_out = 1; - break; - } - } - - /* Let's move the buckets from the request processing in here, so - * that the main thread can read them when it has time/capacity. - * - * Move at most "maxlen" memory bytes. If buckets remain, it is - * the caller's responsibility to take care of this. - * - * We allow passing of file buckets as long as we do not have too - * many open files already buffered. Otherwise we will run out of - * file handles. - */ - check_bbout(io); - start_allowed = *pfile_buckets_allowed; - status = h2_util_move(io->bbout, bb, maxlen, pfile_buckets_allowed, - "h2_io_out_write"); - /* track # file buckets moved into our pool */ - if (start_allowed != *pfile_buckets_allowed) { - io->files_handles_owned += (start_allowed - *pfile_buckets_allowed); - } - return status; -} - - -apr_status_t h2_io_out_close(h2_io *io) -{ - if (io->rst_error) { - return APR_ECONNABORTED; - } - if (!io->eos_out_read) { /* EOS has not been read yet */ - if (!io->eos_out) { - check_bbout(io); - io->eos_out = 1; - if (!h2_util_has_eos(io->bbout, -1)) { - append_eos(io, io->bbout); - } - } - } - return APR_SUCCESS; -} |