From: Greg Stein Date: Wed, 24 Jan 2001 01:31:00 +0000 (+0000) Subject: Improve the performance of the ap_r* functions by buffering their data in X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5cfdcb3f847c29f249cb6af1baf7da613e51c5ea;p=apache Improve the performance of the ap_r* functions by buffering their data in [the context of] a new filter ("OLD_WRITE"). Further information/discussion of this patch is available on new-httpd between Jan 16 and Jan 23, 2001. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@87804 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/include/http_protocol.h b/include/http_protocol.h index 0d76090057..9347ecaee9 100644 --- a/include/http_protocol.h +++ b/include/http_protocol.h @@ -107,6 +107,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, apr_buc AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, apr_bucket_brigade *b); AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *, apr_bucket_brigade *); +AP_CORE_DECLARE_NONSTD(apr_status_t) ap_old_write_filter(ap_filter_t *f, apr_bucket_brigade *b); /* Send the response to special method requests */ diff --git a/modules/http/http_core.c b/modules/http/http_core.c index aaa64f9065..b7e6e20e38 100644 --- a/modules/http/http_core.c +++ b/modules/http/http_core.c @@ -3562,6 +3562,8 @@ static void register_hooks(apr_pool_t *p) AP_FTYPE_CONTENT); ap_register_output_filter("CHUNK", chunk_filter, AP_FTYPE_TRANSCODE); ap_register_output_filter("COALESCE", coalesce_filter, AP_FTYPE_CONTENT); + ap_register_output_filter("OLD_WRITE", ap_old_write_filter, + AP_FTYPE_CONTENT - 1); } AP_DECLARE_DATA module core_module = { diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c index c0b8da5c09..f1cade777c 100644 --- a/modules/http/http_protocol.c +++ b/modules/http/http_protocol.c @@ -2945,28 +2945,146 @@ AP_DECLARE(size_t) ap_send_mmap(apr_mmap_t *mm, request_rec *r, size_t offset, } #endif /* APR_HAS_MMAP */ +typedef struct { + char *buf; + char *cur; + apr_size_t avail; +} old_write_filter_ctx; + +AP_CORE_DECLARE_NONSTD(apr_status_t) ap_old_write_filter( + ap_filter_t *f, apr_bucket_brigade *bb) +{ + old_write_filter_ctx *ctx = f->ctx; + + AP_DEBUG_ASSERT(ctx); + + if (ctx->buf != NULL) { + apr_size_t nbyte = ctx->cur - ctx->buf; + + if (nbyte != 0) { + /* whatever is coming down the pipe (we don't care), we + can simply insert our buffered data at the front and + pass the whole bundle down the chain. */ + apr_bucket *b = apr_bucket_create_heap(ctx->buf, nbyte, 0, NULL); + APR_BRIGADE_INSERT_HEAD(bb, b); + ctx->buf = NULL; + } + } + + return ap_pass_brigade(f->next, bb); +} + +static apr_status_t flush_buffer(request_rec *r, old_write_filter_ctx *ctx, + const char *extra, apr_size_t extra_len) +{ + apr_bucket_brigade *bb = apr_brigade_create(r->pool); + apr_size_t nbyte = ctx->cur - ctx->buf; + apr_bucket *b = apr_bucket_create_heap(ctx->buf, nbyte, 0, NULL); + + APR_BRIGADE_INSERT_TAIL(bb, b); + ctx->buf = NULL; + + /* if there is extra data, then send that, too */ + if (extra != NULL) { + b = apr_bucket_create_transient(extra, extra_len); + APR_BRIGADE_INSERT_TAIL(bb, b); + } + + return ap_pass_brigade(r->output_filters, bb); +} + +static apr_status_t buffer_output(request_rec *r, + const char *str, apr_size_t len) +{ + ap_filter_t *f; + old_write_filter_ctx *ctx; + apr_size_t amt; + apr_status_t status; + + if (len == 0) + return APR_SUCCESS; + + /* ### future optimization: record some flags in the request_rec to + ### say whether we've added our filter, and whether it is first. */ + + /* this will typically exit on the first test */ + for (f = r->output_filters; f != NULL; f = f->next) + if (strcmp("OLD_WRITE", f->frec->name) == 0) + break; + if (f == NULL) { + /* our filter hasn't been added yet */ + ctx = apr_pcalloc(r->pool, sizeof(*ctx)); + ap_add_output_filter("OLD_WRITE", ctx, r, r->connection); + } + + /* if the first filter is not our buffering filter, then we have to + deliver the content through the normal filter chain */ + if (strcmp("OLD_WRITE", r->output_filters->frec->name) != 0) { + apr_bucket_brigade *bb = apr_brigade_create(r->pool); + apr_bucket *b = apr_bucket_create_transient(str, len); + APR_BRIGADE_INSERT_TAIL(bb, b); + + return ap_pass_brigade(r->output_filters, bb); + } + + /* grab the context from our filter */ + ctx = r->output_filters->ctx; + + /* if there isn't a buffer in the context yet, put one there. */ + if (ctx->buf == NULL) { + /* use the heap so it will get free'd after being flushed */ + ctx->avail = AP_MIN_BYTES_TO_WRITE; + ctx->buf = ctx->cur = malloc(ctx->avail); + } + + /* squeeze the data into the existing buffer */ + if (len <= ctx->avail) { + memcpy(ctx->cur, str, len); + ctx->cur += len; + if ((ctx->avail -= len) == 0) + return flush_buffer(r, ctx, NULL, 0); + return APR_SUCCESS; + } + + /* the new content can't fit in the existing buffer */ + + if (len >= AP_MIN_BYTES_TO_WRITE) { + /* it is really big. send what we have, and the new stuff. */ + return flush_buffer(r, ctx, str, len); + } + + /* the new data is small. put some into the current buffer, flush it, + and then drop the remaining into a new buffer. */ + amt = ctx->avail; + memcpy(ctx->cur, str, amt); + ctx->cur += amt; + ctx->avail = 0; + if ((status = flush_buffer(r, ctx, NULL, 0)) != APR_SUCCESS) + return status; + + ctx->buf = malloc(AP_MIN_BYTES_TO_WRITE); + memcpy(ctx->buf, str + amt, len - amt); + ctx->cur = ctx->buf + (len - amt); + ctx->avail = AP_MIN_BYTES_TO_WRITE - (len - amt); + + return APR_SUCCESS; +} + AP_DECLARE(int) ap_rputc(int c, request_rec *r) { - apr_bucket_brigade *bb = NULL; - apr_bucket *b; char c2 = (char)c; if (r->connection->aborted) { return EOF; } - bb = apr_brigade_create(r->pool); - b = apr_bucket_create_transient(&c2, 1); - APR_BRIGADE_INSERT_TAIL(bb, b); - ap_pass_brigade(r->output_filters, bb); + (void) buffer_output(r, &c2, 1); return c; } AP_DECLARE(int) ap_rputs(const char *str, request_rec *r) { - apr_bucket_brigade *bb = NULL; - apr_bucket *b; apr_size_t len; if (r->connection->aborted) @@ -2974,50 +3092,36 @@ AP_DECLARE(int) ap_rputs(const char *str, request_rec *r) if (*str == '\0') return 0; - len = strlen(str); - bb = apr_brigade_create(r->pool); - b = apr_bucket_create_transient(str, len); - APR_BRIGADE_INSERT_TAIL(bb, b); - ap_pass_brigade(r->output_filters, bb); + (void) buffer_output(r, str, len = strlen(str)); return len; } AP_DECLARE(int) ap_rwrite(const void *buf, int nbyte, request_rec *r) { - apr_bucket_brigade *bb = NULL; - apr_bucket *b; - if (r->connection->aborted) return EOF; - if (nbyte == 0) - return 0; - bb = apr_brigade_create(r->pool); - b = apr_bucket_create_transient(buf, nbyte); - APR_BRIGADE_INSERT_TAIL(bb, b); - ap_pass_brigade(r->output_filters, bb); + (void) buffer_output(r, buf, nbyte); + return nbyte; } AP_DECLARE(int) ap_vrprintf(request_rec *r, const char *fmt, va_list va) { - apr_bucket_brigade *bb = NULL; + char buf[4096]; apr_size_t written; if (r->connection->aborted) return EOF; - bb = apr_brigade_create(r->pool); - written = apr_brigade_vprintf(bb, fmt, va); - if (written != 0) - ap_pass_brigade(r->output_filters, bb); + /* ### fix this mechanism to allow more than 4K of output */ + written = apr_vsnprintf(buf, sizeof(buf), fmt, va); + (void) buffer_output(r, buf, written); + return written; } -/* TODO: Make ap pa_bucket_vprintf that printfs directly into a - * bucket. - */ AP_DECLARE_NONSTD(int) ap_rprintf(request_rec *r, const char *fmt, ...) { va_list va; @@ -3035,24 +3139,37 @@ AP_DECLARE_NONSTD(int) ap_rprintf(request_rec *r, const char *fmt, ...) AP_DECLARE_NONSTD(int) ap_rvputs(request_rec *r, ...) { - apr_bucket_brigade *bb = NULL; - apr_size_t written; va_list va; + const char *s; + apr_size_t len; + apr_size_t written; if (r->connection->aborted) return EOF; - bb = apr_brigade_create(r->pool); + + /* ### TODO: if the total output is large, put all the strings + ### into a single brigade, rather than flushing each time we + ### fill the buffer */ va_start(va, r); - written = apr_brigade_vputstrs(bb, va); + while (1) { + s = va_arg(va, const char *); + if (s == NULL) + break; + + len = strlen(s); + if (buffer_output(r, s, len) != APR_SUCCESS) { + return -1; + } + + written += len; + } va_end(va); - if (written != 0) - ap_pass_brigade(r->output_filters, bb); + return written; } AP_DECLARE(int) ap_rflush(request_rec *r) { - /* we should be using a flush bucket to flush the stack, not buff code. */ apr_bucket_brigade *bb; apr_bucket *b;