Return-Path: Delivered-To: apmail-new-httpd-archive@apache.org Received: (qmail 40441 invoked by uid 500); 22 Jan 2001 02:03:37 -0000 Mailing-List: contact new-httpd-help@apache.org; run by ezmlm Precedence: bulk Reply-To: new-httpd@apache.org list-help: list-unsubscribe: list-post: Delivered-To: mailing list new-httpd@apache.org Received: (qmail 40420 invoked from network); 22 Jan 2001 02:03:34 -0000 X-Authentication-Warning: kurgan.lyra.org: gstein set sender to gstein@lyra.org using -f Date: Sun, 21 Jan 2001 18:04:04 -0800 From: Greg Stein To: new-httpd@apache.org Subject: [PATCH] updated ap_r* patch Message-ID: <20010121180404.T704@lyra.org> Mail-Followup-To: new-httpd@apache.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="/04w6evG8XlLl3ft" Content-Disposition: inline User-Agent: Mutt/1.2i X-URL: http://www.lyra.org/greg/ X-Spam-Rating: h31.sny.collab.net 1.6.2 0/1000/N --/04w6evG8XlLl3ft Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Attached is an updated version of my patch to fix ap_r* performance. - fixed bug found by Doug - small tweak for clarify from Greg Marr - updated against most recent CVS Cheers, -g -- Greg Stein, http://www.lyra.org/ --/04w6evG8XlLl3ft Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="output.patch" Index: include/http_protocol.h =================================================================== RCS file: /home/cvs/httpd-2.0/include/http_protocol.h,v retrieving revision 1.43 diff -u -r1.43 http_protocol.h --- include/http_protocol.h 2001/01/19 07:04:12 1.43 +++ include/http_protocol.h 2001/01/22 02:01:14 @@ -107,6 +107,7 @@ 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 */ Index: modules/http/http_core.c =================================================================== RCS file: /home/cvs/httpd-2.0/modules/http/http_core.c,v retrieving revision 1.240 diff -u -r1.240 http_core.c --- modules/http/http_core.c 2001/01/21 22:14:15 1.240 +++ modules/http/http_core.c 2001/01/22 02:01:43 @@ -3547,6 +3547,8 @@ 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 = { Index: modules/http/http_protocol.c =================================================================== RCS file: /home/cvs/httpd-2.0/modules/http/http_protocol.c,v retrieving revision 1.268 diff -u -r1.268 http_protocol.c --- modules/http/http_protocol.c 2001/01/19 07:04:21 1.268 +++ modules/http/http_protocol.c 2001/01/22 02:02:08 @@ -2996,28 +2996,146 @@ } #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) @@ -3025,50 +3143,36 @@ 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; + + /* ### fix this mechanism to allow more than 4K of output */ + written = apr_vsnprintf(buf, sizeof(buf), fmt, va); + (void) buffer_output(r, buf, written); - bb = apr_brigade_create(r->pool); - written = apr_brigade_vprintf(bb, fmt, va); - if (written != 0) - ap_pass_brigade(r->output_filters, bb); 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; @@ -3086,24 +3190,37 @@ 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; --/04w6evG8XlLl3ft--