From: Greg Stein Date: Tue, 3 Jun 2003 22:09:24 +0000 (+0000) Subject: mod_dav improvement: make dav_method_propfind stream its response, X-Git-Tag: pre_ajp_proxy~1572 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=97a278f1b316b69cae7e3331671b47b460ec051e;p=apache mod_dav improvement: make dav_method_propfind stream its response, rather than cache every object and send the whole 207 at once. Note: this patch doesn't affect the mod_dav provider API at all. Providers still return property results in text-buffers, but mod_dav then streams them out immediately. Submitted by: Ben Collins-Sussman Reviewed by: gstein, jerenkrantz, sander * mod_dav.h (dav_walker_ctx): add a brigade field and a scratchpool field. * mod_dav.c (dav_send_one_response): new helper function to write a into a brigade/filter. this code has been factorized out of dav_send_multistatus. (dav_begin_multistatus): new factorized helper func; creates brigade and sends initial tag. (dav_send_multistatus): create brigade, call dav_begin_multistatus, and switch all ap_rputs calls to ap_fputs instead. call dav_send_one_response when looping over response list. use a subpool when iterating. (dav_method_propfind): initialize walker ctx's brigade. initialize ctx's scratchpool as a subpool of r->pool. Send a tag before calling the provider's walk() function, and a tag afterwards. (dav_stream_response): new function, originally based on dav_add_repsonse. don't build linked list of responses in memory; just spew each response object into the brigade via dav_send_one_response(). take an incoming pool argument to do the temporary allocation and streaming. (dav_propfind_walker): pass ctx->scratchpool to dav_stream_response, and clear the pool when finished. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@100164 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/dav/main/mod_dav.c b/modules/dav/main/mod_dav.c index 8be2c68ba9..affc4ed049 100644 --- a/modules/dav/main/mod_dav.c +++ b/modules/dav/main/mod_dav.c @@ -453,78 +453,121 @@ static const char *dav_xml_escape_uri(apr_pool_t *p, const char *uri) return apr_xml_quote_string(p, e_uri, 0); } -static void dav_send_multistatus(request_rec *r, int status, - dav_response *first, - apr_array_header_t *namespaces) + +/* Write a complete RESPONSE object out as a xml + element. Data is sent into brigade BB, which is auto-flushed into + OUTPUT filter stack. Use POOL for any temporary allocations. + + [Presumably the tag has already been written; this + routine is shared by dav_send_multistatus and dav_stream_response.] +*/ +static void dav_send_one_response(dav_response *response, + apr_bucket_brigade *bb, + ap_filter_t *output, + apr_pool_t *pool) +{ + apr_text *t = NULL; + + if (response->propresult.xmlns == NULL) { + ap_fputs(output, bb, ""); + } + else { + ap_fputs(output, bb, "propresult.xmlns; t; t = t->next) { + ap_fputs(output, bb, t->text); + } + ap_fputc(output, bb, '>'); + } + + ap_fputstrs(output, bb, + DEBUG_CR "", + dav_xml_escape_uri(pool, response->href), + "" DEBUG_CR, + NULL); + + if (response->propresult.propstats == NULL) { + /* use the Status-Line text from Apache. Note, this will + * default to 500 Internal Server Error if first->status + * is not a known (or valid) status code. + */ + ap_fputstrs(output, bb, + "HTTP/1.1 ", + ap_get_status_line(response->status), + "" DEBUG_CR, + NULL); + } + else { + /* assume this includes and is quoted properly */ + for (t = response->propresult.propstats; t; t = t->next) { + ap_fputs(output, bb, t->text); + } + } + + if (response->desc != NULL) { + /* + * We supply the description, so we know it doesn't have to + * have any escaping/encoding applied to it. + */ + ap_fputstrs(output, bb, + "", + response->desc, + "" DEBUG_CR, + NULL); + } + + ap_fputs(output, bb, "" DEBUG_CR); +} + + +/* Factorized helper function: prep request_rec R for a multistatus + response and write tag into BB, destined for + R->output_filters. Use xml NAMESPACES in initial tag, if + non-NULL. */ +static void dav_begin_multistatus(apr_bucket_brigade *bb, + request_rec *r, int status, + apr_array_header_t *namespaces) { /* Set the correct status and Content-Type */ r->status = status; ap_set_content_type(r, DAV_XML_CONTENT_TYPE); /* Send the headers and actual multistatus response now... */ - ap_rputs(DAV_XML_HEADER DEBUG_CR - "output_filters, bb, DAV_XML_HEADER DEBUG_CR + "nelts; i--; ) { - ap_rprintf(r, " xmlns:ns%d=\"%s\"", i, + ap_fprintf(r->output_filters, bb, " xmlns:ns%d=\"%s\"", i, APR_XML_GET_URI_ITEM(namespaces, i)); } } - /* ap_rputc('>', r); */ - ap_rputs(">" DEBUG_CR, r); - - for (; first != NULL; first = first->next) { - apr_text *t; + ap_fputs(r->output_filters, bb, ">" DEBUG_CR); +} - if (first->propresult.xmlns == NULL) { - ap_rputs("", r); - } - else { - ap_rputs("propresult.xmlns; t; t = t->next) { - ap_rputs(t->text, r); - } - ap_rputc('>', r); - } - ap_rputs(DEBUG_CR "", r); - ap_rputs(dav_xml_escape_uri(r->pool, first->href), r); - ap_rputs("" DEBUG_CR, r); +static void dav_send_multistatus(request_rec *r, int status, + dav_response *first, + apr_array_header_t *namespaces) +{ + apr_pool_t *subpool; + apr_bucket_brigade *bb = apr_brigade_create(r->pool, + r->connection->bucket_alloc); - if (first->propresult.propstats == NULL) { - /* use the Status-Line text from Apache. Note, this will - * default to 500 Internal Server Error if first->status - * is not a known (or valid) status code. - */ - ap_rprintf(r, - "HTTP/1.1 %s" DEBUG_CR, - ap_get_status_line(first->status)); - } - else { - /* assume this includes and is quoted properly */ - for (t = first->propresult.propstats; t; t = t->next) { - ap_rputs(t->text, r); - } - } + dav_begin_multistatus(bb, r, status, namespaces); - if (first->desc != NULL) { - /* - * We supply the description, so we know it doesn't have to - * have any escaping/encoding applied to it. - */ - ap_rputs("", r); - ap_rputs(first->desc, r); - ap_rputs("" DEBUG_CR, r); - } + apr_pool_create(&subpool, r->pool); - ap_rputs("" DEBUG_CR, r); + for (; first != NULL; first = first->next) { + apr_pool_clear(subpool); + dav_send_one_response(first, bb, r->output_filters, subpool); } + apr_pool_destroy(subpool); - ap_rputs("" DEBUG_CR, r); + ap_fputs(r->output_filters, bb, "" DEBUG_CR); + ap_filter_flush(bb, r->output_filters); } /* @@ -1048,6 +1091,27 @@ static int dav_method_put(request_rec *r) return dav_created(r, NULL, "Resource", resource_state == DAV_RESOURCE_EXISTS); } + +/* Use POOL to temporarily construct a dav_response object (from WRES + STATUS, and PROPSTATS) and stream it via WRES's ctx->brigade. */ +static void dav_stream_response(dav_walk_resource *wres, + int status, + dav_get_props_result *propstats, + apr_pool_t *pool) +{ + dav_response resp = { 0 }; + dav_walker_ctx *ctx = wres->walk_ctx; + + resp.href = wres->resource->uri; + resp.status = status; + if (propstats) { + resp.propresult = *propstats; + } + + dav_send_one_response(&resp, ctx->bb, ctx->r->output_filters, pool); +} + + /* ### move this to dav_util? */ DAV_DECLARE(void) dav_add_response(dav_walk_resource *wres, int status, dav_get_props_result *propstats) @@ -1066,6 +1130,7 @@ DAV_DECLARE(void) dav_add_response(dav_walk_resource *wres, wres->response = resp; } + /* handle the DELETE method */ static int dav_method_delete(request_rec *r) { @@ -1837,12 +1902,14 @@ static dav_error * dav_propfind_walker(dav_walk_resource *wres, int calltype) /* some props were expected on this collection/resource */ dav_cache_badprops(ctx); badprops.propstats = ctx->propstat_404; - dav_add_response(wres, 0, &badprops); + dav_stream_response(wres, 0, &badprops, ctx->scratchpool); } else { /* no props on this collection/resource */ - dav_add_response(wres, HTTP_OK, NULL); + dav_stream_response(wres, HTTP_OK, NULL, ctx->scratchpool); } + + apr_pool_clear(ctx->scratchpool); return NULL; } /* ### what to do about closing the propdb on server failure? */ @@ -1858,7 +1925,13 @@ static dav_error * dav_propfind_walker(dav_walk_resource *wres, int calltype) } dav_close_propdb(propdb); - dav_add_response(wres, 0, &propstats); + dav_stream_response(wres, 0, &propstats, ctx->scratchpool); + + /* at this point, ctx->scratchpool has been used to stream a + single response. this function fully controls the pool, and + thus has the right to clear it for the next iteration of this + callback. */ + apr_pool_clear(ctx->scratchpool); return NULL; } @@ -1951,6 +2024,8 @@ static int dav_method_propfind(request_rec *r) ctx.doc = doc; ctx.r = r; + ctx.bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + apr_pool_create(&ctx.scratchpool, r->pool); /* ### should open read-only */ if ((err = dav_open_lockdb(r, 0, &ctx.w.lockdb)) != NULL) { @@ -1966,6 +2041,17 @@ static int dav_method_propfind(request_rec *r) ctx.w.walk_type |= DAV_WALKTYPE_LOCKNULL; } + /* send tag, with all doc->namespaces attached. */ + + /* NOTE: we *cannot* leave out the doc's namespaces from the + initial tag. if a 404 was generated for an HREF, + then we need to spit out the doc's namespaces for use by the + 404. Note that elements will override these ns0, + ns1, etc, but NOT within the scope for the + badprops. */ + dav_begin_multistatus(ctx.bb, r, HTTP_MULTI_STATUS, doc->namespaces); + + /* Have the provider walk the resource. */ err = (*resource->hooks->walk)(&ctx.w, depth, &multi_status); if (ctx.w.lockdb != NULL) { @@ -1977,20 +2063,9 @@ static int dav_method_propfind(request_rec *r) return dav_handle_err(r, err, NULL); } - /* return a 207 (Multi-Status) response now. */ - - /* if a 404 was generated for an HREF, then we need to spit out the - * doc's namespaces for use by the 404. Note that elements - * will override these ns0, ns1, etc, but NOT within the - * scope for the badprops. */ - /* NOTE: propstat_404 != NULL implies doc != NULL */ - if (ctx.propstat_404 != NULL) { - dav_send_multistatus(r, HTTP_MULTI_STATUS, multi_status, - doc->namespaces); - } - else { - dav_send_multistatus(r, HTTP_MULTI_STATUS, multi_status, NULL); - } + /* Finish up the multistatus response. */ + ap_fputs(r->output_filters, ctx.bb, "" DEBUG_CR); + ap_filter_flush(ctx.bb, r->output_filters); /* the response has been sent. */ return DONE; diff --git a/modules/dav/main/mod_dav.h b/modules/dav/main/mod_dav.h index 3f6893197f..c8737168cd 100644 --- a/modules/dav/main/mod_dav.h +++ b/modules/dav/main/mod_dav.h @@ -1671,6 +1671,12 @@ typedef struct dav_walker_ctx /* ### client data... phasing out this big glom */ + /* this brigade buffers data being sent to r->output_filters */ + apr_bucket_brigade *bb; + + /* a scratch pool, used to stream responses and iteratively cleared. */ + apr_pool_t *scratchpool; + request_rec *r; /* original request */ /* for PROPFIND operations */