|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <assert.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <apr_strings.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <httpd.h>
|
|
Packit |
90a5c9 |
#include <http_core.h>
|
|
Packit |
90a5c9 |
#include <http_connection.h>
|
|
Packit |
90a5c9 |
#include <http_protocol.h>
|
|
Packit |
90a5c9 |
#include <http_request.h>
|
|
Packit |
90a5c9 |
#include <http_log.h>
|
|
Packit |
90a5c9 |
#include <http_vhost.h>
|
|
Packit |
90a5c9 |
#include <util_filter.h>
|
|
Packit |
90a5c9 |
#include <ap_mpm.h>
|
|
Packit |
90a5c9 |
#include <mod_core.h>
|
|
Packit |
90a5c9 |
#include <scoreboard.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "h2_private.h"
|
|
Packit |
90a5c9 |
#include "h2_config.h"
|
|
Packit |
90a5c9 |
#include "h2_push.h"
|
|
Packit |
90a5c9 |
#include "h2_request.h"
|
|
Packit |
90a5c9 |
#include "h2_util.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
apr_table_t *headers;
|
|
Packit |
90a5c9 |
apr_pool_t *pool;
|
|
Packit |
90a5c9 |
apr_status_t status;
|
|
Packit |
90a5c9 |
} h1_ctx;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int set_h1_header(void *ctx, const char *key, const char *value)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h1_ctx *x = ctx;
|
|
Packit |
90a5c9 |
x->status = h2_req_add_header(x->headers, x->pool, key, strlen(key),
|
|
Packit |
90a5c9 |
value, strlen(value));
|
|
Packit |
90a5c9 |
return (x->status == APR_SUCCESS)? 1 : 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_request_rcreate(h2_request **preq, apr_pool_t *pool,
|
|
Packit |
90a5c9 |
request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_request *req;
|
|
Packit |
90a5c9 |
const char *scheme, *authority, *path;
|
|
Packit |
90a5c9 |
h1_ctx x;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
*preq = NULL;
|
|
Packit |
90a5c9 |
scheme = apr_pstrdup(pool, r->parsed_uri.scheme? r->parsed_uri.scheme
|
|
Packit |
90a5c9 |
: ap_http_scheme(r));
|
|
Packit |
90a5c9 |
authority = apr_pstrdup(pool, r->hostname);
|
|
Packit |
90a5c9 |
path = apr_uri_unparse(pool, &r->parsed_uri, APR_URI_UNP_OMITSITEPART);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!r->method || !scheme || !r->hostname || !path) {
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!ap_strchr_c(authority, ':') && r->server && r->server->port) {
|
|
Packit |
90a5c9 |
apr_port_t defport = apr_uri_port_of_scheme(scheme);
|
|
Packit |
90a5c9 |
if (defport != r->server->port) {
|
|
Packit |
90a5c9 |
/* port info missing and port is not default for scheme: append */
|
|
Packit |
90a5c9 |
authority = apr_psprintf(pool, "%s:%d", authority,
|
|
Packit |
90a5c9 |
(int)r->server->port);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
req = apr_pcalloc(pool, sizeof(*req));
|
|
Packit |
90a5c9 |
req->method = apr_pstrdup(pool, r->method);
|
|
Packit |
90a5c9 |
req->scheme = scheme;
|
|
Packit |
90a5c9 |
req->authority = authority;
|
|
Packit |
90a5c9 |
req->path = path;
|
|
Packit |
90a5c9 |
req->headers = apr_table_make(pool, 10);
|
|
Packit |
90a5c9 |
if (r->server) {
|
|
Packit |
90a5c9 |
req->serialize = h2_config_geti(h2_config_sget(r->server),
|
|
Packit |
90a5c9 |
H2_CONF_SER_HEADERS);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
x.pool = pool;
|
|
Packit |
90a5c9 |
x.headers = req->headers;
|
|
Packit |
90a5c9 |
x.status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
apr_table_do(set_h1_header, &x, r->headers_in, NULL);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
*preq = req;
|
|
Packit |
90a5c9 |
return x.status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool,
|
|
Packit |
90a5c9 |
const char *name, size_t nlen,
|
|
Packit |
90a5c9 |
const char *value, size_t vlen)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (nlen <= 0) {
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (name[0] == ':') {
|
|
Packit |
90a5c9 |
/* pseudo header, see ch. 8.1.2.3, always should come first */
|
|
Packit |
90a5c9 |
if (!apr_is_empty_table(req->headers)) {
|
|
Packit |
90a5c9 |
ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool,
|
|
Packit |
90a5c9 |
APLOGNO(02917)
|
|
Packit |
90a5c9 |
"h2_request: pseudo header after request start");
|
|
Packit |
90a5c9 |
return APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (H2_HEADER_METHOD_LEN == nlen
|
|
Packit |
90a5c9 |
&& !strncmp(H2_HEADER_METHOD, name, nlen)) {
|
|
Packit |
90a5c9 |
req->method = apr_pstrndup(pool, value, vlen);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (H2_HEADER_SCHEME_LEN == nlen
|
|
Packit |
90a5c9 |
&& !strncmp(H2_HEADER_SCHEME, name, nlen)) {
|
|
Packit |
90a5c9 |
req->scheme = apr_pstrndup(pool, value, vlen);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (H2_HEADER_PATH_LEN == nlen
|
|
Packit |
90a5c9 |
&& !strncmp(H2_HEADER_PATH, name, nlen)) {
|
|
Packit |
90a5c9 |
req->path = apr_pstrndup(pool, value, vlen);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (H2_HEADER_AUTH_LEN == nlen
|
|
Packit |
90a5c9 |
&& !strncmp(H2_HEADER_AUTH, name, nlen)) {
|
|
Packit |
90a5c9 |
req->authority = apr_pstrndup(pool, value, vlen);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
char buffer[32];
|
|
Packit |
90a5c9 |
memset(buffer, 0, 32);
|
|
Packit |
90a5c9 |
strncpy(buffer, name, (nlen > 31)? 31 : nlen);
|
|
Packit |
90a5c9 |
ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, pool,
|
|
Packit |
90a5c9 |
APLOGNO(02954)
|
|
Packit |
90a5c9 |
"h2_request: ignoring unknown pseudo header %s",
|
|
Packit |
90a5c9 |
buffer);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* non-pseudo header, append to work bucket of stream */
|
|
Packit |
90a5c9 |
status = h2_req_add_header(req->headers, pool, name, nlen, value, vlen);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos, size_t raw_bytes)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *s;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* rfc7540, ch. 8.1.2.3:
|
|
Packit |
90a5c9 |
* - if we have :authority, it overrides any Host header
|
|
Packit |
90a5c9 |
* - :authority MUST be ommited when converting h1->h2, so we
|
|
Packit |
90a5c9 |
* might get a stream without, but then Host needs to be there */
|
|
Packit |
90a5c9 |
if (!req->authority) {
|
|
Packit |
90a5c9 |
const char *host = apr_table_get(req->headers, "Host");
|
|
Packit |
90a5c9 |
if (!host) {
|
|
Packit |
90a5c9 |
return APR_BADARG;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
req->authority = host;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
apr_table_setn(req->headers, "Host", req->authority);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
s = apr_table_get(req->headers, "Content-Length");
|
|
Packit |
90a5c9 |
if (!s) {
|
|
Packit |
90a5c9 |
/* HTTP/2 does not need a Content-Length for framing, but our
|
|
Packit |
90a5c9 |
* internal request processing is used to HTTP/1.1, so we
|
|
Packit |
90a5c9 |
* need to either add a Content-Length or a Transfer-Encoding
|
|
Packit |
90a5c9 |
* if any content can be expected. */
|
|
Packit |
90a5c9 |
if (!eos) {
|
|
Packit |
90a5c9 |
/* We have not seen a content-length and have no eos,
|
|
Packit |
90a5c9 |
* simulate a chunked encoding for our HTTP/1.1 infrastructure,
|
|
Packit |
90a5c9 |
* in case we have "H2SerializeHeaders on" here
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
req->chunked = 1;
|
|
Packit |
90a5c9 |
apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (apr_table_get(req->headers, "Content-Type")) {
|
|
Packit |
90a5c9 |
/* If we have a content-type, but already seen eos, no more
|
|
Packit |
90a5c9 |
* data will come. Signal a zero content length explicitly.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
apr_table_setn(req->headers, "Content-Length", "0");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
req->raw_bytes += raw_bytes;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
h2_request *h2_request_clone(apr_pool_t *p, const h2_request *src)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_request *dst = apr_pmemdup(p, src, sizeof(*dst));
|
|
Packit |
90a5c9 |
dst->method = apr_pstrdup(p, src->method);
|
|
Packit |
90a5c9 |
dst->scheme = apr_pstrdup(p, src->scheme);
|
|
Packit |
90a5c9 |
dst->authority = apr_pstrdup(p, src->authority);
|
|
Packit |
90a5c9 |
dst->path = apr_pstrdup(p, src->path);
|
|
Packit |
90a5c9 |
dst->headers = apr_table_clone(p, src->headers);
|
|
Packit |
90a5c9 |
return dst;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int access_status = HTTP_OK;
|
|
Packit |
90a5c9 |
const char *rpath;
|
|
Packit |
90a5c9 |
apr_pool_t *p;
|
|
Packit |
90a5c9 |
request_rec *r;
|
|
Packit |
90a5c9 |
const char *s;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_pool_create(&p, c->pool);
|
|
Packit |
90a5c9 |
apr_pool_tag(p, "request");
|
|
Packit |
90a5c9 |
r = apr_pcalloc(p, sizeof(request_rec));
|
|
Packit |
90a5c9 |
AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)c);
|
|
Packit |
90a5c9 |
r->pool = p;
|
|
Packit |
90a5c9 |
r->connection = c;
|
|
Packit |
90a5c9 |
r->server = c->base_server;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->user = NULL;
|
|
Packit |
90a5c9 |
r->ap_auth_type = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->allowed_methods = ap_make_method_list(p, 2);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->headers_in = apr_table_clone(r->pool, req->headers);
|
|
Packit |
90a5c9 |
r->trailers_in = apr_table_make(r->pool, 5);
|
|
Packit |
90a5c9 |
r->subprocess_env = apr_table_make(r->pool, 25);
|
|
Packit |
90a5c9 |
r->headers_out = apr_table_make(r->pool, 12);
|
|
Packit |
90a5c9 |
r->err_headers_out = apr_table_make(r->pool, 5);
|
|
Packit |
90a5c9 |
r->trailers_out = apr_table_make(r->pool, 5);
|
|
Packit |
90a5c9 |
r->notes = apr_table_make(r->pool, 5);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->request_config = ap_create_request_config(r->pool);
|
|
Packit |
90a5c9 |
/* Must be set before we run create request hook */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->proto_output_filters = c->output_filters;
|
|
Packit |
90a5c9 |
r->output_filters = r->proto_output_filters;
|
|
Packit |
90a5c9 |
r->proto_input_filters = c->input_filters;
|
|
Packit |
90a5c9 |
r->input_filters = r->proto_input_filters;
|
|
Packit |
90a5c9 |
ap_run_create_request(r);
|
|
Packit |
90a5c9 |
r->per_dir_config = r->server->lookup_defaults;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->sent_bodyct = 0; /* bytect isn't for body */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->read_length = 0;
|
|
Packit |
90a5c9 |
r->read_body = REQUEST_NO_BODY;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->status = HTTP_OK; /* Until further notice */
|
|
Packit |
90a5c9 |
r->header_only = 0;
|
|
Packit |
90a5c9 |
r->the_request = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Begin by presuming any module can make its own path_info assumptions,
|
|
Packit |
90a5c9 |
* until some module interjects and changes the value.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->useragent_addr = c->client_addr;
|
|
Packit |
90a5c9 |
r->useragent_ip = c->client_ip;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_run_pre_read_request(r, c);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Time to populate r with the data we have. */
|
|
Packit |
90a5c9 |
r->request_time = req->request_time;
|
|
Packit |
90a5c9 |
r->method = req->method;
|
|
Packit |
90a5c9 |
/* Provide quick information about the request method as soon as known */
|
|
Packit |
90a5c9 |
r->method_number = ap_method_number_of(r->method);
|
|
Packit |
90a5c9 |
if (r->method_number == M_GET && r->method[0] == 'H') {
|
|
Packit |
90a5c9 |
r->header_only = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rpath = (req->path ? req->path : "");
|
|
Packit |
90a5c9 |
ap_parse_uri(r, rpath);
|
|
Packit |
90a5c9 |
r->protocol = (char*)"HTTP/2.0";
|
|
Packit |
90a5c9 |
r->proto_num = HTTP_VERSION(2, 0);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->the_request = apr_psprintf(r->pool, "%s %s %s",
|
|
Packit |
90a5c9 |
r->method, rpath, r->protocol);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* update what we think the virtual host is based on the headers we've
|
|
Packit |
90a5c9 |
* now read. may update status.
|
|
Packit |
90a5c9 |
* Leave r->hostname empty, vhost will parse if form our Host: header,
|
|
Packit |
90a5c9 |
* otherwise we get complains about port numbers.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
r->hostname = NULL;
|
|
Packit |
90a5c9 |
ap_update_vhost_from_headers(r);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* we may have switched to another server */
|
|
Packit |
90a5c9 |
r->per_dir_config = r->server->lookup_defaults;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
s = apr_table_get(r->headers_in, "Expect");
|
|
Packit |
90a5c9 |
if (s && s[0]) {
|
|
Packit |
90a5c9 |
if (ap_cstr_casecmp(s, "100-continue") == 0) {
|
|
Packit |
90a5c9 |
r->expecting_100 = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
r->status = HTTP_EXPECTATION_FAILED;
|
|
Packit |
90a5c9 |
ap_send_error_response(r, 0);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Add the HTTP_IN filter here to ensure that ap_discard_request_body
|
|
Packit |
90a5c9 |
* called by ap_die and by ap_send_error_response works correctly on
|
|
Packit |
90a5c9 |
* status codes that do not cause the connection to be dropped and
|
|
Packit |
90a5c9 |
* in situations where the connection should be kept alive.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
ap_add_input_filter_handle(ap_http_input_filter_handle,
|
|
Packit |
90a5c9 |
NULL, r, r->connection);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (access_status != HTTP_OK
|
|
Packit |
90a5c9 |
|| (access_status = ap_run_post_read_request(r))) {
|
|
Packit |
90a5c9 |
/* Request check post hooks failed. An example of this would be a
|
|
Packit |
90a5c9 |
* request for a vhost where h2 is disabled --> 421.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03367)
|
|
Packit |
90a5c9 |
"h2_request: access_status=%d, request_create failed",
|
|
Packit |
90a5c9 |
access_status);
|
|
Packit |
90a5c9 |
ap_die(access_status, r);
|
|
Packit |
90a5c9 |
ap_update_child_status(c->sbh, SERVER_BUSY_LOG, r);
|
|
Packit |
90a5c9 |
ap_run_log_transaction(r);
|
|
Packit |
90a5c9 |
r = NULL;
|
|
Packit |
90a5c9 |
goto traceout;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method,
|
|
Packit |
90a5c9 |
(char *)r->uri, (char *)r->server->defn_name,
|
|
Packit |
90a5c9 |
r->status);
|
|
Packit |
90a5c9 |
return r;
|
|
Packit |
90a5c9 |
traceout:
|
|
Packit |
90a5c9 |
AP_READ_REQUEST_FAILURE((uintptr_t)r);
|
|
Packit |
90a5c9 |
return r;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|