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;
return rv;
}
- /* Something horribly wrong happened. Someone didn't block! */
+ /* Something horribly wrong happened. Someone didn't block!
+ * (this also happens at the end of each keepalive connection)
+ */
if (APR_BRIGADE_EMPTY(bb)) {
return APR_EGENERAL;
}
*/
if (do_alloc) {
tmp = NULL;
- } else {
+ }
+ else {
/* We're null terminated. */
tmp = last_char;
}
if (status == APR_SUCCESS) {
/* if it has a scheme we may need to do absoluteURI vhost stuff */
if (r->parsed_uri.scheme
- && !strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r))) {
+ && !ap_cstr_casecmp(r->parsed_uri.scheme, ap_http_scheme(r))) {
r->hostname = r->parsed_uri.hostname;
}
else if (r->method_number == M_CONNECT) {
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;
+ 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_STRICT;
int enforce_strict = !(conf->http_conformance & AP_HTTP_CONFORMANCE_LOGONLY);
- if (max_blank_lines <= 0) {
- max_blank_lines = DEFAULT_LIMIT_REQUEST_FIELDS;
- }
-
/* Read past empty lines until we get a real request line,
* a read error, the connection closes (EOF), or we timeout.
*
* happen if it exceeds the configured limit for a request-line.
*/
if (APR_STATUS_IS_ENOSPC(rv)) {
- r->status = HTTP_REQUEST_URI_TOO_LARGE;
+ r->status = HTTP_REQUEST_URI_TOO_LARGE;
}
else if (APR_STATUS_IS_TIMEUP(rv)) {
r->status = HTTP_REQUEST_TIME_OUT;
r->status = HTTP_BAD_REQUEST;
}
r->proto_num = HTTP_VERSION(1,0);
- r->protocol = apr_pstrdup(r->pool, "HTTP/1.0");
+ r->protocol = "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,
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 = "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);
}
ap_parse_uri(r, uri);
+ if (r->status != HTTP_OK) {
+ r->proto_num = HTTP_VERSION(1,0);
+ r->protocol = "HTTP/1.0";
+ return 0;
+ }
if (ll[0]) {
r->assbackwards = 0;
pro = ll;
len = strlen(ll);
- } else {
+ }
+ else {
r->assbackwards = 1;
pro = "HTTP/0.9";
len = 8;
"Invalid protocol '%s'", r->protocol);
if (enforce_strict) {
r->proto_num = HTTP_VERSION(1,0);
- r->protocol = apr_pstrdup(r->pool, "HTTP/1.0");
+ r->protocol = "HTTP/1.0";
r->connection->keepalive = AP_CONN_CLOSE;
r->status = HTTP_BAD_REQUEST;
return 0;
}
}
if (3 == sscanf(r->protocol, "%4s/%u.%u", http, &major, &minor)
- && (strcasecmp("http", http) == 0)
+ && (ap_cstr_casecmp("http", http) == 0)
&& (minor < HTTP_VERSION(1, 0)) ) { /* don't allow HTTP/0.1000 */
r->proto_num = HTTP_VERSION(major, minor);
}
*/
while(1) {
apr_status_t rv;
- int folded = 0;
field = NULL;
rv = ap_rgetline(&field, r->server->limit_req_fieldsize + 2,
*/
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);
return;
}
- if (last_field != NULL) {
- if ((len > 0) && ((*field == '\t') || *field == ' ')) {
- /* This line is a continuation of the preceding line(s),
- * so append it to the line that we've set aside.
- * Note: this uses a power-of-two allocator to avoid
- * doing O(n) allocs and using O(n^2) space for
- * continuations that span many many lines.
- */
- apr_size_t fold_len = last_len + len + 1; /* trailing null */
+ /* Process an obs-fold immediately by appending it to last_field */
+ if ((*field == '\t') || *field == ' ') {
- if (fold_len >= (apr_size_t)(r->server->limit_req_fieldsize)) {
- r->status = HTTP_BAD_REQUEST;
- /* report what we have accumulated so far before the
- * overflow (last_field) as the field with the problem
- */
- 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)));
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00562)
- "Request header exceeds LimitRequestFieldSize "
- "after folding: %.*s",
- field_name_len(last_field), last_field);
- return;
- }
+ if (last_field == NULL) {
+ r->status = HTTP_BAD_REQUEST;
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03442)
+ "Line folding encountered before first"
+ " header line");
+ return;
+ }
+
+ /* This line is a continuation of the preceding line(s),
+ * so append it to the line that we've set aside.
+ * Note: this uses a power-of-two allocator to avoid
+ * doing O(n) allocs and using O(n^2) space for
+ * continuations that span many many lines.
+ */
+ 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(field_escaped),
+ field_escaped));
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00562)
+ "Request header exceeds LimitRequestFieldSize "
+ "after folding: %.*s",
+ field_name_len(last_field), last_field);
+ return;
+ }
+
+ if (fold_len > alloc_len) {
+ char *fold_buf;
+ alloc_len += alloc_len;
if (fold_len > alloc_len) {
- char *fold_buf;
- alloc_len += alloc_len;
- if (fold_len > alloc_len) {
- alloc_len = fold_len;
- }
- fold_buf = (char *)apr_palloc(r->pool, alloc_len);
- memcpy(fold_buf, last_field, last_len);
- last_field = fold_buf;
+ alloc_len = fold_len;
}
- memcpy(last_field + last_len, field, len +1); /* +1 for nul */
- last_len += len;
- folded = 1;
+ fold_buf = (char *)apr_palloc(r->pool, alloc_len);
+ memcpy(fold_buf, last_field, last_len);
+ last_field = fold_buf;
}
- else /* not a continuation line */ {
-
- if (r->server->limit_req_fields
- && (++fields_read > r->server->limit_req_fields)) {
- r->status = HTTP_BAD_REQUEST;
- apr_table_setn(r->notes, "error-notes",
- "The number of request header fields "
- "exceeds this server's limit.");
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00563)
- "Number of request headers exceeds "
- "LimitRequestFields");
- return;
- }
+ memcpy(last_field + last_len, field, len +1); /* +1 for nul */
+ /* Replace obs-fold w/ SP per RFC 7230 3.2.4 */
+ if (conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT) {
+ last_field[last_len] = ' ';
+ }
+ last_len += len;
- if (!(value = strchr(last_field, ':'))) { /* Find ':' or */
- r->status = HTTP_BAD_REQUEST; /* abort bad request */
- apr_table_setn(r->notes, "error-notes",
- apr_psprintf(r->pool,
- "Request header field is "
- "missing ':' separator.<br />\n"
- "<pre>\n%.*s</pre>\n",
- (int)LOG_NAME_MAX_LEN,
- ap_escape_html(r->pool,
- last_field)));
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00564)
- "Request header field is missing ':' "
- "separator: %.*s", (int)LOG_NAME_MAX_LEN,
- last_field);
- return;
- }
+ /* The obs-fold continuation line is merged to the last_field
+ * so continue to the next input line
+ */
+ continue;
+ }
- tmp_field = value - 1; /* last character of field-name */
+ /* Not a continuation line, so process the last_field fully composed */
- *value++ = '\0'; /* NUL-terminate at colon */
+ if (r->server->limit_req_fields
+ && (++fields_read > r->server->limit_req_fields)) {
+ r->status = HTTP_BAD_REQUEST;
+ apr_table_setn(r->notes, "error-notes",
+ "The number of request header fields "
+ "exceeds this server's limit.");
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00563)
+ "Number of request headers exceeds "
+ "LimitRequestFields");
+ return;
+ }
- while (*value == ' ' || *value == '\t') {
- ++value; /* Skip to start of value */
- }
+ if (!(conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT))
+ {
+ /* Not Strict, using the legacy parser */
- /* Strip LWS after field-name: */
- while (tmp_field > last_field
- && (*tmp_field == ' ' || *tmp_field == '\t')) {
- *tmp_field-- = '\0';
- }
+ if (!(value = strchr(last_field, ':'))) { /* Find ':' or */
+ r->status = HTTP_BAD_REQUEST; /* abort bad request */
+ apr_table_setn(r->notes, "error-notes",
+ apr_psprintf(r->pool,
+ "Request header field is "
+ "missing ':' separator.<br />\n"
+ "<pre>\n%.*s</pre>\n", (int)LOG_NAME_MAX_LEN,
+ ap_escape_html(r->pool, last_field)));
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00564)
+ "Request header field is missing ':' "
+ "separator: %.*s", (int)LOG_NAME_MAX_LEN,
+ last_field);
+ return;
+ }
- /* Strip LWS after field-value: */
- tmp_field = last_field + last_len - 1;
- while (tmp_field > value
- && (*tmp_field == ' ' || *tmp_field == '\t')) {
- *tmp_field-- = '\0';
- }
+ tmp_field = value - 1; /* last character of field-name */
- if (conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT) {
- int err = 0;
+ *value++ = '\0'; /* NUL-terminate at colon */
- if (*last_field == '\0') {
- err = HTTP_BAD_REQUEST;
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02425)
- "Empty request header field name not allowed");
- }
- else if (ap_has_cntrl(last_field)) {
- err = HTTP_BAD_REQUEST;
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02426)
- "[HTTP strict] Request header field name contains "
- "control character: %.*s",
- (int)LOG_NAME_MAX_LEN, last_field);
- }
- else if (ap_has_cntrl(value)) {
- err = HTTP_BAD_REQUEST;
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02427)
- "Request header field '%.*s' contains "
- "control character", (int)LOG_NAME_MAX_LEN,
- last_field);
- }
- if (err && !(conf->http_conformance & AP_HTTP_CONFORMANCE_LOGONLY)) {
- r->status = err;
- return;
- }
- }
- apr_table_addn(r->headers_in, last_field, value);
+ while (*value == ' ' || *value == '\t') {
+ ++value; /* Skip to start of value */
+ }
- /* reset the alloc_len so that we'll allocate a new
- * buffer if we have to do any more folding: we can't
- * use the previous buffer because its contents are
- * now part of r->headers_in
- */
- alloc_len = 0;
+ /* Strip LWS after field-name: */
+ while (tmp_field > last_field
+ && (*tmp_field == ' ' || *tmp_field == '\t')) {
+ *tmp_field-- = '\0';
+ }
+
+ /* Strip LWS after field-value: */
+ tmp_field = last_field + last_len - 1;
+ while (tmp_field > value
+ && (*tmp_field == ' ' || *tmp_field == '\t')) {
+ *tmp_field-- = '\0';
+ }
+ }
+ else /* Using strict RFC7230 parsing */
+ {
+ /* Ensure valid token chars before ':' per RFC 7230 3.2.4 */
+ value = (char *)ap_scan_http_token(last_field);
+ if ((value == last_field) || *value != ':') {
+ r->status = HTTP_BAD_REQUEST;
+ apr_table_setn(r->notes, "error-notes",
+ apr_psprintf(r->pool,
+ "Request header field name"
+ " is malformed.<br />\n"
+ "<pre>\n%.*s</pre>\n", (int)LOG_NAME_MAX_LEN,
+ ap_escape_html(r->pool, last_field)));
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02426)
+ "Request header field name is malformed: "
+ "%.*s", (int)LOG_NAME_MAX_LEN, last_field);
+ return;
+ }
+
+ *value++ = '\0'; /* NUL-terminate last_field name at ':' */
- } /* end if current line is not a continuation starting with tab */
+ while (*value == ' ' || *value == '\t') {
+ ++value; /* Skip LWS of value */
+ }
+
+ /* Find invalid, non-HT ctrl char, or the trailing NULL */
+ tmp_field = (char *)ap_scan_http_field_content(value);
+
+ /* Strip LWS after field-value, if string not empty */
+ if (*value && (*tmp_field == '\0')) {
+ tmp_field--;
+ while (*tmp_field == ' ' || *tmp_field == '\t') {
+ *tmp_field-- = '\0';
+ }
+ ++tmp_field;
+ }
+
+ /* Reject value for all garbage input (CTRLs excluding HT)
+ * e.g. only VCHAR / SP / HT / obs-text are allowed per
+ * RFC7230 3.2.6 - leave all more explicit rule enforcement
+ * for specific header handler logic later in the cycle
+ */
+ if (*tmp_field != '\0') {
+ r->status = HTTP_BAD_REQUEST;
+ apr_table_setn(r->notes, "error-notes",
+ apr_psprintf(r->pool,
+ "Request header value is malformed.<br />\n"
+ "<pre>\n%.*s</pre>\n", (int)LOG_NAME_MAX_LEN,
+ ap_escape_html(r->pool, value)));
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02427)
+ "Request header value is malformed: "
+ "%.*s", (int)LOG_NAME_MAX_LEN, value);
+ return;
+ }
}
- /* Found a blank line, stop. */
+ apr_table_addn(r->headers_in, last_field, value);
+
+ /* After recording the last_field header line, we end our read loop
+ * here upon encountering the trailing line terminating headers
+ */
if (len == 0) {
break;
}
- /* Keep track of this line so that we can parse it on
- * the next loop iteration. (In the folded case, last_field
- * has been updated already.)
+ /* Keep track of this new header line (not an obs-fold). We will merge
+ * subsequent obs_fold lines on the next loop iterations. The zero
+ * alloc_len signals we have not allocated an obs-folding buffer yet
*/
- if (!folded) {
- last_field = field;
- last_len = len;
- }
+ alloc_len = 0;
+ last_field = field;
+ last_len = len;
}
/* Combine multiple message-header fields with the same
apr_brigade_destroy(tmp_bb);
}
-request_rec *ap_read_request(conn_rec *conn)
+AP_DECLARE(request_rec *) ap_create_request(conn_rec *conn)
{
request_rec *r;
apr_pool_t *p;
- const char *expect;
- int access_status;
- apr_bucket_brigade *tmp_bb;
- apr_socket_t *csd;
- apr_interval_time_t cur_timeout;
-
apr_pool_create(&p, conn->pool);
apr_pool_tag(p, "request");
r->read_body = REQUEST_NO_BODY;
r->status = HTTP_OK; /* Until further notice */
+ r->header_only = 0;
r->the_request = NULL;
/* Begin by presuming any module can make its own path_info assumptions,
r->useragent_addr = conn->client_addr;
r->useragent_ip = conn->client_ip;
+ return r;
+}
+
+request_rec *ap_read_request(conn_rec *conn)
+{
+ const char *expect;
+ int access_status;
+ apr_bucket_brigade *tmp_bb;
+ apr_socket_t *csd;
+ apr_interval_time_t cur_timeout;
+
+
+ request_rec *r = ap_create_request(conn);
+
tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
ap_run_pre_read_request(r, conn);
}
else if (r->method == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00566)
- "request failed: invalid characters in URI");
+ "request failed: malformed request line");
}
access_status = r->status;
r->status = HTTP_OK;
apr_brigade_destroy(tmp_bb);
goto traceout;
case 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);
apr_brigade_destroy(tmp_bb);
* the final encoding ...; the server MUST respond with the 400
* (Bad Request) status code and then close the connection".
*/
- if (!(strcasecmp(tenc, "chunked") == 0 /* fast path */
+ if (!(ap_cstr_casecmp(tenc, "chunked") == 0 /* fast path */
|| ap_find_last_token(r->pool, tenc, "chunked"))) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02539)
"client sent unknown Transfer-Encoding "
* unfortunately, to signal a poor man's mandatory extension that
* the server must understand or return 417 Expectation Failed.
*/
- if (strcasecmp(expect, "100-continue") == 0) {
+ if (ap_cstr_casecmp(expect, "100-continue") == 0) {
r->expecting_100 = 1;
}
else {
AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
{
- const char *auth_line = apr_table_get(r->headers_in,
- (PROXYREQ_PROXY == r->proxyreq)
- ? "Proxy-Authorization"
- : "Authorization");
- const char *t;
+ const char *t, *auth_line;
- if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic"))
+ if (!(t = ap_auth_type(r)) || ap_cstr_casecmp(t, "Basic"))
return DECLINED;
if (!ap_auth_name(r)) {
return HTTP_INTERNAL_SERVER_ERROR;
}
+ auth_line = apr_table_get(r->headers_in,
+ (PROXYREQ_PROXY == r->proxyreq)
+ ? "Proxy-Authorization" : "Authorization");
+
if (!auth_line) {
ap_note_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
- if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
+ if (ap_cstr_casecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
/* Client tried to authenticate using wrong auth scheme */
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00573)
"client used wrong authentication scheme: %s", r->uri);
ap_filter_t *f;
apr_bucket_brigade *bb;
} hdr_ptr;
+
static int send_header(void *data, const char *key, const char *val)
{
ap_fputstrs(((hdr_ptr*)data)->f, ((hdr_ptr*)data)->bb,
key, ": ", val, CRLF, NULL);
return 1;
}
+
AP_DECLARE(void) ap_send_interim_response(request_rec *r, int send_headers)
{
hdr_ptr x;
rr->expecting_100 = 0;
}
- status_line = apr_pstrcat(r->pool, AP_SERVER_PROTOCOL, " ", r->status_line, CRLF, NULL);
+ status_line = apr_pstrcat(r->pool, AP_SERVER_PROTOCOL " ", r->status_line, CRLF, NULL);
ap_xlate_proto_to_ascii(status_line, strlen(status_line));
x.f = r->connection->output_filters;
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),
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)