summaryrefslogtreecommitdiffstats
path: root/server/protocol.c
diff options
context:
space:
mode:
authorStefan Eissing <icing@apache.org>2022-04-13 09:40:17 +0200
committerStefan Eissing <icing@apache.org>2022-04-13 09:40:17 +0200
commitfbb84e00fa53ca34b97f9acc4d024e512d4e6f23 (patch)
tree47249fbbc06ebbf64e767683f870c0d7dd14213f /server/protocol.c
parentFollow up to r1899777: CHANGES entry [skip ci]. (diff)
downloadapache2-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.c675
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)) {