From: Stefan Eissing Date: Fri, 23 Oct 2015 15:14:50 +0000 (+0000) Subject: announce protocol choices on first request X-Git-Tag: 2.5.0-alpha~2698 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=87b2c32276bec17d8976a99d8401300a837f1bdf;p=thirdparty%2Fapache%2Fhttpd.git announce protocol choices on first request git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1710231 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 37050ca6aea..028249ea593 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,11 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 + *) core: add ap_get_protocol_upgrades() to retrieve the list of protocols + that a client could possibly upgrade to. Use in first request on a + connection to announce protocol choices. + [Stefan Eissing] + *) mod_http2: reworked deallocation on connection shutdown and worker abort. Separate parent pool for all workers. worker threads are joined on planned worker shutdown. diff --git a/include/http_protocol.h b/include/http_protocol.h index f9110385cd5..20e1b782e74 100644 --- a/include/http_protocol.h +++ b/include/http_protocol.h @@ -782,7 +782,22 @@ AP_DECLARE_HOOK(int,protocol_switch,(conn_rec *c, request_rec *r, * @return The identifier of the protocol in place or NULL */ AP_DECLARE_HOOK(const char *,protocol_get,(const conn_rec *c)) - + +/** + * Get the protocols that the connection and optional request may + * upgrade to - besides the protocol currently active on the connection. These + * values may be used to announce to a client what choices it has. + * + * @param c The current connection + * @param r The current request or NULL + * @param s The server/virtual host selected or NULL + * @param pupgrades on return, possible protocols to upgrade to in descending order + * of preference. Maybe NULL if none are available. + */ +AP_DECLARE(apr_status_t) ap_get_protocol_upgrades(conn_rec *c, request_rec *r, + server_rec *s, + const apr_array_header_t **pupgrades); + /** * Select a protocol for the given connection and optional request. Will return * the protocol identifier selected which may be the protocol already in place diff --git a/server/core.c b/server/core.c index 6ffeb19240c..5b8f230a99c 100644 --- a/server/core.c +++ b/server/core.c @@ -5363,6 +5363,18 @@ static int core_upgrade_handler(request_rec *r) } } } + else if (!r->connection->keepalives) { + /* first request on connection, if we have protocols other + * than the current one enabled here, announce them to the + * client */ + const apr_array_header_t *upgrades; + ap_get_protocol_upgrades(r->connection, r, NULL, &upgrades); + if (upgrades && upgrades->nelts > 0) { + char *protocols = apr_array_pstrcat(r->pool, upgrades, ','); + apr_table_setn(r->headers_out, "Upgrade", protocols); + apr_table_setn(r->headers_out, "Connection", "Upgrade"); + } + } return DECLINED; } diff --git a/server/protocol.c b/server/protocol.c index 33bf1488477..fc2a20f6023 100644 --- a/server/protocol.c +++ b/server/protocol.c @@ -1982,6 +1982,42 @@ AP_DECLARE(const char *) ap_get_protocol(conn_rec *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, + 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)) { + /* 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 (int 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; + } + } + } + } + + *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)