diff options
author | Stefan Eissing <icing@apache.org> | 2022-04-13 09:40:17 +0200 |
---|---|---|
committer | Stefan Eissing <icing@apache.org> | 2022-04-13 09:40:17 +0200 |
commit | fbb84e00fa53ca34b97f9acc4d024e512d4e6f23 (patch) | |
tree | 47249fbbc06ebbf64e767683f870c0d7dd14213f /server/protocol.c | |
parent | Follow up to r1899777: CHANGES entry [skip ci]. (diff) | |
download | apache2-fbb84e00fa53ca34b97f9acc4d024e512d4e6f23.tar.xz apache2-fbb84e00fa53ca34b97f9acc4d024e512d4e6f23.zip |
Merge PR 311:
*) core/mod_http: use REQUEST meta buckets and a new HTTP/1.x specific
input filter to separate the handling for HTTP requests from the
handling of HTTP/1.x request parsing and checks.
A new HTTP1_REQUEST_IN filter installs itself on http/1.1 connections
before a request is being read. It generates either a REQUEST meta
bucket on success or an ERROR bucket with the proposed response status.
The core connection processing, relying on ap_read_request(), now expects
a REQUEST or ERROR bucket from the input filters and is agnostic to
specific HTTP versions and how they bring requests into the server.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1899799 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'server/protocol.c')
-rw-r--r-- | server/protocol.c | 675 |
1 files changed, 257 insertions, 418 deletions
diff --git a/server/protocol.c b/server/protocol.c index a11ae6cce2..8d9b4fd169 100644 --- a/server/protocol.c +++ b/server/protocol.c @@ -682,359 +682,13 @@ static int field_name_len(const char *field) return end - field; } -static const char m_invalid_str[] = "-"; - -static int read_request_line(request_rec *r, apr_bucket_brigade *bb) -{ - apr_size_t len; - int num_blank_lines = DEFAULT_LIMIT_BLANK_LINES; - core_server_config *conf = ap_get_core_module_config(r->server->module_config); - int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE); - - /* Read past empty lines until we get a real request line, - * a read error, the connection closes (EOF), or we timeout. - * - * We skip empty lines because browsers have to tack a CRLF on to the end - * of POSTs to support old CERN webservers. But note that we may not - * have flushed any previous response completely to the client yet. - * We delay the flush as long as possible so that we can improve - * performance for clients that are pipelining requests. If a request - * is pipelined then we won't block during the (implicit) read() below. - * If the requests aren't pipelined, then the client is still waiting - * for the final buffer flush from us, and we will block in the implicit - * read(). B_SAFEREAD ensures that the BUFF layer flushes if it will - * have to block during a read. - */ - - do { - apr_status_t rv; - - /* ensure ap_rgetline allocates memory each time thru the loop - * if there are empty lines - */ - r->the_request = NULL; - rv = ap_rgetline(&(r->the_request), (apr_size_t)(r->server->limit_req_line + 2), - &len, r, strict ? AP_GETLINE_CRLF : 0, bb); - - if (rv != APR_SUCCESS) { - r->request_time = apr_time_now(); - - /* Fall through with an invalid (non NULL) request */ - r->method = m_invalid_str; - r->method_number = M_INVALID; - r->uri = r->unparsed_uri = apr_pstrdup(r->pool, "-"); - - /* ap_rgetline returns APR_ENOSPC if it fills up the - * buffer before finding the end-of-line. This is only going to - * happen if it exceeds the configured limit for a request-line. - */ - if (APR_STATUS_IS_ENOSPC(rv)) { - r->status = HTTP_REQUEST_URI_TOO_LARGE; - } - else if (APR_STATUS_IS_TIMEUP(rv)) { - r->status = HTTP_REQUEST_TIME_OUT; - } - else if (APR_STATUS_IS_BADARG(rv)) { - r->status = HTTP_BAD_REQUEST; - } - else if (APR_STATUS_IS_EINVAL(rv)) { - r->status = HTTP_BAD_REQUEST; - } - r->proto_num = HTTP_VERSION(1,0); - r->protocol = "HTTP/1.0"; - return 0; - } - } while ((len <= 0) && (--num_blank_lines >= 0)); - - /* Set r->request_time before any logging, mod_unique_id needs it. */ - r->request_time = apr_time_now(); - - if (APLOGrtrace5(r)) { - ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, - "Request received from client: %s", - ap_escape_logitem(r->pool, r->the_request)); - } - - return 1; -} - AP_DECLARE(int) ap_parse_request_line(request_rec *r) { - core_server_config *conf = ap_get_core_module_config(r->server->module_config); - int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE); - enum { - rrl_none, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace, - rrl_missinguri, rrl_baduri, rrl_badprotocol, rrl_trailingtext, - rrl_badmethod09, rrl_reject09 - } deferred_error = rrl_none; - apr_size_t len = 0; - char *uri, *ll; - - r->method = r->the_request; - - /* If there is whitespace before a method, skip it and mark in error */ - if (apr_isspace(*r->method)) { - deferred_error = rrl_badwhitespace; - for ( ; apr_isspace(*r->method); ++r->method) - ; - } + const char *method, *uri, *protocol; - /* Scan the method up to the next whitespace, ensure it contains only - * valid http-token characters, otherwise mark in error - */ - if (strict) { - ll = (char*) ap_scan_http_token(r->method); - } - else { - ll = (char*) ap_scan_vchar_obstext(r->method); - } - - if (((ll == r->method) || (*ll && !apr_isspace(*ll))) - && deferred_error == rrl_none) { - deferred_error = rrl_badmethod; - ll = strpbrk(ll, "\t\n\v\f\r "); - } - - /* Verify method terminated with a single SP, or mark as specific error */ - if (!ll) { - if (deferred_error == rrl_none) - deferred_error = rrl_missinguri; - r->protocol = uri = ""; - goto rrl_done; - } - else if (strict && ll[0] && apr_isspace(ll[1]) - && deferred_error == rrl_none) { - deferred_error = rrl_excesswhitespace; - } - - /* Advance uri pointer over leading whitespace, NUL terminate the method - * If non-SP whitespace is encountered, mark as specific error - */ - for (uri = ll; apr_isspace(*uri); ++uri) - if (*uri != ' ' && deferred_error == rrl_none) - deferred_error = rrl_badwhitespace; - *ll = '\0'; - - if (!*uri && deferred_error == rrl_none) - deferred_error = rrl_missinguri; - - /* Scan the URI up to the next whitespace, ensure it contains no raw - * control characters, otherwise mark in error - */ - ll = (char*) ap_scan_vchar_obstext(uri); - if (ll == uri || (*ll && !apr_isspace(*ll))) { - deferred_error = rrl_baduri; - ll = strpbrk(ll, "\t\n\v\f\r "); - } - - /* Verify URI terminated with a single SP, or mark as specific error */ - if (!ll) { - r->protocol = ""; - goto rrl_done; - } - else if (strict && ll[0] && apr_isspace(ll[1]) - && deferred_error == rrl_none) { - deferred_error = rrl_excesswhitespace; - } - - /* Advance protocol pointer over leading whitespace, NUL terminate the uri - * If non-SP whitespace is encountered, mark as specific error - */ - for (r->protocol = ll; apr_isspace(*r->protocol); ++r->protocol) - if (*r->protocol != ' ' && deferred_error == rrl_none) - deferred_error = rrl_badwhitespace; - *ll = '\0'; - - /* Scan the protocol up to the next whitespace, validation comes later */ - if (!(ll = (char*) ap_scan_vchar_obstext(r->protocol))) { - len = strlen(r->protocol); - goto rrl_done; - } - len = ll - r->protocol; - - /* Advance over trailing whitespace, if found mark in error, - * determine if trailing text is found, unconditionally mark in error, - * finally NUL terminate the protocol string - */ - if (*ll && !apr_isspace(*ll)) { - deferred_error = rrl_badprotocol; - } - else if (strict && *ll) { - deferred_error = rrl_excesswhitespace; - } - else { - for ( ; apr_isspace(*ll); ++ll) - if (*ll != ' ' && deferred_error == rrl_none) - deferred_error = rrl_badwhitespace; - if (*ll && deferred_error == rrl_none) - deferred_error = rrl_trailingtext; - } - *((char *)r->protocol + len) = '\0'; - -rrl_done: - /* For internal integrity and palloc efficiency, reconstruct the_request - * in one palloc, using only single SP characters, per spec. - */ - r->the_request = apr_pstrcat(r->pool, r->method, *uri ? " " : NULL, uri, - *r->protocol ? " " : NULL, r->protocol, NULL); - - if (len == 8 - && r->protocol[0] == 'H' && r->protocol[1] == 'T' - && r->protocol[2] == 'T' && r->protocol[3] == 'P' - && r->protocol[4] == '/' && apr_isdigit(r->protocol[5]) - && r->protocol[6] == '.' && apr_isdigit(r->protocol[7]) - && r->protocol[5] != '0') { - r->assbackwards = 0; - r->proto_num = HTTP_VERSION(r->protocol[5] - '0', r->protocol[7] - '0'); - } - else if (len == 8 - && (r->protocol[0] == 'H' || r->protocol[0] == 'h') - && (r->protocol[1] == 'T' || r->protocol[1] == 't') - && (r->protocol[2] == 'T' || r->protocol[2] == 't') - && (r->protocol[3] == 'P' || r->protocol[3] == 'p') - && r->protocol[4] == '/' && apr_isdigit(r->protocol[5]) - && r->protocol[6] == '.' && apr_isdigit(r->protocol[7]) - && r->protocol[5] != '0') { - r->assbackwards = 0; - r->proto_num = HTTP_VERSION(r->protocol[5] - '0', r->protocol[7] - '0'); - if (strict && deferred_error == rrl_none) - deferred_error = rrl_badprotocol; - else - memcpy((char*)r->protocol, "HTTP", 4); - } - else if (r->protocol[0]) { - r->proto_num = HTTP_VERSION(0, 9); - /* Defer setting the r->protocol string till error msg is composed */ - if (deferred_error == rrl_none) - deferred_error = rrl_badprotocol; - } - else { - r->assbackwards = 1; - r->protocol = "HTTP/0.9"; - r->proto_num = HTTP_VERSION(0, 9); - } - - /* Determine the method_number and parse the uri prior to invoking error - * handling, such that these fields are available for substitution - */ - r->method_number = ap_method_number_of(r->method); - if (r->method_number == M_GET && r->method[0] == 'H') - r->header_only = 1; - - ap_parse_uri(r, uri); - if (r->status == HTTP_OK - && (r->parsed_uri.path != NULL) - && (r->parsed_uri.path[0] != '/') - && (r->method_number != M_OPTIONS - || strcmp(r->parsed_uri.path, "*") != 0)) { - /* Invalid request-target per RFC 7230 section 5.3 */ - r->status = HTTP_BAD_REQUEST; - } - - /* With the request understood, we can consider HTTP/0.9 specific errors */ - if (r->proto_num == HTTP_VERSION(0, 9) && deferred_error == rrl_none) { - if (conf->http09_enable == AP_HTTP09_DISABLE) - deferred_error = rrl_reject09; - else if (strict && (r->method_number != M_GET || r->header_only)) - deferred_error = rrl_badmethod09; - } - - /* Now that the method, uri and protocol are all processed, - * we can safely resume any deferred error reporting - */ - if (deferred_error != rrl_none) { - if (deferred_error == rrl_badmethod) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03445) - "HTTP Request Line; Invalid method token: '%.*s'", - field_name_len(r->method), r->method); - else if (deferred_error == rrl_badmethod09) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03444) - "HTTP Request Line; Invalid method token: '%.*s'" - " (only GET is allowed for HTTP/0.9 requests)", - field_name_len(r->method), r->method); - else if (deferred_error == rrl_missinguri) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03446) - "HTTP Request Line; Missing URI"); - else if (deferred_error == rrl_baduri) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03454) - "HTTP Request Line; URI incorrectly encoded: '%.*s'", - field_name_len(r->unparsed_uri), r->unparsed_uri); - else if (deferred_error == rrl_badwhitespace) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03447) - "HTTP Request Line; Invalid whitespace"); - else if (deferred_error == rrl_excesswhitespace) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03448) - "HTTP Request Line; Excess whitespace " - "(disallowed by HttpProtocolOptions Strict)"); - else if (deferred_error == rrl_trailingtext) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03449) - "HTTP Request Line; Extraneous text found '%.*s' " - "(perhaps whitespace was injected?)", - field_name_len(ll), ll); - else if (deferred_error == rrl_reject09) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02401) - "HTTP Request Line; Rejected HTTP/0.9 request"); - else if (deferred_error == rrl_badprotocol) - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02418) - "HTTP Request Line; Unrecognized protocol '%.*s' " - "(perhaps whitespace was injected?)", - field_name_len(r->protocol), r->protocol); - r->status = HTTP_BAD_REQUEST; - goto rrl_failed; - } - - if (conf->http_methods == AP_HTTP_METHODS_REGISTERED - && r->method_number == M_INVALID) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02423) - "HTTP Request Line; Unrecognized HTTP method: '%.*s' " - "(disallowed by RegisteredMethods)", - field_name_len(r->method), r->method); - r->status = HTTP_NOT_IMPLEMENTED; - /* This can't happen in an HTTP/0.9 request, we verified GET above */ - return 0; - } - - if (r->status != HTTP_OK) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03450) - "HTTP Request Line; Unable to parse URI: '%.*s'", - field_name_len(r->uri), r->uri); - goto rrl_failed; - } - - if (strict) { - if (r->parsed_uri.fragment) { - /* RFC3986 3.5: no fragment */ - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02421) - "HTTP Request Line; URI must not contain a fragment"); - r->status = HTTP_BAD_REQUEST; - goto rrl_failed; - } - if (r->parsed_uri.user || r->parsed_uri.password) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02422) - "HTTP Request Line; URI must not contain a " - "username/password"); - r->status = HTTP_BAD_REQUEST; - goto rrl_failed; - } - } - - return 1; - -rrl_failed: - if (r->proto_num == HTTP_VERSION(0, 9)) { - /* Send all parsing and protocol error response with 1.x behavior, - * and reserve 505 errors for actual HTTP protocols presented. - * As called out in RFC7230 3.5, any errors parsing the protocol - * from the request line are nearly always misencoded HTTP/1.x - * requests. Only a valid 0.9 request with no parsing errors - * at all may be treated as a simple request, if allowed. - */ - r->assbackwards = 0; - r->connection->keepalive = AP_CONN_CLOSE; - r->proto_num = HTTP_VERSION(1, 0); - r->protocol = "HTTP/1.0"; - } - return 0; + return ap_h1_tokenize_request_line(r, r->the_request, + &method, &uri, &protocol) + && ap_assign_request_line(r, method, uri, protocol); } AP_DECLARE(int) ap_check_request_header(request_rec *r) @@ -1466,22 +1120,196 @@ static void apply_server_config(request_rec *r) r->per_dir_config = r->server->lookup_defaults; } -AP_DECLARE(int) ap_assign_request(request_rec *r, - const char *method, const char *uri, - const char *protocol) +typedef enum { + http_error_none, + http_error_badprotocol, + http_error_reject09, + http_error_badmethod09, +} http_error; + +static http_error r_assign_protocol(request_rec *r, + const char *protocol, + int strict) { - /* dummy, for now */ - (void)r; - (void)method; - (void)uri; - (void)protocol; + int proto_num; + http_error error = http_error_none; + + if (protocol[0] == 'H' && protocol[1] == 'T' + && protocol[2] == 'T' && protocol[3] == 'P' + && protocol[4] == '/' && apr_isdigit(protocol[5]) + && protocol[6] == '.' && apr_isdigit(protocol[7]) + && !protocol[8] && protocol[5] != '0') { + r->assbackwards = 0; + proto_num = HTTP_VERSION(protocol[5] - '0', protocol[7] - '0'); + } + else if ((protocol[0] == 'H' || protocol[0] == 'h') + && (protocol[1] == 'T' || protocol[1] == 't') + && (protocol[2] == 'T' || protocol[2] == 't') + && (protocol[3] == 'P' || protocol[3] == 'p') + && protocol[4] == '/' && apr_isdigit(protocol[5]) + && protocol[6] == '.' && apr_isdigit(protocol[7]) + && !protocol[8] && protocol[5] != '0') { + r->assbackwards = 0; + proto_num = HTTP_VERSION(protocol[5] - '0', protocol[7] - '0'); + if (strict && error == http_error_none) + error = http_error_badprotocol; + else + protocol = apr_psprintf(r->pool, "HTTP/%d.%d", HTTP_VERSION_MAJOR(proto_num), + HTTP_VERSION_MINOR(proto_num)); + } + else if (protocol[0]) { + proto_num = HTTP_VERSION(0, 9); + if (error == http_error_none) + error = http_error_badprotocol; + } + else { + r->assbackwards = 1; + protocol = "HTTP/0.9"; + proto_num = HTTP_VERSION(0, 9); + } + r->protocol = protocol; + r->proto_num = proto_num; + return error; +} + +AP_DECLARE(int) ap_assign_request_line(request_rec *r, + const char *method, const char *uri, + const char *protocol) +{ + core_server_config *conf = ap_get_core_module_config(r->server->module_config); + int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE); + http_error error = r_assign_protocol(r, protocol, strict); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "assigned protocol: %s, error=%d", r->protocol, error); + + /* Determine the method_number and parse the uri prior to invoking error + * handling, such that these fields are available for substitution + */ + r->method = method; + r->method_number = ap_method_number_of(r->method); + if (r->method_number == M_GET && r->method[0] == 'H') + r->header_only = 1; + + /* For internal integrity and palloc efficiency, reconstruct the_request + * in one palloc, using only single SP characters, per spec. + */ + r->the_request = apr_pstrcat(r->pool, r->method, *uri ? " " : NULL, uri, + *r->protocol ? " " : NULL, r->protocol, NULL); + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "assigned request_line: %s, error=%d", r->the_request, error); + + ap_parse_uri(r, uri); + if (r->status == HTTP_OK + && (r->parsed_uri.path != NULL) + && (r->parsed_uri.path[0] != '/') + && (r->method_number != M_OPTIONS + || strcmp(r->parsed_uri.path, "*") != 0)) { + /* Invalid request-target per RFC 7230 section 5.3 */ + r->status = HTTP_BAD_REQUEST; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "parsed uri: r->status=%d, error=%d", r->status, error); + /* With the request understood, we can consider HTTP/0.9 specific errors */ + if (r->proto_num == HTTP_VERSION(0, 9) && error == http_error_none) { + if (conf->http09_enable == AP_HTTP09_DISABLE) + error = http_error_reject09; + else if (strict && (r->method_number != M_GET || r->header_only)) + error = http_error_badmethod09; + } + + /* Now that the method, uri and protocol are all processed, + * we can safely resume any deferred error reporting + */ + if (error != http_error_none) { + switch (error) { + case http_error_badprotocol: + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(10388) + "HTTP Request: Unrecognized protocol '%.*s' " + "(perhaps whitespace was injected?)", + field_name_len(r->protocol), r->protocol); + break; + case http_error_reject09: + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02401) + "HTTP Request: Rejected HTTP/0.9 request"); + break; + case http_error_badmethod09: + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03444) + "HTTP Request: Invalid method token: '%.*s'" + " (only GET is allowed for HTTP/0.9 requests)", + field_name_len(r->method), r->method); + break; + default: + break; + } + r->status = HTTP_BAD_REQUEST; + goto failed; + } + + if (conf->http_methods == AP_HTTP_METHODS_REGISTERED + && r->method_number == M_INVALID) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02423) + "HTTP Request Line; Unrecognized HTTP method: '%.*s' " + "(disallowed by RegisteredMethods)", + field_name_len(r->method), r->method); + r->status = HTTP_NOT_IMPLEMENTED; + /* This can't happen in an HTTP/0.9 request, we verified GET above */ + goto failed; + } + + if (r->status != HTTP_OK) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03450) + "HTTP Request Line; Unable to parse URI: '%.*s'", + field_name_len(r->uri), r->uri); + goto failed; + } + + if (strict) { + if (r->parsed_uri.fragment) { + /* RFC3986 3.5: no fragment */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02421) + "HTTP Request Line; URI must not contain a fragment"); + r->status = HTTP_BAD_REQUEST; + goto failed; + } + if (r->parsed_uri.user || r->parsed_uri.password) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02422) + "HTTP Request Line; URI must not contain a " + "username/password"); + r->status = HTTP_BAD_REQUEST; + goto failed; + } + } + return 1; + +failed: + if (error != http_error_none && r->proto_num == HTTP_VERSION(0, 9)) { + /* Send all parsing and protocol error response with 1.x behavior, + * and reserve 505 errors for actual HTTP protocols presented. + * As called out in RFC7230 3.5, any errors parsing the protocol + * from the request line are nearly always misencoded HTTP/1.x + * requests. Only a valid 0.9 request with no parsing errors + * at all may be treated as a simple request, if allowed. + */ + r->assbackwards = 0; + r->connection->keepalive = AP_CONN_CLOSE; + r->proto_num = HTTP_VERSION(1, 0); + r->protocol = "HTTP/1.0"; + } return 0; + } -request_rec *ap_read_request(conn_rec *conn) +AP_DECLARE(request_rec *) ap_read_request(conn_rec *conn) { int access_status; apr_bucket_brigade *tmp_bb; + apr_bucket *e, *bdata = NULL; + ap_bucket_request *breq = NULL; + const char *method, *uri, *protocol; + apr_table_t *headers; + apr_status_t rv; request_rec *r = ap_create_request(conn); @@ -1490,8 +1318,72 @@ request_rec *ap_read_request(conn_rec *conn) ap_run_pre_read_request(r, conn); - /* Get the request... */ - if (!read_request_line(r, tmp_bb) || !ap_parse_request_line(r)) { + r->request_time = apr_time_now(); + rv = ap_get_brigade(r->proto_input_filters, tmp_bb, AP_MODE_READBYTES, APR_BLOCK_READ, 0); + if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(tmp_bb)) { + /* Not worth dying with. */ + conn->keepalive = AP_CONN_CLOSE; + apr_pool_destroy(r->pool); + goto ignore; + } + + for (e = APR_BRIGADE_FIRST(tmp_bb); + e != APR_BRIGADE_SENTINEL(tmp_bb); + e = APR_BUCKET_NEXT(e)) + { + if (AP_BUCKET_IS_REQUEST(e)) { + if (!breq) breq = e->data; + } + else if (AP_BUCKET_IS_ERROR(e)) { + access_status = ((ap_bucket_error *)(e->data))->status; + goto die_unusable_input; + } + else if (!APR_BUCKET_IS_METADATA(e) && e->length != 0) { + if (!bdata) bdata = e;; + break; + } + } + + if (bdata) { + /* Since processing of a request body depends on knowing the request, we + * cannot handle any data here. For example, chunked-encoding filters are + * added after the request is read, so any data buckets here will not + * have been de-chunked. + */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10391) + "request failed: seeing DATA bucket(len=%d) of request " + "body, too early to process", (int)bdata->length); + access_status = HTTP_INTERNAL_SERVER_ERROR; + goto die_unusable_input; + } + else if (!breq) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10389) + "request failed: neither request bucket nor error at start of input"); + access_status = HTTP_INTERNAL_SERVER_ERROR; + goto die_unusable_input; + } + + if (apr_pool_is_ancestor(r->pool, breq->pool)) { + method = breq->method; + uri = breq->uri; + protocol = breq->protocol; + headers = breq->headers; + } + else { + method = apr_pstrdup(r->pool, breq->method); + uri = apr_pstrdup(r->pool, breq->uri); + protocol = apr_pstrdup(r->pool, breq->protocol); + headers = breq->headers? apr_table_clone(r->pool, breq->headers) : NULL; + } + + if (headers) { + r->headers_in = headers; + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "checking request: %s %s %s", + method, uri, protocol); + + if (!ap_assign_request_line(r, method, uri, protocol)) { apr_brigade_cleanup(tmp_bb); switch (r->status) { case HTTP_REQUEST_URI_TOO_LARGE: @@ -1503,7 +1395,7 @@ request_rec *ap_read_request(conn_rec *conn) "request failed: client's request-line exceeds LimitRequestLine (longer than %d)", r->server->limit_req_line); } - else if (r->method == m_invalid_str) { + else if (!strcmp("-", r->method)) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00566) "request failed: malformed request line"); } @@ -1531,18 +1423,7 @@ request_rec *ap_read_request(conn_rec *conn) apply_server_config(r); if (!r->assbackwards) { - const char *tenc, *clen; - - ap_get_mime_headers_core(r, tmp_bb); - apr_brigade_cleanup(tmp_bb); - if (r->status != HTTP_OK) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00567) - "request failed: error reading the headers"); - access_status = r->status; - goto die_unusable_input; - } - - clen = apr_table_get(r->headers_in, "Content-Length"); + const char *clen = apr_table_get(r->headers_in, "Content-Length"); if (clen) { apr_off_t cl; @@ -1554,56 +1435,14 @@ request_rec *ap_read_request(conn_rec *conn) goto die_unusable_input; } } - - tenc = apr_table_get(r->headers_in, "Transfer-Encoding"); - if (tenc) { - /* https://tools.ietf.org/html/rfc7230 - * Section 3.3.3.3: "If a Transfer-Encoding header field is - * present in a request and the chunked transfer coding is not - * the final encoding ...; the server MUST respond with the 400 - * (Bad Request) status code and then close the connection". - */ - 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); - access_status = HTTP_BAD_REQUEST; - goto die_unusable_input; - } - - /* https://tools.ietf.org/html/rfc7230 - * Section 3.3.3.3: "If a message is received with both a - * Transfer-Encoding and a Content-Length header field, the - * Transfer-Encoding overrides the Content-Length. ... A sender - * MUST remove the received Content-Length field". - */ - if (clen) { - apr_table_unset(r->headers_in, "Content-Length"); - - /* Don't reuse this connection anyway to avoid confusion with - * intermediaries and request/reponse spltting. - */ - conn->keepalive = AP_CONN_CLOSE; - } - } } /* - * Add the HTTP_IN filter here to ensure that ap_discard_request_body - * called by ap_die and by ap_send_error_response works correctly on - * status codes that do not cause the connection to be dropped and - * in situations where the connection should be kept alive. + * Add HTTP_IN here to ensure that content and HEADERS buckets are processed + * accordingly, e.g. populating r->trailers_in for example. */ ap_add_input_filter_handle(ap_http_input_filter_handle, NULL, r, r->connection); - if (r->proto_num <= HTTP_VERSION(1,1)) { - ap_add_input_filter_handle(ap_h1_body_in_filter_handle, - NULL, r, r->connection); - if (r->proto_num >= HTTP_VERSION(1,0) - && apr_table_get(r->headers_in, "Transfer-Encoding")) { - r->body_indeterminate = 1; - } - } /* Validate Host/Expect headers and select vhost. */ if (!ap_check_request_header(r)) { |