diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 401067e510..5d1941228c 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -619,6 +619,7 @@ * and ap_proxy_tunnel_run() to proxy_util. * 20190312.6 (2.5.1-dev) Add proxy check_trans hook * 20190312.7 (2.5.1-dev) AP_REG_DEFAULT macro in ap_regex.h + * 20190312.8 (2.5.1-dev) ap_is_chunked() in httpd.h */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ @@ -626,7 +627,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20190312 #endif -#define MODULE_MAGIC_NUMBER_MINOR 7 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 8 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/httpd.h b/include/httpd.h index 03b7751fee..ee2acc25d9 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -2660,6 +2660,15 @@ AP_DECLARE(const char *)ap_dir_nofnmatch(ap_dir_match_t *w, const char *fname) AP_DECLARE(const char *)ap_dir_fnmatch(ap_dir_match_t *w, const char *path, const char *fname) __attribute__((nonnull(1,3))); +/** + * Determine if the final Transfer-Encoding is "chunked". + * + * @param p The pool to allocate from + * @param line the header field-value to scan + * @return 1 if the last Transfer-Encoding is "chunked", else 0 + */ +AP_DECLARE(int) ap_is_chunked(apr_pool_t *p, const char *line); + #ifdef __cplusplus } #endif diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c index 327d902a3c..42423e4dde 100644 --- a/modules/http/http_filters.c +++ b/modules/http/http_filters.c @@ -333,8 +333,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, lenp = apr_table_get(f->r->headers_in, "Content-Length"); if (tenc) { - if (ap_cstr_casecmp(tenc, "chunked") == 0 /* fast path */ - || ap_find_last_token(f->r->pool, tenc, "chunked")) { + if (ap_is_chunked(f->r->pool, tenc)) { ctx->state = BODY_CHUNK; } else if (f->r->proxyreq == PROXYREQ_RESPONSE) { diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c index 899fb787e3..9cf0cbb2ee 100644 --- a/modules/http/http_protocol.c +++ b/modules/http/http_protocol.c @@ -257,10 +257,9 @@ AP_DECLARE(int) ap_set_keepalive(request_rec *r) && (r->header_only || AP_STATUS_IS_HEADER_ONLY(r->status) || apr_table_get(r->headers_out, "Content-Length") - || ap_find_last_token(r->pool, + || ap_is_chunked(r->pool, apr_table_get(r->headers_out, - "Transfer-Encoding"), - "chunked") + "Transfer-Encoding")) || ((r->proto_num >= HTTP_VERSION(1,1)) && (r->chunked = 1))) /* THIS CODE IS CORRECT, see above. */ && r->server->keep_alive diff --git a/modules/test/mod_policy.c b/modules/test/mod_policy.c index ded77c761d..841b1b16bd 100644 --- a/modules/test/mod_policy.c +++ b/modules/test/mod_policy.c @@ -311,8 +311,8 @@ static apr_status_t policy_keepalive_out_filter(ap_filter_t *f, if (!(r->header_only || AP_STATUS_IS_HEADER_ONLY(r->status) || apr_table_get(r->headers_out, "Content-Length") - || ap_find_last_token(r->pool, apr_table_get(r->headers_out, - "Transfer-Encoding"), "chunked") + || ap_is_chunked(r->pool, apr_table_get(r->headers_out, + "Transfer-Encoding")) || r->proto_num >= HTTP_VERSION(1, 1))) { handle_policy(r, result, "Keepalive should be possible (supply Content-Length or HTTP/1.1 Transfer-Encoding)", diff --git a/server/protocol.c b/server/protocol.c index a0209715a2..7313b0a190 100644 --- a/server/protocol.c +++ b/server/protocol.c @@ -1430,8 +1430,7 @@ request_rec *ap_read_request(conn_rec *conn) * the final encoding ...; the server MUST respond with the 400 * (Bad Request) status code and then close the connection". */ - if (!(ap_cstr_casecmp(tenc, "chunked") == 0 /* fast path */ - || ap_find_last_token(r->pool, tenc, "chunked"))) { + if (!ap_is_chunked(r->pool, tenc)) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02539) "client sent unknown Transfer-Encoding " "(%s): %s", tenc, r->uri); diff --git a/server/util.c b/server/util.c index 044b073bf5..7603895e02 100644 --- a/server/util.c +++ b/server/util.c @@ -1709,14 +1709,13 @@ AP_DECLARE(int) ap_find_token(apr_pool_t *p, const char *line, const char *tok) } } - -AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line, - const char *tok) +static const char *find_last_token(apr_pool_t *p, const char *line, + const char *tok) { int llen, tlen, lidx; if (!line) - return 0; + return NULL; llen = strlen(line); tlen = strlen(tok); @@ -1724,9 +1723,44 @@ AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line, if (lidx < 0 || (lidx > 0 && !(apr_isspace(line[lidx - 1]) || line[lidx - 1] == ','))) - return 0; + return NULL; - return (ap_cstr_casecmpn(&line[lidx], tok, tlen) == 0); + if (ap_cstr_casecmpn(&line[lidx], tok, tlen) == 0) { + return &line[lidx]; + } + return NULL; +} + +AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line, + const char *tok) +{ + return find_last_token(p, line, tok) != NULL; +} + +AP_DECLARE(int) ap_is_chunked(apr_pool_t *p, const char *line) +{ + const char *s; + + if (!line) + return 0; + if (!ap_cstr_casecmp(line, "chunked")) { + return 1; + } + + s = find_last_token(p, line, "chunked"); + + if (!s) return 0; + + /* eat spaces right-to-left to see what precedes "chunked" */ + while (--s > line) { + if (*s != ' ') break; + } + + /* found delim, or leading ws (input wasn't parsed by httpd as a header) */ + if (*s == ',' || *s == ' ') { + return 1; + } + return 0; } AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t *p, const char *str)