summaryrefslogtreecommitdiff
path: root/server/protocol.c
diff options
context:
space:
mode:
Diffstat (limited to 'server/protocol.c')
-rw-r--r--server/protocol.c278
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)