From 57383650238006fdeed8e7900a8cfa4a807c0cf3 Mon Sep 17 00:00:00 2001 From: Yann Ylavic Date: Fri, 20 Apr 2018 14:30:19 +0000 Subject: [PATCH] http: add ap_fgetline() and AP_GETLINE_NONBLOCK flag. It allows to read a line directly from an input filter, in blocking mode or not. Since no request_rec is needed, a pool may be given. Existing ap_[r]getline() function are now based off ap_fgetline() by calling: ap_fgetline(s, n, read, r->proto_input_filters, flags, bb, r->pool); Will follow up with a new ap_get_mime_headers_*() flavor which can be used by any filter that needs non-blocking and not necessarily has a request_rec (e.g. ap_http_filter() to read proxied response trailers). git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1829659 13f79535-47bb-0310-9956-ffa450edef68 --- include/ap_mmn.h | 3 +- include/http_protocol.h | 68 +++++++++++++++++++++---------------- server/protocol.c | 75 ++++++++++++++++++++++++++++------------- 3 files changed, 92 insertions(+), 54 deletions(-) diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 25cd91b900..eb66cf9c00 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -573,6 +573,7 @@ * 20171014.9 (2.5.1-dev) Add response_field_size to proxy_worker_shared * 20180417.1 (2.5.1-dev) Toggle ap_filter_input|output_pending API to _NONSTD * 20180417.2 (2.5.1-dev) Add AP_GETLINE_NOSPC_EOL flag to http_protocol.h + * 20180417.3 (2.5.1-dev) Add ap_fgetline() and AP_GETLINE_NONBLOCK flag */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ @@ -580,7 +581,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20180417 #endif -#define MODULE_MAGIC_NUMBER_MINOR 2 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 3 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/http_protocol.h b/include/http_protocol.h index f38b413b83..bf15f9837b 100644 --- a/include/http_protocol.h +++ b/include/http_protocol.h @@ -630,19 +630,18 @@ AP_DECLARE(apr_status_t) ap_get_basic_auth_components(const request_rec *r, */ AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri); -#define AP_GETLINE_FOLD 1 /* Whether to merge continuation lines */ -#define AP_GETLINE_CRLF 2 /* Whether line ends must be in the form CR LF */ -#define AP_GETLINE_NOSPC_EOL 4 /* Whether to consume up to and including the - end of line on APR_ENOSPC */ +#define AP_GETLINE_FOLD (1 << 0) /* Whether to merge continuation lines */ +#define AP_GETLINE_CRLF (1 << 1) /* Whether line ends must be CRLF */ +#define AP_GETLINE_NOSPC_EOL (1 << 2) /* Whether to consume up to and including + the end of line on APR_ENOSPC */ +#define AP_GETLINE_NONBLOCK (1 << 3) /* Whether to read non-blocking */ /** * Get the next line of input for the request * @param s The buffer into which to read the line * @param n The size of the buffer * @param r The request - * @param flags Bit flag of multiple parsing options - * AP_GETLINE_FOLD Whether to merge continuation lines - * AP_GETLINE_CRLF Whether line ends must be in the form CR LF + * @param flags Bit mask of AP_GETLINE_* options * @return The length of the line, if successful * n, if the line is too big to fit in the buffer * -1 for miscellaneous errors @@ -650,45 +649,56 @@ AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri); AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags); /** - * Get the next line of input for the request - * - * Note: on ASCII boxes, ap_rgetline is a macro which simply calls - * ap_rgetline_core to get the line of input. - * - * on EBCDIC boxes, ap_rgetline is a wrapper function which - * translates ASCII protocol lines to the local EBCDIC code page - * after getting the line of input. + * Get the next line from an input filter * * @param s Pointer to the pointer to the buffer into which the line * should be read; if *s==NULL, a buffer of the necessary size - * to hold the data will be allocated from the request pool + * to hold the data will be allocated from \p p * @param n The size of the buffer * @param read The length of the line. - * @param r The request - * @param flags Bit flag of multiple parsing options - * AP_GETLINE_FOLD Whether to merge continuation lines - * AP_GETLINE_CRLF Whether line ends must be in the form CR LF + * @param f Input filter to read from + * @param flags Bit mask of AP_GETLINE_* options * @param bb Working brigade to use when reading buckets + * @param p The pool to allocate the buffer from (if needed) * @return APR_SUCCESS, if successful * APR_ENOSPC, if the line is too big to fit in the buffer * Other errors where appropriate */ +AP_DECLARE(apr_status_t) ap_fgetline(char **s, apr_size_t n, + apr_size_t *read, ap_filter_t *f, + int flags, apr_bucket_brigade *bb, + apr_pool_t *p); + +/** + * @see ap_fgetline + * + * Note: genuinely calls, ap_fgetline(s, n, read, r->proto_input_filters, + * flags, bb, r->pool) + */ +AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, + apr_size_t *read, request_rec *r, + int flags, apr_bucket_brigade *bb); + +/** + * @see ap_rgetline_core + * + * Note: on ASCII boxes, ap_rgetline is a macro which simply calls + * ap_rgetline_core to get the line of input. + * + * on EBCDIC boxes, ap_rgetline is a wrapper function which + * translates ASCII protocol lines to the local EBCDIC code page + * after getting the line of input. + * + */ #if APR_CHARSET_EBCDIC AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n, - apr_size_t *read, - request_rec *r, int flags, - apr_bucket_brigade *bb); + apr_size_t *read, request_rec *r, + int flags, apr_bucket_brigade *bb); #else /* ASCII box */ #define ap_rgetline(s, n, read, r, flags, bb) \ ap_rgetline_core((s), (n), (read), (r), (flags), (bb)) #endif -/** @see ap_rgetline */ -AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, - apr_size_t *read, - request_rec *r, int flags, - apr_bucket_brigade *bb); - /** * Get the method number associated with the given string, assumed to * contain an HTTP method. Returns M_INVALID if not recognized. diff --git a/server/protocol.c b/server/protocol.c index 27611ed508..ff67115cb4 100644 --- a/server/protocol.c +++ b/server/protocol.c @@ -188,15 +188,11 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime) return (mtime > now) ? now : mtime; } -/* Get a line of protocol input, including any continuation lines +/* Get a line from an input filter, including any continuation lines * caused by MIME folding (or broken clients) if fold != 0, and place it * in the buffer s, of size n bytes, without the ending newline. - * - * Pulls from r->proto_input_filters instead of r->input_filters for - * stricter protocol adherence and better input filter behavior during - * chunked trailer processing (for http). * - * If s is NULL, ap_rgetline_core will allocate necessary memory from r->pool. + * If s is NULL, ap_fgetline_impl will allocate necessary memory from p. * * Returns APR_SUCCESS if there are no problems and sets *read to be * the full length of s. @@ -213,9 +209,10 @@ AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime) * If no LF is detected on the last line due to a dropped connection * or a full buffer, that's considered an error. */ -AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, - apr_size_t *read, request_rec *r, - int flags, apr_bucket_brigade *bb) +static apr_status_t ap_fgetline_impl(char **s, apr_size_t n, + apr_size_t *read, ap_filter_t *f, + int flags, apr_bucket_brigade *bb, + apr_pool_t *p) { apr_status_t rv; apr_bucket *e; @@ -226,6 +223,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, int crlf = flags & AP_GETLINE_CRLF; int nospc_eol = flags & AP_GETLINE_NOSPC_EOL; int saw_eol = 0, saw_nospc = 0; + apr_read_type_e block; if (!n) { /* Needs room for NUL byte at least */ @@ -233,6 +231,9 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, return APR_BADARG; } + block = (flags & AP_GETLINE_NONBLOCK) ? APR_NONBLOCK_READ + : APR_BLOCK_READ; + /* * Initialize last_char as otherwise a random value will be compared * against APR_ASCII_LF at the end of the loop if bb only contains @@ -243,17 +244,22 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, do { apr_brigade_cleanup(bb); - rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_GETLINE, - APR_BLOCK_READ, 0); + rv = ap_get_brigade(f, bb, AP_MODE_GETLINE, block, 0); if (rv != APR_SUCCESS) { goto cleanup; } /* Something horribly wrong happened. Someone didn't block! * (this also happens at the end of each keepalive connection) + * (this also happens when non-blocking is asked too, not that wrong) */ if (APR_BRIGADE_EMPTY(bb)) { - rv = APR_EGENERAL; + if (block != APR_NONBLOCK_READ) { + rv = APR_EGENERAL; + } + else { + rv = APR_EAGAIN; + } goto cleanup; } @@ -338,7 +344,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, /* We'll assume the common case where one bucket is enough. */ if (!*s) { current_alloc = len; - *s = apr_palloc(r->pool, current_alloc + 1); + *s = apr_palloc(p, current_alloc + 1); } else if (bytes_handled + len > current_alloc) { /* Increase the buffer size */ @@ -349,7 +355,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, new_size = (bytes_handled + len) * 2; } - new_buffer = apr_palloc(r->pool, new_size + 1); + new_buffer = apr_palloc(p, new_size + 1); /* Copy what we already had. */ memcpy(new_buffer, *s, bytes_handled); @@ -408,8 +414,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, apr_brigade_cleanup(bb); /* We only care about the first byte. */ - rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_SPECULATIVE, - APR_BLOCK_READ, 1); + rv = ap_get_brigade(f, bb, AP_MODE_SPECULATIVE, block, 1); if (rv != APR_SUCCESS) { goto cleanup; } @@ -464,8 +469,8 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, next_size = n - bytes_handled; - rv = ap_rgetline_core(&tmp, next_size, &next_len, r, - flags & ~AP_GETLINE_FOLD, bb); + rv = ap_fgetline_impl(&tmp, next_size, &next_len, f, + flags & ~AP_GETLINE_FOLD, bb, p); if (rv != APR_SUCCESS) { goto cleanup; } @@ -475,7 +480,7 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, apr_size_t new_size = bytes_handled + next_len + 1; /* we need to alloc an extra byte for a null */ - new_buffer = apr_palloc(r->pool, new_size); + new_buffer = apr_palloc(p, new_size); /* Copy what we already had. */ memcpy(new_buffer, *s, bytes_handled); @@ -517,10 +522,31 @@ cleanup: return rv; } +AP_DECLARE(apr_status_t) ap_fgetline(char **s, apr_size_t n, + apr_size_t *read, ap_filter_t *f, + int flags, apr_bucket_brigade *bb, + apr_pool_t *p) +{ + return ap_fgetline_impl(s, n, read, f, flags, bb, p); +} + +/* Same as ap_fgetline(), working on r's pool and protocol input filters. + * Pulls from r->proto_input_filters instead of r->input_filters for + * stricter protocol adherence and better input filter behavior during + * chunked trailer processing (for http). + */ +AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, + apr_size_t *read, request_rec *r, + int flags, apr_bucket_brigade *bb) +{ + return ap_fgetline_impl(s, n, read, r->proto_input_filters, flags, + bb, r->pool); +} + #if APR_CHARSET_EBCDIC AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n, apr_size_t *read, request_rec *r, - int fold, apr_bucket_brigade *bb) + int flags, apr_bucket_brigade *bb) { /* on ASCII boxes, ap_rgetline is a macro which simply invokes * ap_rgetline_core with the same parms @@ -532,8 +558,9 @@ AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n, */ apr_status_t rv; - rv = ap_rgetline_core(s, n, read, r, fold, bb); - if (rv == APR_SUCCESS || APR_STATUS_IS_ENOSPC(rv)) { + rv = ap_fgetline_impl(s, n, read, r->proto_input_filters, flags, + bb, r->pool); + if (*read) { ap_xlate_proto_from_ascii(*s, *read); } return rv; @@ -542,7 +569,6 @@ AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n, AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags) { - char *tmp_s = s; apr_status_t rv; apr_size_t len; apr_bucket_brigade *tmp_bb; @@ -553,7 +579,8 @@ AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags) } tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); - rv = ap_rgetline(&tmp_s, n, &len, r, flags, tmp_bb); + rv = ap_fgetline_impl(&s, n, &len, r->proto_input_filters, flags, + tmp_bb, r->pool); apr_brigade_destroy(tmp_bb); /* Map the out-of-space condition to the old API. */ -- 2.40.0