|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* @file core_filters.c
|
|
Packit |
90a5c9 |
* @brief Core input/output network filters.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "apr.h"
|
|
Packit |
90a5c9 |
#include "apr_strings.h"
|
|
Packit |
90a5c9 |
#include "apr_lib.h"
|
|
Packit |
90a5c9 |
#include "apr_fnmatch.h"
|
|
Packit |
90a5c9 |
#include "apr_hash.h"
|
|
Packit |
90a5c9 |
#include "apr_thread_proc.h" /* for RLIMIT stuff */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define APR_WANT_IOVEC
|
|
Packit |
90a5c9 |
#define APR_WANT_STRFUNC
|
|
Packit |
90a5c9 |
#define APR_WANT_MEMFUNC
|
|
Packit |
90a5c9 |
#include "apr_want.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "ap_config.h"
|
|
Packit |
90a5c9 |
#include "httpd.h"
|
|
Packit |
90a5c9 |
#include "http_config.h"
|
|
Packit |
90a5c9 |
#include "http_core.h"
|
|
Packit |
90a5c9 |
#include "http_protocol.h" /* For index_of_response(). Grump. */
|
|
Packit |
90a5c9 |
#include "http_request.h"
|
|
Packit |
90a5c9 |
#include "http_vhost.h"
|
|
Packit |
90a5c9 |
#include "http_main.h" /* For the default_handler below... */
|
|
Packit |
90a5c9 |
#include "http_log.h"
|
|
Packit |
90a5c9 |
#include "util_md5.h"
|
|
Packit |
90a5c9 |
#include "http_connection.h"
|
|
Packit |
90a5c9 |
#include "apr_buckets.h"
|
|
Packit |
90a5c9 |
#include "util_filter.h"
|
|
Packit |
90a5c9 |
#include "util_ebcdic.h"
|
|
Packit |
90a5c9 |
#include "mpm_common.h"
|
|
Packit |
90a5c9 |
#include "scoreboard.h"
|
|
Packit |
90a5c9 |
#include "mod_core.h"
|
|
Packit |
90a5c9 |
#include "ap_listen.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "mod_so.h" /* for ap_find_loaded_module_symbol */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define AP_MIN_SENDFILE_BYTES (256)
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Remove all zero length buckets from the brigade.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
#define BRIGADE_NORMALIZE(b) \
|
|
Packit |
90a5c9 |
do { \
|
|
Packit |
90a5c9 |
apr_bucket *e = APR_BRIGADE_FIRST(b); \
|
|
Packit |
90a5c9 |
do { \
|
|
Packit |
90a5c9 |
if (e->length == 0 && !APR_BUCKET_IS_METADATA(e)) { \
|
|
Packit |
90a5c9 |
apr_bucket *d; \
|
|
Packit |
90a5c9 |
d = APR_BUCKET_NEXT(e); \
|
|
Packit |
90a5c9 |
apr_bucket_delete(e); \
|
|
Packit |
90a5c9 |
e = d; \
|
|
Packit |
90a5c9 |
} \
|
|
Packit |
90a5c9 |
else { \
|
|
Packit |
90a5c9 |
e = APR_BUCKET_NEXT(e); \
|
|
Packit |
90a5c9 |
} \
|
|
Packit |
90a5c9 |
} while (!APR_BRIGADE_EMPTY(b) && (e != APR_BRIGADE_SENTINEL(b))); \
|
|
Packit |
90a5c9 |
} while (0)
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* we know core's module_index is 0 */
|
|
Packit |
90a5c9 |
#undef APLOG_MODULE_INDEX
|
|
Packit |
90a5c9 |
#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
struct core_output_filter_ctx {
|
|
Packit |
90a5c9 |
apr_bucket_brigade *buffered_bb;
|
|
Packit |
90a5c9 |
apr_bucket_brigade *tmp_flush_bb;
|
|
Packit |
90a5c9 |
apr_pool_t *deferred_write_pool;
|
|
Packit |
90a5c9 |
apr_size_t bytes_written;
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
struct core_filter_ctx {
|
|
Packit |
90a5c9 |
apr_bucket_brigade *b;
|
|
Packit |
90a5c9 |
apr_bucket_brigade *tmpbb;
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
|
|
Packit |
90a5c9 |
ap_input_mode_t mode, apr_read_type_e block,
|
|
Packit |
90a5c9 |
apr_off_t readbytes)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
core_net_rec *net = f->ctx;
|
|
Packit |
90a5c9 |
core_ctx_t *ctx = net->in_ctx;
|
|
Packit |
90a5c9 |
const char *str;
|
|
Packit |
90a5c9 |
apr_size_t len;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (mode == AP_MODE_INIT) {
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* this mode is for filters that might need to 'initialize'
|
|
Packit |
90a5c9 |
* a connection before reading request data from a client.
|
|
Packit |
90a5c9 |
* NNTP over SSL for example needs to handshake before the
|
|
Packit |
90a5c9 |
* server sends the welcome message.
|
|
Packit |
90a5c9 |
* such filters would have changed the mode before this point
|
|
Packit |
90a5c9 |
* is reached. however, protocol modules such as NNTP should
|
|
Packit |
90a5c9 |
* not need to know anything about SSL. given the example, if
|
|
Packit |
90a5c9 |
* SSL is not in the filter chain, AP_MODE_INIT is a noop.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!ctx)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
net->in_ctx = ctx = apr_palloc(f->c->pool, sizeof(*ctx));
|
|
Packit |
90a5c9 |
ctx->b = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
|
|
Packit |
90a5c9 |
ctx->tmpbb = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
|
|
Packit |
90a5c9 |
/* seed the brigade with the client socket. */
|
|
Packit |
90a5c9 |
rv = ap_run_insert_network_bucket(f->c, ctx->b, net->client_socket);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS)
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (APR_BRIGADE_EMPTY(ctx->b)) {
|
|
Packit |
90a5c9 |
return APR_EOF;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* ### This is bad. */
|
|
Packit |
90a5c9 |
BRIGADE_NORMALIZE(ctx->b);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* check for empty brigade again *AFTER* BRIGADE_NORMALIZE()
|
|
Packit |
90a5c9 |
* If we have lost our socket bucket (see above), we are EOF.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Ideally, this should be returning SUCCESS with EOS bucket, but
|
|
Packit |
90a5c9 |
* some higher-up APIs (spec. read_request_line via ap_rgetline)
|
|
Packit |
90a5c9 |
* want an error code. */
|
|
Packit |
90a5c9 |
if (APR_BRIGADE_EMPTY(ctx->b)) {
|
|
Packit |
90a5c9 |
return APR_EOF;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (mode == AP_MODE_GETLINE) {
|
|
Packit |
90a5c9 |
/* we are reading a single LF line, e.g. the HTTP headers */
|
|
Packit |
90a5c9 |
rv = apr_brigade_split_line(b, ctx->b, block, HUGE_STRING_LEN);
|
|
Packit |
90a5c9 |
/* We should treat EAGAIN here the same as we do for EOF (brigade is
|
|
Packit |
90a5c9 |
* empty). We do this by returning whatever we have read. This may
|
|
Packit |
90a5c9 |
* or may not be bogus, but is consistent (for now) with EOF logic.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_EAGAIN(rv) && block == APR_NONBLOCK_READ) {
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* ### AP_MODE_PEEK is a horrific name for this mode because we also
|
|
Packit |
90a5c9 |
* eat any CRLFs that we see. That's not the obvious intention of
|
|
Packit |
90a5c9 |
* this mode. Determine whether anyone actually uses this or not. */
|
|
Packit |
90a5c9 |
if (mode == AP_MODE_EATCRLF) {
|
|
Packit |
90a5c9 |
apr_bucket *e;
|
|
Packit |
90a5c9 |
const char *c;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* The purpose of this loop is to ignore any CRLF (or LF) at the end
|
|
Packit |
90a5c9 |
* of a request. Many browsers send extra lines at the end of POST
|
|
Packit |
90a5c9 |
* requests. We use the PEEK method to determine if there is more
|
|
Packit |
90a5c9 |
* data on the socket, so that we know if we should delay sending the
|
|
Packit |
90a5c9 |
* end of one request until we have served the second request in a
|
|
Packit |
90a5c9 |
* pipelined situation. We don't want to actually delay sending a
|
|
Packit |
90a5c9 |
* response if the server finds a CRLF (or LF), becuause that doesn't
|
|
Packit |
90a5c9 |
* mean that there is another request, just a blank line.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
while (1) {
|
|
Packit |
90a5c9 |
if (APR_BRIGADE_EMPTY(ctx->b))
|
|
Packit |
90a5c9 |
return APR_EOF;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
e = APR_BRIGADE_FIRST(ctx->b);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = apr_bucket_read(e, &str, &len, APR_NONBLOCK_READ);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS)
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
c = str;
|
|
Packit |
90a5c9 |
while (c < str + len) {
|
|
Packit |
90a5c9 |
if (*c == APR_ASCII_LF)
|
|
Packit |
90a5c9 |
c++;
|
|
Packit |
90a5c9 |
else if (*c == APR_ASCII_CR && *(c + 1) == APR_ASCII_LF)
|
|
Packit |
90a5c9 |
c += 2;
|
|
Packit |
90a5c9 |
else
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If we reach here, we were a bucket just full of CRLFs, so
|
|
Packit |
90a5c9 |
* just toss the bucket. */
|
|
Packit |
90a5c9 |
/* FIXME: Is this the right thing to do in the core? */
|
|
Packit |
90a5c9 |
apr_bucket_delete(e);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If mode is EXHAUSTIVE, we want to just read everything until the end
|
|
Packit |
90a5c9 |
* of the brigade, which in this case means the end of the socket.
|
|
Packit |
90a5c9 |
* To do this, we attach the brigade that has currently been setaside to
|
|
Packit |
90a5c9 |
* the brigade that was passed down, and send that brigade back.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* NOTE: This is VERY dangerous to use, and should only be done with
|
|
Packit |
90a5c9 |
* extreme caution. FWLIW, this would be needed by an MPM like Perchild;
|
|
Packit |
90a5c9 |
* such an MPM can easily request the socket and all data that has been
|
|
Packit |
90a5c9 |
* read, which means that it can pass it to the correct child process.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (mode == AP_MODE_EXHAUSTIVE) {
|
|
Packit |
90a5c9 |
apr_bucket *e;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Tack on any buckets that were set aside. */
|
|
Packit |
90a5c9 |
APR_BRIGADE_CONCAT(b, ctx->b);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Since we've just added all potential buckets (which will most
|
|
Packit |
90a5c9 |
* likely simply be the socket bucket) we know this is the end,
|
|
Packit |
90a5c9 |
* so tack on an EOS too. */
|
|
Packit |
90a5c9 |
/* We have read until the brigade was empty, so we know that we
|
|
Packit |
90a5c9 |
* must be EOS. */
|
|
Packit |
90a5c9 |
e = apr_bucket_eos_create(f->c->bucket_alloc);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(b, e);
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* read up to the amount they specified. */
|
|
Packit |
90a5c9 |
if (mode == AP_MODE_READBYTES || mode == AP_MODE_SPECULATIVE) {
|
|
Packit |
90a5c9 |
apr_bucket *e;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DEBUG_ASSERT(readbytes > 0);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
e = APR_BRIGADE_FIRST(ctx->b);
|
|
Packit |
90a5c9 |
rv = apr_bucket_read(e, &str, &len, block);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_EAGAIN(rv) && block == APR_NONBLOCK_READ) {
|
|
Packit |
90a5c9 |
/* getting EAGAIN for a blocking read is an error; for a
|
|
Packit |
90a5c9 |
* non-blocking read, return an empty brigade. */
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (block == APR_BLOCK_READ && len == 0) {
|
|
Packit |
90a5c9 |
/* We wanted to read some bytes in blocking mode. We read
|
|
Packit |
90a5c9 |
* 0 bytes. Hence, we now assume we are EOS.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* When we are in normal mode, return an EOS bucket to the
|
|
Packit |
90a5c9 |
* caller.
|
|
Packit |
90a5c9 |
* When we are in speculative mode, leave ctx->b empty, so
|
|
Packit |
90a5c9 |
* that the next call returns an EOS bucket.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
apr_bucket_delete(e);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (mode == AP_MODE_READBYTES) {
|
|
Packit |
90a5c9 |
e = apr_bucket_eos_create(f->c->bucket_alloc);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(b, e);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Have we read as much data as we wanted (be greedy)? */
|
|
Packit |
90a5c9 |
if (len < readbytes) {
|
|
Packit |
90a5c9 |
apr_size_t bucket_len;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
/* We already registered the data in e in len */
|
|
Packit |
90a5c9 |
e = APR_BUCKET_NEXT(e);
|
|
Packit |
90a5c9 |
while ((len < readbytes) && (rv == APR_SUCCESS)
|
|
Packit |
90a5c9 |
&& (e != APR_BRIGADE_SENTINEL(ctx->b))) {
|
|
Packit |
90a5c9 |
/* Check for the availability of buckets with known length */
|
|
Packit |
90a5c9 |
if (e->length != -1) {
|
|
Packit |
90a5c9 |
len += e->length;
|
|
Packit |
90a5c9 |
e = APR_BUCKET_NEXT(e);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Read from bucket, but non blocking. If there isn't any
|
|
Packit |
90a5c9 |
* more data, well than this is fine as well, we will
|
|
Packit |
90a5c9 |
* not wait for more since we already got some and we are
|
|
Packit |
90a5c9 |
* only checking if there isn't more.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
rv = apr_bucket_read(e, &str, &bucket_len,
|
|
Packit |
90a5c9 |
APR_NONBLOCK_READ);
|
|
Packit |
90a5c9 |
if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
len += bucket_len;
|
|
Packit |
90a5c9 |
e = APR_BUCKET_NEXT(e);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We can only return at most what we read. */
|
|
Packit |
90a5c9 |
if (len < readbytes) {
|
|
Packit |
90a5c9 |
readbytes = len;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = apr_brigade_partition(ctx->b, readbytes, &e);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Must do move before CONCAT */
|
|
Packit |
90a5c9 |
ctx->tmpbb = apr_brigade_split_ex(ctx->b, e, ctx->tmpbb);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (mode == AP_MODE_READBYTES) {
|
|
Packit |
90a5c9 |
APR_BRIGADE_CONCAT(b, ctx->b);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (mode == AP_MODE_SPECULATIVE) {
|
|
Packit |
90a5c9 |
apr_bucket *copy_bucket;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (e = APR_BRIGADE_FIRST(ctx->b);
|
|
Packit |
90a5c9 |
e != APR_BRIGADE_SENTINEL(ctx->b);
|
|
Packit |
90a5c9 |
e = APR_BUCKET_NEXT(e))
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
rv = apr_bucket_copy(e, ©_bucket);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(b, copy_bucket);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Take what was originally there and place it back on ctx->b */
|
|
Packit |
90a5c9 |
APR_BRIGADE_CONCAT(ctx->b, ctx->tmpbb);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void setaside_remaining_output(ap_filter_t *f,
|
|
Packit |
90a5c9 |
core_output_filter_ctx_t *ctx,
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb,
|
|
Packit |
90a5c9 |
conn_rec *c);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb,
|
|
Packit |
90a5c9 |
apr_size_t *bytes_written,
|
|
Packit |
90a5c9 |
conn_rec *c);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void remove_empty_buckets(apr_bucket_brigade *bb);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t send_brigade_blocking(apr_socket_t *s,
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb,
|
|
Packit |
90a5c9 |
apr_size_t *bytes_written,
|
|
Packit |
90a5c9 |
conn_rec *c);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t writev_nonblocking(apr_socket_t *s,
|
|
Packit |
90a5c9 |
struct iovec *vec, apr_size_t nvec,
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb,
|
|
Packit |
90a5c9 |
apr_size_t *cumulative_bytes_written,
|
|
Packit |
90a5c9 |
conn_rec *c);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#if APR_HAS_SENDFILE
|
|
Packit |
90a5c9 |
static apr_status_t sendfile_nonblocking(apr_socket_t *s,
|
|
Packit |
90a5c9 |
apr_bucket *bucket,
|
|
Packit |
90a5c9 |
apr_size_t *cumulative_bytes_written,
|
|
Packit |
90a5c9 |
conn_rec *c);
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* XXX: Should these be configurable parameters? */
|
|
Packit |
90a5c9 |
#define THRESHOLD_MIN_WRITE 4096
|
|
Packit |
90a5c9 |
#define THRESHOLD_MAX_BUFFER 65536
|
|
Packit |
90a5c9 |
#define MAX_REQUESTS_IN_PIPELINE 5
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Optional function coming from mod_logio, used for logging of output
|
|
Packit |
90a5c9 |
* traffic
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *ap__logio_add_bytes_out;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
conn_rec *c = f->c;
|
|
Packit |
90a5c9 |
core_net_rec *net = f->ctx;
|
|
Packit |
90a5c9 |
core_output_filter_ctx_t *ctx = net->out_ctx;
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb = NULL;
|
|
Packit |
90a5c9 |
apr_bucket *bucket, *next, *flush_upto = NULL;
|
|
Packit |
90a5c9 |
apr_size_t bytes_in_brigade, non_file_bytes_in_brigade;
|
|
Packit |
90a5c9 |
int eor_buckets_in_brigade, morphing_bucket_in_brigade;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
int loglevel = ap_get_conn_module_loglevel(c, APLOG_MODULE_INDEX);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Fail quickly if the connection has already been aborted. */
|
|
Packit |
90a5c9 |
if (c->aborted) {
|
|
Packit |
90a5c9 |
if (new_bb != NULL) {
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(new_bb);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_ECONNABORTED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ctx == NULL) {
|
|
Packit |
90a5c9 |
ctx = apr_pcalloc(c->pool, sizeof(*ctx));
|
|
Packit |
90a5c9 |
net->out_ctx = (core_output_filter_ctx_t *)ctx;
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Need to create tmp brigade with correct lifetime. Passing
|
|
Packit |
90a5c9 |
* NULL to apr_brigade_split_ex would result in a brigade
|
|
Packit |
90a5c9 |
* allocated from bb->pool which might be wrong.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
ctx->tmp_flush_bb = apr_brigade_create(c->pool, c->bucket_alloc);
|
|
Packit |
90a5c9 |
/* same for buffered_bb and ap_save_brigade */
|
|
Packit |
90a5c9 |
ctx->buffered_bb = apr_brigade_create(c->pool, c->bucket_alloc);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (new_bb != NULL)
|
|
Packit |
90a5c9 |
bb = new_bb;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((ctx->buffered_bb != NULL) &&
|
|
Packit |
90a5c9 |
!APR_BRIGADE_EMPTY(ctx->buffered_bb)) {
|
|
Packit |
90a5c9 |
if (new_bb != NULL) {
|
|
Packit |
90a5c9 |
APR_BRIGADE_PREPEND(bb, ctx->buffered_bb);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
bb = ctx->buffered_bb;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
c->data_in_output_filters = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (new_bb == NULL) {
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Scan through the brigade and decide whether to attempt a write,
|
|
Packit |
90a5c9 |
* and how much to write, based on the following rules:
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* 1) The new_bb is null: Do a nonblocking write of as much as
|
|
Packit |
90a5c9 |
* possible: do a nonblocking write of as much data as possible,
|
|
Packit |
90a5c9 |
* then save the rest in ctx->buffered_bb. (If new_bb == NULL,
|
|
Packit |
90a5c9 |
* it probably means that the MPM is doing asynchronous write
|
|
Packit |
90a5c9 |
* completion and has just determined that this connection
|
|
Packit |
90a5c9 |
* is writable.)
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* 2) Determine if and up to which bucket we need to do a blocking
|
|
Packit |
90a5c9 |
* write:
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* a) The brigade contains a flush bucket: Do a blocking write
|
|
Packit |
90a5c9 |
* of everything up that point.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* b) The request is in CONN_STATE_HANDLER state, and the brigade
|
|
Packit |
90a5c9 |
* contains at least THRESHOLD_MAX_BUFFER bytes in non-file
|
|
Packit |
90a5c9 |
* buckets: Do blocking writes until the amount of data in the
|
|
Packit |
90a5c9 |
* buffer is less than THRESHOLD_MAX_BUFFER. (The point of this
|
|
Packit |
90a5c9 |
* rule is to provide flow control, in case a handler is
|
|
Packit |
90a5c9 |
* streaming out lots of data faster than the data can be
|
|
Packit |
90a5c9 |
* sent to the client.)
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* c) The request is in CONN_STATE_HANDLER state, and the brigade
|
|
Packit |
90a5c9 |
* contains at least MAX_REQUESTS_IN_PIPELINE EOR buckets:
|
|
Packit |
90a5c9 |
* Do blocking writes until less than MAX_REQUESTS_IN_PIPELINE EOR
|
|
Packit |
90a5c9 |
* buckets are left. (The point of this rule is to prevent too many
|
|
Packit |
90a5c9 |
* FDs being kept open by pipelined requests, possibly allowing a
|
|
Packit |
90a5c9 |
* DoS).
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* d) The brigade contains a morphing bucket: If there was no other
|
|
Packit |
90a5c9 |
* reason to do a blocking write yet, try reading the bucket. If its
|
|
Packit |
90a5c9 |
* contents fit into memory before THRESHOLD_MAX_BUFFER is reached,
|
|
Packit |
90a5c9 |
* everything is fine. Otherwise we need to do a blocking write the
|
|
Packit |
90a5c9 |
* up to and including the morphing bucket, because ap_save_brigade()
|
|
Packit |
90a5c9 |
* would read the whole bucket into memory later on.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* 3) Actually do the blocking write up to the last bucket determined
|
|
Packit |
90a5c9 |
* by rules 2a-d. The point of doing only one flush is to make as
|
|
Packit |
90a5c9 |
* few calls to writev() as possible.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* 4) If the brigade contains at least THRESHOLD_MIN_WRITE
|
|
Packit |
90a5c9 |
* bytes: Do a nonblocking write of as much data as possible,
|
|
Packit |
90a5c9 |
* then save the rest in ctx->buffered_bb.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (new_bb == NULL) {
|
|
Packit |
90a5c9 |
rv = send_brigade_nonblocking(net->client_socket, bb,
|
|
Packit |
90a5c9 |
&(ctx->bytes_written), c);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
|
|
Packit |
90a5c9 |
/* The client has aborted the connection */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c,
|
|
Packit |
90a5c9 |
"core_output_filter: writing data to the network");
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(bb);
|
|
Packit |
90a5c9 |
c->aborted = 1;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
setaside_remaining_output(f, ctx, bb, c);
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
bytes_in_brigade = 0;
|
|
Packit |
90a5c9 |
non_file_bytes_in_brigade = 0;
|
|
Packit |
90a5c9 |
eor_buckets_in_brigade = 0;
|
|
Packit |
90a5c9 |
morphing_bucket_in_brigade = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb);
|
|
Packit |
90a5c9 |
bucket = next) {
|
|
Packit |
90a5c9 |
next = APR_BUCKET_NEXT(bucket);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!APR_BUCKET_IS_METADATA(bucket)) {
|
|
Packit |
90a5c9 |
if (bucket->length == (apr_size_t)-1) {
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* A setaside of morphing buckets would read everything into
|
|
Packit |
90a5c9 |
* memory. Instead, we will flush everything up to and
|
|
Packit |
90a5c9 |
* including this bucket.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
morphing_bucket_in_brigade = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
bytes_in_brigade += bucket->length;
|
|
Packit |
90a5c9 |
if (!APR_BUCKET_IS_FILE(bucket))
|
|
Packit |
90a5c9 |
non_file_bytes_in_brigade += bucket->length;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (AP_BUCKET_IS_EOR(bucket)) {
|
|
Packit |
90a5c9 |
eor_buckets_in_brigade++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_BUCKET_IS_FLUSH(bucket)
|
|
Packit |
90a5c9 |
|| non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER
|
|
Packit |
90a5c9 |
|| morphing_bucket_in_brigade
|
|
Packit |
90a5c9 |
|| eor_buckets_in_brigade > MAX_REQUESTS_IN_PIPELINE) {
|
|
Packit |
90a5c9 |
/* this segment of the brigade MUST be sent before returning. */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (loglevel >= APLOG_TRACE6) {
|
|
Packit |
90a5c9 |
char *reason = APR_BUCKET_IS_FLUSH(bucket) ?
|
|
Packit |
90a5c9 |
"FLUSH bucket" :
|
|
Packit |
90a5c9 |
(non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER) ?
|
|
Packit |
90a5c9 |
"THRESHOLD_MAX_BUFFER" :
|
|
Packit |
90a5c9 |
morphing_bucket_in_brigade ? "morphing bucket" :
|
|
Packit |
90a5c9 |
"MAX_REQUESTS_IN_PIPELINE";
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, c,
|
|
Packit |
90a5c9 |
"will flush because of %s", reason);
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
|
|
Packit |
90a5c9 |
"seen in brigade%s: bytes: %" APR_SIZE_T_FMT
|
|
Packit |
90a5c9 |
", non-file bytes: %" APR_SIZE_T_FMT ", eor "
|
|
Packit |
90a5c9 |
"buckets: %d, morphing buckets: %d",
|
|
Packit |
90a5c9 |
flush_upto == NULL ? " so far"
|
|
Packit |
90a5c9 |
: " since last flush point",
|
|
Packit |
90a5c9 |
bytes_in_brigade,
|
|
Packit |
90a5c9 |
non_file_bytes_in_brigade,
|
|
Packit |
90a5c9 |
eor_buckets_in_brigade,
|
|
Packit |
90a5c9 |
morphing_bucket_in_brigade);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Defer the actual blocking write to avoid doing many writes.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
flush_upto = next;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
bytes_in_brigade = 0;
|
|
Packit |
90a5c9 |
non_file_bytes_in_brigade = 0;
|
|
Packit |
90a5c9 |
eor_buckets_in_brigade = 0;
|
|
Packit |
90a5c9 |
morphing_bucket_in_brigade = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (flush_upto != NULL) {
|
|
Packit |
90a5c9 |
ctx->tmp_flush_bb = apr_brigade_split_ex(bb, flush_upto,
|
|
Packit |
90a5c9 |
ctx->tmp_flush_bb);
|
|
Packit |
90a5c9 |
if (loglevel >= APLOG_TRACE8) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
|
|
Packit |
90a5c9 |
"flushing now");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
rv = send_brigade_blocking(net->client_socket, bb,
|
|
Packit |
90a5c9 |
&(ctx->bytes_written), c);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
/* The client has aborted the connection */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c,
|
|
Packit |
90a5c9 |
"core_output_filter: writing data to the network");
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(bb);
|
|
Packit |
90a5c9 |
c->aborted = 1;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (loglevel >= APLOG_TRACE8) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
|
|
Packit |
90a5c9 |
"total bytes written: %" APR_SIZE_T_FMT,
|
|
Packit |
90a5c9 |
ctx->bytes_written);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
APR_BRIGADE_CONCAT(bb, ctx->tmp_flush_bb);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (loglevel >= APLOG_TRACE8) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
|
|
Packit |
90a5c9 |
"brigade contains: bytes: %" APR_SIZE_T_FMT
|
|
Packit |
90a5c9 |
", non-file bytes: %" APR_SIZE_T_FMT
|
|
Packit |
90a5c9 |
", eor buckets: %d, morphing buckets: %d",
|
|
Packit |
90a5c9 |
bytes_in_brigade, non_file_bytes_in_brigade,
|
|
Packit |
90a5c9 |
eor_buckets_in_brigade, morphing_bucket_in_brigade);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (bytes_in_brigade >= THRESHOLD_MIN_WRITE) {
|
|
Packit |
90a5c9 |
rv = send_brigade_nonblocking(net->client_socket, bb,
|
|
Packit |
90a5c9 |
&(ctx->bytes_written), c);
|
|
Packit |
90a5c9 |
if ((rv != APR_SUCCESS) && (!APR_STATUS_IS_EAGAIN(rv))) {
|
|
Packit |
90a5c9 |
/* The client has aborted the connection */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c,
|
|
Packit |
90a5c9 |
"core_output_filter: writing data to the network");
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(bb);
|
|
Packit |
90a5c9 |
c->aborted = 1;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (loglevel >= APLOG_TRACE8) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
|
|
Packit |
90a5c9 |
"tried nonblocking write, total bytes "
|
|
Packit |
90a5c9 |
"written: %" APR_SIZE_T_FMT,
|
|
Packit |
90a5c9 |
ctx->bytes_written);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
setaside_remaining_output(f, ctx, bb, c);
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* This function assumes that either ctx->buffered_bb == NULL, or
|
|
Packit |
90a5c9 |
* ctx->buffered_bb is empty, or ctx->buffered_bb == bb
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static void setaside_remaining_output(ap_filter_t *f,
|
|
Packit |
90a5c9 |
core_output_filter_ctx_t *ctx,
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb,
|
|
Packit |
90a5c9 |
conn_rec *c)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (bb == NULL) {
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
remove_empty_buckets(bb);
|
|
Packit |
90a5c9 |
if (!APR_BRIGADE_EMPTY(bb)) {
|
|
Packit |
90a5c9 |
c->data_in_output_filters = 1;
|
|
Packit |
90a5c9 |
if (bb != ctx->buffered_bb) {
|
|
Packit |
90a5c9 |
if (!ctx->deferred_write_pool) {
|
|
Packit |
90a5c9 |
apr_pool_create(&ctx->deferred_write_pool, c->pool);
|
|
Packit |
90a5c9 |
apr_pool_tag(ctx->deferred_write_pool, "deferred_write");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_save_brigade(f, &(ctx->buffered_bb), &bb,
|
|
Packit |
90a5c9 |
ctx->deferred_write_pool);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (ctx->deferred_write_pool) {
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* There are no more requests in the pipeline. We can just clear the
|
|
Packit |
90a5c9 |
* pool.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
apr_pool_clear(ctx->deferred_write_pool);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#ifndef APR_MAX_IOVEC_SIZE
|
|
Packit |
90a5c9 |
#define MAX_IOVEC_TO_WRITE 16
|
|
Packit |
90a5c9 |
#else
|
|
Packit |
90a5c9 |
#if APR_MAX_IOVEC_SIZE > 16
|
|
Packit |
90a5c9 |
#define MAX_IOVEC_TO_WRITE 16
|
|
Packit |
90a5c9 |
#else
|
|
Packit |
90a5c9 |
#define MAX_IOVEC_TO_WRITE APR_MAX_IOVEC_SIZE
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb,
|
|
Packit |
90a5c9 |
apr_size_t *bytes_written,
|
|
Packit |
90a5c9 |
conn_rec *c)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_bucket *bucket, *next;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
struct iovec vec[MAX_IOVEC_TO_WRITE];
|
|
Packit |
90a5c9 |
apr_size_t nvec = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
remove_empty_buckets(bb);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (bucket = APR_BRIGADE_FIRST(bb);
|
|
Packit |
90a5c9 |
bucket != APR_BRIGADE_SENTINEL(bb);
|
|
Packit |
90a5c9 |
bucket = next) {
|
|
Packit |
90a5c9 |
next = APR_BUCKET_NEXT(bucket);
|
|
Packit |
90a5c9 |
#if APR_HAS_SENDFILE
|
|
Packit |
90a5c9 |
if (APR_BUCKET_IS_FILE(bucket)) {
|
|
Packit |
90a5c9 |
apr_bucket_file *file_bucket = (apr_bucket_file *)(bucket->data);
|
|
Packit |
90a5c9 |
apr_file_t *fd = file_bucket->fd;
|
|
Packit |
90a5c9 |
/* Use sendfile to send this file unless:
|
|
Packit |
90a5c9 |
* - the platform doesn't support sendfile,
|
|
Packit |
90a5c9 |
* - the file is too small for sendfile to be useful, or
|
|
Packit |
90a5c9 |
* - sendfile is disabled in the httpd config via "EnableSendfile off"
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((apr_file_flags_get(fd) & APR_SENDFILE_ENABLED) &&
|
|
Packit |
90a5c9 |
(bucket->length >= AP_MIN_SENDFILE_BYTES)) {
|
|
Packit |
90a5c9 |
if (nvec > 0) {
|
|
Packit |
90a5c9 |
(void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 1);
|
|
Packit |
90a5c9 |
rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
(void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 0);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
rv = sendfile_nonblocking(s, bucket, bytes_written, c);
|
|
Packit |
90a5c9 |
if (nvec > 0) {
|
|
Packit |
90a5c9 |
(void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 0);
|
|
Packit |
90a5c9 |
nvec = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif /* APR_HAS_SENDFILE */
|
|
Packit |
90a5c9 |
/* didn't sendfile */
|
|
Packit |
90a5c9 |
if (!APR_BUCKET_IS_METADATA(bucket)) {
|
|
Packit |
90a5c9 |
const char *data;
|
|
Packit |
90a5c9 |
apr_size_t length;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Non-blocking read first, in case this is a morphing
|
|
Packit |
90a5c9 |
* bucket type. */
|
|
Packit |
90a5c9 |
rv = apr_bucket_read(bucket, &data, &length, APR_NONBLOCK_READ);
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_EAGAIN(rv)) {
|
|
Packit |
90a5c9 |
/* Read would block; flush any pending data and retry. */
|
|
Packit |
90a5c9 |
if (nvec) {
|
|
Packit |
90a5c9 |
rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
|
|
Packit |
90a5c9 |
if (rv) {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
nvec = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = apr_bucket_read(bucket, &data, &length, APR_BLOCK_READ);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* reading may have split the bucket, so recompute next: */
|
|
Packit |
90a5c9 |
next = APR_BUCKET_NEXT(bucket);
|
|
Packit |
90a5c9 |
vec[nvec].iov_base = (char *)data;
|
|
Packit |
90a5c9 |
vec[nvec].iov_len = length;
|
|
Packit |
90a5c9 |
nvec++;
|
|
Packit |
90a5c9 |
if (nvec == MAX_IOVEC_TO_WRITE) {
|
|
Packit |
90a5c9 |
rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
|
|
Packit |
90a5c9 |
nvec = 0;
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (nvec > 0) {
|
|
Packit |
90a5c9 |
rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
remove_empty_buckets(bb);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void remove_empty_buckets(apr_bucket_brigade *bb)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_bucket *bucket;
|
|
Packit |
90a5c9 |
while (((bucket = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) &&
|
|
Packit |
90a5c9 |
(APR_BUCKET_IS_METADATA(bucket) || (bucket->length == 0))) {
|
|
Packit |
90a5c9 |
apr_bucket_delete(bucket);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t send_brigade_blocking(apr_socket_t *s,
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb,
|
|
Packit |
90a5c9 |
apr_size_t *bytes_written,
|
|
Packit |
90a5c9 |
conn_rec *c)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
while (!APR_BRIGADE_EMPTY(bb)) {
|
|
Packit |
90a5c9 |
rv = send_brigade_nonblocking(s, bb, bytes_written, c);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_EAGAIN(rv)) {
|
|
Packit |
90a5c9 |
/* Wait until we can send more data */
|
|
Packit |
90a5c9 |
apr_int32_t nsds;
|
|
Packit |
90a5c9 |
apr_interval_time_t timeout;
|
|
Packit |
90a5c9 |
apr_pollfd_t pollset;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
pollset.p = c->pool;
|
|
Packit |
90a5c9 |
pollset.desc_type = APR_POLL_SOCKET;
|
|
Packit |
90a5c9 |
pollset.reqevents = APR_POLLOUT;
|
|
Packit |
90a5c9 |
pollset.desc.s = s;
|
|
Packit |
90a5c9 |
apr_socket_timeout_get(s, &timeout);
|
|
Packit |
90a5c9 |
do {
|
|
Packit |
90a5c9 |
rv = apr_poll(&pollset, 1, &nsds, timeout);
|
|
Packit |
90a5c9 |
} while (APR_STATUS_IS_EINTR(rv));
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t writev_nonblocking(apr_socket_t *s,
|
|
Packit |
90a5c9 |
struct iovec *vec, apr_size_t nvec,
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb,
|
|
Packit |
90a5c9 |
apr_size_t *cumulative_bytes_written,
|
|
Packit |
90a5c9 |
conn_rec *c)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS, arv;
|
|
Packit |
90a5c9 |
apr_size_t bytes_written = 0, bytes_to_write = 0;
|
|
Packit |
90a5c9 |
apr_size_t i, offset;
|
|
Packit |
90a5c9 |
apr_interval_time_t old_timeout;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
arv = apr_socket_timeout_get(s, &old_timeout);
|
|
Packit |
90a5c9 |
if (arv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return arv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
arv = apr_socket_timeout_set(s, 0);
|
|
Packit |
90a5c9 |
if (arv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return arv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (i = 0; i < nvec; i++) {
|
|
Packit |
90a5c9 |
bytes_to_write += vec[i].iov_len;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
offset = 0;
|
|
Packit |
90a5c9 |
while (bytes_written < bytes_to_write) {
|
|
Packit |
90a5c9 |
apr_size_t n = 0;
|
|
Packit |
90a5c9 |
rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
|
|
Packit |
90a5c9 |
if (n > 0) {
|
|
Packit |
90a5c9 |
bytes_written += n;
|
|
Packit |
90a5c9 |
for (i = offset; i < nvec; ) {
|
|
Packit |
90a5c9 |
apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
|
|
Packit |
90a5c9 |
if (APR_BUCKET_IS_METADATA(bucket)) {
|
|
Packit |
90a5c9 |
apr_bucket_delete(bucket);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (n >= vec[i].iov_len) {
|
|
Packit |
90a5c9 |
apr_bucket_delete(bucket);
|
|
Packit |
90a5c9 |
offset++;
|
|
Packit |
90a5c9 |
n -= vec[i++].iov_len;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
apr_bucket_split(bucket, n);
|
|
Packit |
90a5c9 |
apr_bucket_delete(bucket);
|
|
Packit |
90a5c9 |
vec[i].iov_len -= n;
|
|
Packit |
90a5c9 |
vec[i].iov_base = (char *) vec[i].iov_base + n;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if ((ap__logio_add_bytes_out != NULL) && (bytes_written > 0)) {
|
|
Packit |
90a5c9 |
ap__logio_add_bytes_out(c, bytes_written);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*cumulative_bytes_written += bytes_written;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
arv = apr_socket_timeout_set(s, old_timeout);
|
|
Packit |
90a5c9 |
if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
|
|
Packit |
90a5c9 |
return arv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#if APR_HAS_SENDFILE
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t sendfile_nonblocking(apr_socket_t *s,
|
|
Packit |
90a5c9 |
apr_bucket *bucket,
|
|
Packit |
90a5c9 |
apr_size_t *cumulative_bytes_written,
|
|
Packit |
90a5c9 |
conn_rec *c)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
apr_bucket_file *file_bucket;
|
|
Packit |
90a5c9 |
apr_file_t *fd;
|
|
Packit |
90a5c9 |
apr_size_t file_length;
|
|
Packit |
90a5c9 |
apr_off_t file_offset;
|
|
Packit |
90a5c9 |
apr_size_t bytes_written = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!APR_BUCKET_IS_FILE(bucket)) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server, APLOGNO(00006)
|
|
Packit |
90a5c9 |
"core_filter: sendfile_nonblocking: "
|
|
Packit |
90a5c9 |
"this should never happen");
|
|
Packit |
90a5c9 |
return APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
file_bucket = (apr_bucket_file *)(bucket->data);
|
|
Packit |
90a5c9 |
fd = file_bucket->fd;
|
|
Packit |
90a5c9 |
file_length = bucket->length;
|
|
Packit |
90a5c9 |
file_offset = bucket->start;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (bytes_written < file_length) {
|
|
Packit |
90a5c9 |
apr_size_t n = file_length - bytes_written;
|
|
Packit |
90a5c9 |
apr_status_t arv;
|
|
Packit |
90a5c9 |
apr_interval_time_t old_timeout;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
arv = apr_socket_timeout_get(s, &old_timeout);
|
|
Packit |
90a5c9 |
if (arv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return arv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
arv = apr_socket_timeout_set(s, 0);
|
|
Packit |
90a5c9 |
if (arv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return arv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
rv = apr_socket_sendfile(s, fd, NULL, &file_offset, &n, 0);
|
|
Packit |
90a5c9 |
if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
bytes_written += n;
|
|
Packit |
90a5c9 |
file_offset += n;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
arv = apr_socket_timeout_set(s, old_timeout);
|
|
Packit |
90a5c9 |
if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
|
|
Packit |
90a5c9 |
rv = arv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if ((ap__logio_add_bytes_out != NULL) && (bytes_written > 0)) {
|
|
Packit |
90a5c9 |
ap__logio_add_bytes_out(c, bytes_written);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*cumulative_bytes_written += bytes_written;
|
|
Packit |
90a5c9 |
if ((bytes_written < file_length) && (bytes_written > 0)) {
|
|
Packit |
90a5c9 |
apr_bucket_split(bucket, bytes_written);
|
|
Packit |
90a5c9 |
apr_bucket_delete(bucket);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (bytes_written == file_length) {
|
|
Packit |
90a5c9 |
apr_bucket_delete(bucket);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#endif
|