diff options
Diffstat (limited to 'server/protocol.c')
-rw-r--r-- | server/protocol.c | 278 |
1 files changed, 259 insertions, 19 deletions
diff --git a/server/protocol.c b/server/protocol.c index bf915a0a..88d0f992 100644 --- a/server/protocol.c +++ b/server/protocol.c @@ -67,6 +67,9 @@ APR_HOOK_STRUCT( APR_HOOK_LINK(http_scheme) APR_HOOK_LINK(default_port) APR_HOOK_LINK(note_auth_failure) + APR_HOOK_LINK(protocol_propose) + APR_HOOK_LINK(protocol_switch) + APR_HOOK_LINK(protocol_get) ) AP_DECLARE_DATA ap_filter_rec_t *ap_old_write_func = NULL; @@ -555,15 +558,10 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb) const char *uri; const char *pro; - int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol */ + unsigned int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol */ char http[5]; apr_size_t len; - int num_blank_lines = 0; - int max_blank_lines = r->server->limit_req_fields; - - if (max_blank_lines <= 0) { - max_blank_lines = DEFAULT_LIMIT_REQUEST_FIELDS; - } + int num_blank_lines = DEFAULT_LIMIT_BLANK_LINES; /* Read past empty lines until we get a real request line, * a read error, the connection closes (EOF), or we timeout. @@ -599,8 +597,6 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb) */ if (APR_STATUS_IS_ENOSPC(rv)) { r->status = HTTP_REQUEST_URI_TOO_LARGE; - r->proto_num = HTTP_VERSION(1,0); - r->protocol = apr_pstrdup(r->pool, "HTTP/1.0"); } else if (APR_STATUS_IS_TIMEUP(rv)) { r->status = HTTP_REQUEST_TIME_OUT; @@ -608,9 +604,11 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb) else if (APR_STATUS_IS_EINVAL(rv)) { r->status = HTTP_BAD_REQUEST; } + r->proto_num = HTTP_VERSION(1,0); + r->protocol = apr_pstrdup(r->pool, "HTTP/1.0"); return 0; } - } while ((len <= 0) && (++num_blank_lines < max_blank_lines)); + } while ((len <= 0) && (--num_blank_lines >= 0)); if (APLOGrtrace5(r)) { ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, @@ -624,6 +622,13 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb) uri = ap_getword_white(r->pool, &ll); + if (!*r->method || !*uri) { + r->status = HTTP_BAD_REQUEST; + r->proto_num = HTTP_VERSION(1,0); + r->protocol = apr_pstrdup(r->pool, "HTTP/1.0"); + return 0; + } + /* Provide quick information about the request method as soon as known */ r->method_number = ap_method_number_of(r->method); @@ -632,6 +637,11 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb) } ap_parse_uri(r, uri); + if (r->status != HTTP_OK) { + r->proto_num = HTTP_VERSION(1,0); + r->protocol = apr_pstrdup(r->pool, "HTTP/1.0"); + return 0; + } if (ll[0]) { r->assbackwards = 0; @@ -718,6 +728,8 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb r->status = HTTP_REQUEST_TIME_OUT; } else { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, + "Failed to read request header line %s", field); r->status = HTTP_BAD_REQUEST; } @@ -727,7 +739,7 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb */ if (rv == APR_ENOSPC) { const char *field_escaped; - if (field) { + if (field && len) { /* ensure ap_escape_html will terminate correctly */ field[len - 1] = '\0'; field_escaped = ap_escape_html(r->pool, field); @@ -763,18 +775,21 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb apr_size_t fold_len = last_len + len + 1; /* trailing null */ if (fold_len >= (apr_size_t)(r->server->limit_req_fieldsize)) { + const char *field_escaped; + r->status = HTTP_BAD_REQUEST; /* report what we have accumulated so far before the * overflow (last_field) as the field with the problem */ + field_escaped = ap_escape_html(r->pool, last_field); apr_table_setn(r->notes, "error-notes", apr_psprintf(r->pool, "Size of a request header field " "after folding " "exceeds server limit.<br />\n" "<pre>\n%.*s\n</pre>\n", - field_name_len(last_field), - ap_escape_html(r->pool, last_field))); + field_name_len(field_escaped), + field_escaped)); ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00562) "Request header exceeds LimitRequestFieldSize " "after folding: %.*s", @@ -917,9 +932,11 @@ request_rec *ap_read_request(conn_rec *conn) r->allowed_methods = ap_make_method_list(p, 2); r->headers_in = apr_table_make(r->pool, 25); + r->trailers_in = apr_table_make(r->pool, 5); r->subprocess_env = apr_table_make(r->pool, 25); r->headers_out = apr_table_make(r->pool, 12); r->err_headers_out = apr_table_make(r->pool, 5); + r->trailers_out = apr_table_make(r->pool, 5); r->notes = apr_table_make(r->pool, 5); r->request_config = ap_create_request_config(r->pool); @@ -965,14 +982,17 @@ request_rec *ap_read_request(conn_rec *conn) ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00566) "request failed: invalid characters in URI"); } - ap_send_error_response(r, 0); + access_status = r->status; + r->status = HTTP_OK; + ap_die(access_status, r); ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); ap_run_log_transaction(r); + r = NULL; apr_brigade_destroy(tmp_bb); goto traceout; } else if (r->status == HTTP_REQUEST_TIME_OUT) { - ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); + ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, NULL); if (!r->connection->keepalives) { ap_run_log_transaction(r); } @@ -1185,6 +1205,7 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_rec *rnew, rnew->status = HTTP_OK; rnew->headers_in = apr_table_copy(rnew->pool, r->headers_in); + rnew->trailers_in = apr_table_copy(rnew->pool, r->trailers_in); /* did the original request have a body? (e.g. POST w/SSI tags) * if so, make sure the subrequest doesn't inherit body headers @@ -1196,6 +1217,7 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_rec *rnew, rnew->subprocess_env = apr_table_copy(rnew->pool, r->subprocess_env); rnew->headers_out = apr_table_make(rnew->pool, 5); rnew->err_headers_out = apr_table_make(rnew->pool, 5); + rnew->trailers_out = apr_table_make(rnew->pool, 5); rnew->notes = apr_table_make(rnew->pool, 5); rnew->expecting_100 = r->expecting_100; @@ -1250,8 +1272,8 @@ AP_DECLARE(void) ap_note_auth_failure(request_rec *r) ap_run_note_auth_failure(r, type); } else { - ap_log_rerror(APLOG_MARK, APLOG_ERR, - 0, r, APLOGNO(00571) "need AuthType to note auth failure: %s", r->uri); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00571) + "need AuthType to note auth failure: %s", r->uri); } } @@ -1277,8 +1299,8 @@ AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw) return DECLINED; if (!ap_auth_name(r)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, - 0, r, APLOGNO(00572) "need AuthName: %s", r->uri); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00572) + "need AuthName: %s", r->uri); return HTTP_INTERNAL_SERVER_ERROR; } @@ -1778,6 +1800,213 @@ AP_DECLARE(void) ap_send_interim_response(request_rec *r, int send_headers) apr_brigade_destroy(x.bb); } +/* + * Compare two protocol identifier. Result is similar to strcmp(): + * 0 gives same precedence, >0 means proto1 is preferred. + */ +static int protocol_cmp(const apr_array_header_t *preferences, + const char *proto1, + const char *proto2) +{ + if (preferences && preferences->nelts > 0) { + int index1 = ap_array_str_index(preferences, proto1, 0); + int index2 = ap_array_str_index(preferences, proto2, 0); + if (index2 > index1) { + return (index1 >= 0) ? 1 : -1; + } + else if (index1 > index2) { + return (index2 >= 0) ? -1 : 1; + } + } + /* both have the same index (mabye -1 or no pref configured) and we compare + * the names so that spdy3 gets precedence over spdy2. That makes + * the outcome at least deterministic. */ + return strcmp(proto1, proto2); +} + +AP_DECLARE(const char *) ap_get_protocol(conn_rec *c) +{ + const char *protocol = ap_run_protocol_get(c); + return protocol? protocol : AP_PROTOCOL_HTTP1; +} + +AP_DECLARE(apr_status_t) ap_get_protocol_upgrades(conn_rec *c, request_rec *r, + server_rec *s, int report_all, + const apr_array_header_t **pupgrades) +{ + apr_pool_t *pool = r? r->pool : c->pool; + core_server_config *conf; + const char *existing; + apr_array_header_t *upgrades = NULL; + + if (!s) { + s = (r? r->server : c->base_server); + } + conf = ap_get_core_module_config(s->module_config); + + if (conf->protocols->nelts > 0) { + existing = ap_get_protocol(c); + if (conf->protocols->nelts > 1 + || !ap_array_str_contains(conf->protocols, existing)) { + int i; + + /* possibly more than one choice or one, but not the + * existing. (TODO: maybe 426 and Upgrade then?) */ + upgrades = apr_array_make(pool, conf->protocols->nelts + 1, + sizeof(char *)); + for (i = 0; i < conf->protocols->nelts; i++) { + const char *p = APR_ARRAY_IDX(conf->protocols, i, char *); + if (strcmp(existing, p)) { + /* not the one we have and possible, add in this order */ + APR_ARRAY_PUSH(upgrades, const char*) = p; + } + else if (!report_all) { + break; + } + } + } + } + + *pupgrades = upgrades; + return APR_SUCCESS; +} + +AP_DECLARE(const char *) ap_select_protocol(conn_rec *c, request_rec *r, + server_rec *s, + const apr_array_header_t *choices) +{ + apr_pool_t *pool = r? r->pool : c->pool; + core_server_config *conf; + const char *protocol = NULL, *existing; + apr_array_header_t *proposals; + + if (!s) { + s = (r? r->server : c->base_server); + } + conf = ap_get_core_module_config(s->module_config); + + if (APLOGcdebug(c)) { + const char *p = apr_array_pstrcat(pool, conf->protocols, ','); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03155) + "select protocol from %s, choices=%s for server %s", + p, apr_array_pstrcat(pool, choices, ','), + s->server_hostname); + } + + if (conf->protocols->nelts <= 0) { + /* nothing configured, by default, we only allow http/1.1 here. + * For now... + */ + if (ap_array_str_contains(choices, AP_PROTOCOL_HTTP1)) { + return AP_PROTOCOL_HTTP1; + } + else { + return NULL; + } + } + + proposals = apr_array_make(pool, choices->nelts + 1, sizeof(char *)); + ap_run_protocol_propose(c, r, s, choices, proposals); + + /* If the existing protocol has not been proposed, but is a choice, + * add it to the proposals implicitly. + */ + existing = ap_get_protocol(c); + if (!ap_array_str_contains(proposals, existing) + && ap_array_str_contains(choices, existing)) { + APR_ARRAY_PUSH(proposals, const char*) = existing; + } + + if (proposals->nelts > 0) { + int i; + const apr_array_header_t *prefs = NULL; + + /* Default for protocols_honor_order is 'on' or != 0 */ + if (conf->protocols_honor_order == 0 && choices->nelts > 0) { + prefs = choices; + } + else { + prefs = conf->protocols; + } + + /* Select the most preferred protocol */ + if (APLOGcdebug(c)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03156) + "select protocol, proposals=%s preferences=%s configured=%s", + apr_array_pstrcat(pool, proposals, ','), + apr_array_pstrcat(pool, prefs, ','), + apr_array_pstrcat(pool, conf->protocols, ',')); + } + for (i = 0; i < proposals->nelts; ++i) { + const char *p = APR_ARRAY_IDX(proposals, i, const char *); + if (!ap_array_str_contains(conf->protocols, p)) { + /* not a configured protocol here */ + continue; + } + else if (!protocol + || (protocol_cmp(prefs, protocol, p) < 0)) { + /* none selected yet or this one has preference */ + protocol = p; + } + } + } + if (APLOGcdebug(c)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03157) + "selected protocol=%s", + protocol? protocol : "(none)"); + } + + return protocol; +} + +AP_DECLARE(apr_status_t) ap_switch_protocol(conn_rec *c, request_rec *r, + server_rec *s, + const char *protocol) +{ + const char *current = ap_get_protocol(c); + int rc; + + if (!strcmp(current, protocol)) { + ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(02906) + "already at it, protocol_switch to %s", + protocol); + return APR_SUCCESS; + } + + rc = ap_run_protocol_switch(c, r, s, protocol); + switch (rc) { + case DECLINED: + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02907) + "no implementation for protocol_switch to %s", + protocol); + return APR_ENOTIMPL; + case OK: + case DONE: + return APR_SUCCESS; + default: + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02905) + "unexpected return code %d from protocol_switch to %s" + , rc, protocol); + return APR_EOF; + } +} + +AP_DECLARE(int) ap_is_allowed_protocol(conn_rec *c, request_rec *r, + server_rec *s, const char *protocol) +{ + core_server_config *conf; + + if (!s) { + s = (r? r->server : c->base_server); + } + conf = ap_get_core_module_config(s->module_config); + + if (conf->protocols->nelts > 0) { + return ap_array_str_contains(conf->protocols, protocol); + } + return !strcmp(AP_PROTOCOL_HTTP1, protocol); +} + AP_IMPLEMENT_HOOK_VOID(pre_read_request, (request_rec *r, conn_rec *c), @@ -1793,3 +2022,14 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(unsigned short,default_port, AP_IMPLEMENT_HOOK_RUN_FIRST(int, note_auth_failure, (request_rec *r, const char *auth_type), (r, auth_type), DECLINED) +AP_IMPLEMENT_HOOK_RUN_ALL(int,protocol_propose, + (conn_rec *c, request_rec *r, server_rec *s, + const apr_array_header_t *offers, + apr_array_header_t *proposals), + (c, r, s, offers, proposals), OK, DECLINED) +AP_IMPLEMENT_HOOK_RUN_FIRST(int,protocol_switch, + (conn_rec *c, request_rec *r, server_rec *s, + const char *protocol), + (c, r, s, protocol), DECLINED) +AP_IMPLEMENT_HOOK_RUN_FIRST(const char *,protocol_get, + (const conn_rec *c), (c), NULL) |