Return-Path: Delivered-To: apmail-httpd-apreq-dev-archive@www.apache.org Received: (qmail 45941 invoked from network); 9 Mar 2005 23:13:36 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur-2.apache.org with SMTP; 9 Mar 2005 23:13:36 -0000 Received: (qmail 33129 invoked by uid 500); 9 Mar 2005 23:13:36 -0000 Delivered-To: apmail-httpd-apreq-dev-archive@httpd.apache.org Received: (qmail 33098 invoked by uid 500); 9 Mar 2005 23:13:36 -0000 Mailing-List: contact apreq-dev-help@httpd.apache.org; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: Delivered-To: mailing list apreq-dev@httpd.apache.org Received: (qmail 33085 invoked by uid 99); 9 Mar 2005 23:13:36 -0000 X-ASF-Spam-Status: No, hits=0.1 required=10.0 tests=FORGED_RCVD_HELO X-Spam-Check-By: apache.org Received-SPF: pass (hermes.apache.org: local policy) Received: from duempel.org (HELO swift.roonstrasse.net) (81.209.165.42) by apache.org (qpsmtpd/0.28) with SMTP; Wed, 09 Mar 2005 15:13:34 -0800 Received: (qmail 32275 invoked by uid 1001); 9 Mar 2005 23:13:08 -0000 Date: Thu, 10 Mar 2005 00:13:08 +0100 From: Max Kellermann To: apreq-dev@httpd.apache.org Subject: [PATCH multi-env] optimizations, API documentation Message-ID: <20050309231308.GA32211@roonstrasse.net> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="G4iJoqBmSsgzjUCe" Content-Disposition: inline User-Agent: Mutt/1.5.6+20040907i X-Virus-Checked: Checked X-Spam-Rating: minotaur-2.apache.org 1.6.2 0/1000/N --G4iJoqBmSsgzjUCe Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 01-minor_optimizations.patch: - several tiny optimizations - apreq_join() does not need a second n==0 check - in apreq_fwritev(), the loop can stop as soon as bytes_avail>len, as we don't use the exact bytes_avail value 02-api_docs.patch - update doxygen.conf.in with new directory names - many many API documentation updates (not complete yet) - please check my English, it's not my mother tongue ;) Max --G4iJoqBmSsgzjUCe Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="01-minor_optimizations.patch" Wed Mar 9 21:29:33 CET 2005 max@duempel.org * several minor optimizations diff -rN -u old-apreq2/library/param.c new-apreq2/library/param.c --- old-apreq2/library/param.c 2005-03-10 00:08:46.198011184 +0100 +++ new-apreq2/library/param.c 2005-03-09 21:29:54.000000000 +0100 @@ -102,7 +102,7 @@ *(const apreq_value_t **)&v = &p->v; if (vlen > 0) { - status = apreq_decode(v->data, &vlen, word + nlen + 1, vlen); + status = apreq_decode(v->data, &v->dlen, word + nlen + 1, vlen); if (status != APR_SUCCESS) { *param = NULL; return status; @@ -113,13 +113,12 @@ } v->name = v->data + vlen + 1; - status = apreq_decode(v->name, &nlen, word, nlen); + status = apreq_decode(v->name, &v->nlen, word, nlen); if (status != APR_SUCCESS) { *param = NULL; return status; } - v->nlen = nlen; - v->dlen = vlen; + *param = p; return APR_SUCCESS; diff -rN -u old-apreq2/library/util.c new-apreq2/library/util.c --- old-apreq2/library/util.c 2005-03-10 00:08:46.195011640 +0100 +++ new-apreq2/library/util.c 2005-03-09 21:29:54.000000000 +0100 @@ -39,9 +39,12 @@ while (apr_isspace(*p)) ++p; - switch (apr_tolower(*p)) { + switch (*p) { + case 'G': /* fall thru */ case 'g': return n * 1024*1024*1024; + case 'M': /* fall thru */ case 'm': return n * 1024*1024; + case 'K': /* fall thru */ case 'k': return n * 1024; } @@ -428,9 +431,6 @@ rv = apr_palloc(p, len); - if (n == 0) - return rv; - /* Pass two --- copy the argument strings into the result space */ d = rv; @@ -510,7 +510,7 @@ if (s != APR_SUCCESS) return s; - while (--n >= 0) + while (--n >= 0 && bytes_avail <= len) bytes_avail += v[n].iov_len; @@ -525,11 +525,8 @@ v[n].iov_base = (char *)(v[n].iov_base) + len; if (n > 0) { - struct iovec *dest = v; - do { - *dest++ = v[n++]; - } while (n < *nelts); - *nelts = dest - v; + (*nelts) -= n; + memmove(v, v + n, sizeof(*v) * *nelts); } else { s = apreq_fwritev(f, v, nelts, &len); --G4iJoqBmSsgzjUCe Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="02-api_docs.patch" Wed Mar 9 23:59:26 CET 2005 max@duempel.org * big API doc update diff -rN -u old-apreq2/build/doxygen.conf.in new-apreq2/build/doxygen.conf.in --- old-apreq2/build/doxygen.conf.in 2005-03-10 00:08:48.865605648 +0100 +++ new-apreq2/build/doxygen.conf.in 2005-03-09 22:41:23.000000000 +0100 @@ -27,7 +27,7 @@ WARN_IF_DOC_ERROR = YES WARN_FORMAT = "$file:$line: $text" -INPUT = . src env +INPUT = . include library module/apache module/apache2 FILE_PATTERNS = *.c *.h *.dox CHANGES STATUS RECURSIVE = NO EXCLUDE = diff -rN -u old-apreq2/include/apreq_cookie.h new-apreq2/include/apreq_cookie.h --- old-apreq2/include/apreq_cookie.h 2005-03-10 00:08:48.881603216 +0100 +++ new-apreq2/include/apreq_cookie.h 2005-03-09 23:18:50.000000000 +0100 @@ -125,7 +125,14 @@ APREQ_FLAGS_OFF(c->flags, APREQ_TAINT); } - +/** + * Parse a cookie header and store the cookies in an apr_table_t. + * + * @param pool pool which allocates the cookies + * @param jar table where parsed cookies are stored + * @param header the header value + * @return APR_SUCCESS or an error code + */ APREQ_DECLARE(apr_status_t) apreq_parse_cookie_header(apr_pool_t *pool, apr_table_t *jar, const char *header); @@ -138,6 +145,7 @@ * @param nlen Length of name. * @param value The cookie's value. * @param vlen Length of value. + * @return the new cookie */ APREQ_DECLARE(apreq_cookie_t *) apreq_cookie_make(apr_pool_t *pool, const char *name, @@ -179,7 +187,7 @@ * attribute will appear in the cookie's serialized form. If time_str * is not NULL, the expiration date will be reset to the offset (from now) * represented by time_str. The time_str should be in a format that - * apreq_atoi64t() can understand, namely /[+-]?\d+\s*[YMDhms]/. + * apreq_atoi64t() can understand, namely /[+-]?\\d+\\s*[YMDhms]/. * * @remarks Now time_str may also be a fixed date; see apr_date_parse_rfc() * for admissible formats. diff -rN -u old-apreq2/include/apreq_module.h new-apreq2/include/apreq_module.h --- old-apreq2/include/apreq_module.h 2005-03-10 00:08:48.879603520 +0100 +++ new-apreq2/include/apreq_module.h 2005-03-09 23:44:16.000000000 +0100 @@ -26,11 +26,19 @@ #endif /** + * @file apreq_module.h + * @brief Module API + * @ingroup libapreq2 + */ + + +/** * An apreq handle associated with a module. The structure * may have variable size, because the module may append its own data * structures after it. */ typedef struct apreq_handle_t { + /** the apreq module which implements this handle */ const struct apreq_module_t *module; } apreq_handle_t; @@ -40,31 +48,50 @@ typedef struct apreq_module_t { + /** name of this apreq module */ const char *name; + /** magic number identifying the module and version */ apr_uint32_t magic_number; + /** get a table with all cookies */ apr_status_t (*jar)(apreq_handle_t *, const apr_table_t **); + /** get a table with all query string parameters */ apr_status_t (*args)(apreq_handle_t *, const apr_table_t **); + /** get a table with all body parameters */ apr_status_t (*body)(apreq_handle_t *, const apr_table_t **); + /** get a cookie by its name */ apreq_cookie_t *(*jar_get)(apreq_handle_t *, const char *); + /** get a query string parameter by its name */ apreq_param_t *(*args_get)(apreq_handle_t *, const char *); + /** get a body parameter by its name */ apreq_param_t *(*body_get)(apreq_handle_t *, const char *); + /** gets the parser associated with the request body */ apr_status_t (*parser_get)(apreq_handle_t *, const apreq_parser_t **); + /** manually set a parser for the request body */ apr_status_t (*parser_set)(apreq_handle_t *, apreq_parser_t *); + /** add a hook function */ apr_status_t (*hook_add)(apreq_handle_t *, apreq_hook_t *); + /** determine the maximum in-memory bytes a brigade may use */ apr_status_t (*brigade_limit_get)(apreq_handle_t *, apr_size_t *); + /** set the maximum in-memory bytes a brigade may use */ apr_status_t (*brigade_limit_set)(apreq_handle_t *, apr_size_t); + /** determine the maximum amount of data that will be fed into a parser */ apr_status_t (*read_limit_get)(apreq_handle_t *, apr_uint64_t *); + /** set the maximum amount of data that will be fed into a parser */ apr_status_t (*read_limit_set)(apreq_handle_t *, apr_uint64_t); + /** determine the directory used by the parser for temporary files */ apr_status_t (*temp_dir_get)(apreq_handle_t *, const char **); + /** set the directory used by the parser for temporary files */ apr_status_t (*temp_dir_set)(apreq_handle_t *, const char *); + /** get the value of a request header */ const char *(*header_in)(apreq_handle_t *,const char *); + /** send a response header */ apr_status_t (*header_out)(apreq_handle_t *, const char *,char *); } apreq_module_t; @@ -88,7 +115,7 @@ /** * Expose the parsed "cookie" header associated to this handle. - * @req + * @param req the apreq request handle * @arg t The resulting table, or which may point to NULL * when the return value is not APR_SUCCESS. Otherwise * it must point to a valid table object. @@ -103,7 +130,7 @@ /** * Expose the parsed "query string" associated to this handle. - * @req + * @param req the apreq request handle * @arg t The resulting table, or which may point to NULL * when the return value is not APR_SUCCESS. Otherwise * it must point to a valid table object. @@ -118,12 +145,13 @@ /** * Expose the parsed "request body" associated to this handle. - * @req + * @param req the apreq request handle * @arg t The resulting table, or which may point to NULL * when the return value is not APR_SUCCESS. Otherwise * it must point to a valid table object. * @return APR_SUCCESS on success. * @return APREQ_ERROR_NODATA if no request content is available. + * @remark Will parse the request as necessary. */ static APR_INLINE apr_status_t apreq_body(apreq_handle_t *req, const apr_table_t **t) @@ -134,7 +162,7 @@ /** * Fetch the first cookie with the given name. - * @req + * @param req the apreq request handle * @arg name Case-insensitive cookie name. * @return Desired cookie, or NULL if none match. */ @@ -146,7 +174,7 @@ /** * Fetch the first query string param with the given name. - * @req + * @param req the apreq request handle * @arg name Case-insensitive param name. * @return Desired param, or NULL if none match. */ @@ -158,9 +186,10 @@ /** * Fetch the first cookie with the given name. - * @req + * @param req the apreq request handle * @arg name Case-insensitive cookie name. * @return Desired cookie, or NULL if none match. + * @remark Will parse the request as necessary. */ static APR_INLINE apreq_param_t *apreq_body_get(apreq_handle_t *req, const char *name) @@ -170,7 +199,7 @@ /** * Fetch the active body parser. - * @req + * @param req the apreq request handle * @arg parser Points to the active parser on return. * @return Parser's current status. Use apreq_body * if you need its final status (the return values @@ -187,7 +216,7 @@ /** * Set the body parser for this request. - * @req + * @param req the apreq request handle * @arg parser New parser to use. * @return APR_SUCCESS if the action was succesful, error otherwise. */ @@ -200,7 +229,7 @@ /** * Add a parser hook for this request. - * @req + * @param req the apreq request handle * @arg hook Hook to add. * @return APR_SUCCESS if the action was succesful, error otherwise. */ @@ -214,7 +243,7 @@ /** * Fetch the header value (joined by ", " if there are multiple headers) * for a given header name. - * @req + * @param req the apreq request handle * @param name The header name. * @return The value of the header, NULL if not found. */ @@ -227,7 +256,7 @@ /** * Add a header field to the environment's outgoing response headers - * @req. + * @param req the apreq request handle * @param name The name of the outgoing header. * @param val Value of the outgoing header. * @return APR_SUCCESS on success, error code otherwise. @@ -289,7 +318,6 @@ * @param pre Prefix to define new environment. All attributes of * the apreq_env_module_t struct are defined with this as their prefix. The * generated struct is named by appending "_module" to the prefix. - * @param name Name of this environment. * @param mmn Magic number (i.e. version number) of this environment. */ #define APREQ_MODULE(pre, mmn) const apreq_module_t \ @@ -317,6 +345,7 @@ * @param cookie value of the request "Cookie" header * @param cookie2 value of the request "Cookie2" header * @param parser parser for handling the request body + * @param read_limit maximum number of bytes to read from the body * @param in a bucket brigade containing the request body */ APREQ_DECLARE(apreq_handle_t*) apreq_handle_custom(apr_pool_t *pool, @@ -331,7 +360,7 @@ * Add the cookie to the outgoing "Set-Cookie" headers. * * @param c The cookie. - * @req + * @param req the apreq request handle */ APREQ_DECLARE(apr_status_t) apreq_cookie_bake(const apreq_cookie_t *c, apreq_handle_t *req); @@ -340,6 +369,7 @@ * Add the cookie to the outgoing "Set-Cookie2" headers. * * @param c The cookie. + * @param req the apreq request handle */ APREQ_DECLARE(apr_status_t) apreq_cookie_bake2(const apreq_cookie_t *c, apreq_handle_t *req); @@ -347,13 +377,21 @@ /** * Looks for the presence of a "Cookie2" header to determine whether * or not the current User-Agent supports rfc2965. - * @req + * @param req the apreq request handle * @return APREQ_COOKIE_VERSION_RFC if rfc2965 is supported, * APREQ_COOKIE_VERSION_NETSCAPE otherwise. */ APREQ_DECLARE(unsigned)apreq_ua_cookie_version(apreq_handle_t *req); - +/** + * Find the first query string parameter or body parameter with the + * specified name. + * + * @param req the apreq request handle + * @param key the requested parameter name + * @return the parameter, or NULL if not found + * @remark Will parse the request as necessary. + */ APREQ_DECLARE(apreq_param_t *)apreq_param(apreq_handle_t *req, const char *key); #define apreq_cookie(req, name) apreq_jar_get(req, name) @@ -361,18 +399,25 @@ /** * Returns a table containing key-value pairs for the full request * (args + body). + * + * @param req the apreq request handle * @param p Allocates the returned table. - * @req * @remark Also parses the request if necessary. */ APREQ_DECLARE(apr_table_t *) apreq_params(apreq_handle_t *req, apr_pool_t *p); +/** + * Returns a table containing all request cookies. + * + * @param req the apreq request handle + * @param p Allocates the returned table. + */ APREQ_DECLARE(apr_table_t *)apreq_cookies(apreq_handle_t *req, apr_pool_t *p); /** * Force a complete parse. - * @req + * @param req the apreq request handle * @return APR_SUCCESS on an error-free parse of the request data. * Any other status code indicates a problem somewhere. * diff -rN -u old-apreq2/include/apreq_param.h new-apreq2/include/apreq_param.h --- old-apreq2/include/apreq_param.h 2005-03-10 00:08:48.875604128 +0100 +++ new-apreq2/include/apreq_param.h 2005-03-09 23:14:26.000000000 +0100 @@ -61,20 +61,20 @@ APREQ_FLAGS_OFF(p->flags, APREQ_TAINT); } -/** @return 1 if the taint flag is set, 0 otherwise. */ +/** @return 1 if the UTF-8 flag is set, 0 otherwise. */ static APR_INLINE unsigned apreq_param_is_utf8(const apreq_param_t *p) { return APREQ_CHARSET_UTF8 & APREQ_FLAGS_GET(p->flags, APREQ_CHARSET); } -/** Sets the tainted flag. */ +/** Sets the charset of this parameter to UTf-8. */ static APR_INLINE void apreq_param_utf8_on(apreq_param_t *p) { APREQ_FLAGS_SET(p->flags, APREQ_CHARSET, APREQ_CHARSET_UTF8); } -/** Turns off the taint flag. */ +/** Sets the charset of this parameter to 8 bit ASCII. */ static APR_INLINE void apreq_param_utf8_off(apreq_param_t *p) { APREQ_FLAGS_SET(p->flags, APREQ_CHARSET, APREQ_CHARSET_ASCII); @@ -103,6 +103,8 @@ /** * Url-decodes a name=value pair into a param. + * + * @param param points to the decoded parameter on success * @param pool Pool from which the param is allocated. * @param word Start of the name=value pair. * @param nlen Length of urlencoded name. @@ -146,8 +148,11 @@ * Returns an array of parameters (apreq_param_t *) matching the given key. * The key is case-insensitive. * @param p Allocates the returned array. - * @param req The current apreq_request_t object. - * @param key Null-terminated search key. key==NULL fetches all parameters. + * @param t the parameter table returned by apreq_args(), apreq_body() + * or apreq_params() + * @param key Null-terminated search key, case insensitive. + * key==NULL fetches all parameters. + * @return an array of apreq_param_t* (pointers) * @remark Also parses the request if necessary. */ APREQ_DECLARE(apr_array_header_t *) apreq_params_as_array(apr_pool_t *p, @@ -155,14 +160,15 @@ const char *key); /** - * Returns a ", " -separated string containing all parameters + * Returns a ", " -joined string containing all parameters * for the requested key, NULL if none are found. The key is case-insensitive. * @param p Allocates the return string. - * @param req The current apreq_request_t object. - * @param key Null-terminated parameter name. key==NULL fetches all values. + * @param t the parameter table returned by apreq_args(), apreq_body() + * or apreq_params() + * @param key Null-terminated parameter name, case insensitive. + * key==NULL fetches all values. * @param mode Join type- see apreq_join(). - * @return Returned string is the data attribute of an apreq_value_t, - * so it is safe to use in apreq_strlen() and apreq_strtoval(). + * @return the joined string * @remark Also parses the request if necessary. */ APREQ_DECLARE(const char *) apreq_params_as_string(apr_pool_t *p, @@ -172,8 +178,8 @@ /** * Returns a table of all params in req->body with non-NULL upload brigades. + * @param body parameter table returned by apreq_body() or apreq_params() * @param pool Pool which allocates the table struct. - * @param req Current request. * @return Upload table. * @remark Will parse the request if necessary. */ @@ -182,9 +188,9 @@ /** * Returns the first param in req->body which has both param->v.name - * matching key and param->upload != NULL. - * @param req The current request. - * @param key Parameter name. key == NULL returns first upload. + * matching key (case insensitive) and param->upload != NULL. + * @param body parameter table returned by apreq_body() or apreq_params() + * @param name Parameter name. key == NULL returns first upload. * @return Corresponding upload, NULL if none found. * @remark Will parse the request as necessary. */ diff -rN -u old-apreq2/include/apreq_parser.h new-apreq2/include/apreq_parser.h --- old-apreq2/include/apreq_parser.h 2005-03-10 00:08:48.873604432 +0100 +++ new-apreq2/include/apreq_parser.h 2005-03-09 23:55:55.000000000 +0100 @@ -24,7 +24,22 @@ extern "C" { #endif /* __cplusplus */ +/** + * @file apreq_parser.h + * @brief Request body parser API + * @ingroup libapreq2 + */ + +/** + * A hook is called by the parser whenever data arrives in a file + * upload parameter of the request body. You may associate any number + * of hooks with a parser instance with apreq_parser_add_hook(). + */ typedef struct apreq_hook_t apreq_hook_t; + +/** + * A request body parser instance. + */ typedef struct apreq_parser_t apreq_parser_t; /** Parser arguments. */ @@ -37,7 +52,14 @@ apreq_param_t *param, \ apr_bucket_brigade *bb +/** + * The callback function implementing a request body parser. + */ typedef apr_status_t (*apreq_parser_function_t)(APREQ_PARSER_ARGS); + +/** + * The callback function of a hook. See apreq_hook_t. + */ typedef apr_status_t (*apreq_hook_function_t)(APREQ_HOOK_ARGS); /** @@ -53,28 +75,36 @@ (f) (APREQ_HOOK_ARGS) /** - * Singly linked list of hooks. - * + * A hook is called by the parser whenever data arrives in a file + * upload parameter of the request body. You may associate any number + * of hooks with a parser instance with apreq_parser_add_hook(). */ struct apreq_hook_t { - apreq_hook_function_t hook; - apreq_hook_t *next; - apr_pool_t *pool; - void *ctx; + apreq_hook_function_t hook; /**< the hook function */ + apreq_hook_t *next; /**< next item in the linked list */ + apr_pool_t *pool; /**< pool which allocated this hook */ + void *ctx; /**< a user defined pointer passed to the hook function */ }; /** - * Request parser with associated enctype and hooks. - * + * A request body parser instance. */ struct apreq_parser_t { + /** the function which parses chunks of body data */ apreq_parser_function_t parser; + /** the Content-Type request header */ const char *content_type; + /** a pool used by the parser */ apr_pool_t *pool; + /** bucket allocator used to create bucket brigades */ apr_bucket_alloc_t *bucket_alloc; + /** the maximum in-memory bytes a brigade may use */ apr_size_t brigade_limit; + /** the directory used by the parser for temporary files */ const char *temp_dir; + /** linked list of hooks */ apreq_hook_t *hook; + /** internal context pointer used by the parser function */ void *ctx; }; @@ -154,8 +184,11 @@ * Construct a parser. * * @param pool Pool used to allocate the parser. - * @param enctype Content-type that this parser can deal with. - * @param parser The parser function. + * @param ba bucket allocator used to create bucket brigades + * @param content_type Content-type that this parser can deal with. + * @param pfn The parser function. + * @param brigade_limit the maximum in-memory bytes a brigade may use + * @param temp_dir the directory used by the parser for temporary files * @param hook Hooks to asssociate this parser with. * @param ctx Parser's internal scratch pad. * @return New parser. @@ -196,7 +229,7 @@ /** * Fetch the default parser function associated with the given MIME type. - * @param encytpe The desired enctype (can also be a full "Content-Type" + * @param enctype The desired enctype (can also be a full "Content-Type" * header). * @return The parser function, or NULL if the enctype is unrecognized. */ @@ -209,7 +242,7 @@ * internal lookup table. * * @param enctype The MIME type. - * @param parser The function to use during parsing. Setting + * @param pfn The function to use during parsing. Setting * parser == NULL will remove an existing parser. * @remark This is not a thread-safe operation, so applications * should only call this during process startup, diff -rN -u old-apreq2/include/apreq_util.h new-apreq2/include/apreq_util.h --- old-apreq2/include/apreq_util.h 2005-03-10 00:08:48.870604888 +0100 +++ new-apreq2/include/apreq_util.h 2005-03-09 22:47:04.000000000 +0100 @@ -103,7 +103,7 @@ * Url-encodes a string. * * @param dest Location of url-encoded result string. Caller must ensure it - * is large enough to hold the encoded string and trailing '\0'. + * is large enough to hold the encoded string and trailing '\\0'. * @param src Original string. * @param slen Length of original string. * @@ -199,7 +199,7 @@ /** * Converts file sizes (KMG) to bytes * - * @param s file size matching m/^\d+[KMG]b?$/i + * @param s file size matching m/^\\d+[KMG]b?$/i * * @return 64-bit integer representation of s. * @@ -212,7 +212,7 @@ /** * Converts time strings (YMDhms) to seconds * - * @param s time string matching m/^\+?\d+[YMDhms]$/ + * @param s time string matching m/^\\+?\\d+[YMDhms]$/ * * @return 64-bit integer representation of s as seconds. * @@ -347,6 +347,14 @@ apr_bucket_brigade *out, apr_bucket_brigade *in); +/** + * Determines the spool file used by the brigade. Returns NULL if the + * brigade is not spooled in a file (does not use a APREQ_SPOOL + * bucket). + * + * @param bb the bucket brigade + * @return the spool file, or NULL + */ APREQ_DECLARE(apr_file_t *)apreq_brigade_spoolfile(apr_bucket_brigade *bb); #ifdef __cplusplus --G4iJoqBmSsgzjUCe--