You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2021/10/12 13:34:01 UTC
svn commit: r1894163 [3/8] - in /httpd/httpd/trunk: ./ changes-entries/
modules/http2/ test/modules/http2/
Added: httpd/httpd/trunk/modules/http2/h2_c2_filter.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_c2_filter.c?rev=1894163&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_c2_filter.c (added)
+++ httpd/httpd/trunk/modules/http2/h2_c2_filter.c Tue Oct 12 13:34:01 2021
@@ -0,0 +1,940 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <apr_date.h>
+#include <apr_lib.h>
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_connection.h>
+#include <http_protocol.h>
+#include <http_request.h>
+#include <util_time.h>
+
+#include "h2_private.h"
+#include "h2_conn_ctx.h"
+#include "h2_headers.h"
+#include "h2_c2_filter.h"
+#include "h2_c2.h"
+#include "h2_mplx.h"
+#include "h2_request.h"
+#include "h2_util.h"
+
+
+#define H2_FILTER_LOG(name, c, level, rv, msg, bb) \
+ do { \
+ if (APLOG_C_IS_LEVEL((c),(level))) { \
+ char buffer[4 * 1024]; \
+ apr_size_t len, bmax = sizeof(buffer)/sizeof(buffer[0]); \
+ len = h2_util_bb_print(buffer, bmax, "", "", (bb)); \
+ ap_log_cerror(APLOG_MARK, (level), rv, (c), \
+ "FILTER[%s]: %s %s", \
+ (name), (msg), len? buffer : ""); \
+ } \
+ } while (0)
+
+
+/* This routine is called by apr_table_do and merges all instances of
+ * the passed field values into a single array that will be further
+ * processed by some later routine. Originally intended to help split
+ * and recombine multiple Vary fields, though it is generic to any field
+ * consisting of comma/space-separated tokens.
+ */
+static int uniq_field_values(void *d, const char *key, const char *val)
+{
+ apr_array_header_t *values;
+ char *start;
+ char *e;
+ char **strpp;
+ int i;
+
+ (void)key;
+ values = (apr_array_header_t *)d;
+
+ e = apr_pstrdup(values->pool, val);
+
+ do {
+ /* Find a non-empty fieldname */
+
+ while (*e == ',' || apr_isspace(*e)) {
+ ++e;
+ }
+ if (*e == '\0') {
+ break;
+ }
+ start = e;
+ while (*e != '\0' && *e != ',' && !apr_isspace(*e)) {
+ ++e;
+ }
+ if (*e != '\0') {
+ *e++ = '\0';
+ }
+
+ /* Now add it to values if it isn't already represented.
+ * Could be replaced by a ap_array_strcasecmp() if we had one.
+ */
+ for (i = 0, strpp = (char **) values->elts; i < values->nelts;
+ ++i, ++strpp) {
+ if (*strpp && apr_strnatcasecmp(*strpp, start) == 0) {
+ break;
+ }
+ }
+ if (i == values->nelts) { /* if not found */
+ *(char **)apr_array_push(values) = start;
+ }
+ } while (*e != '\0');
+
+ return 1;
+}
+
+/*
+ * Since some clients choke violently on multiple Vary fields, or
+ * Vary fields with duplicate tokens, combine any multiples and remove
+ * any duplicates.
+ */
+static void fix_vary(request_rec *r)
+{
+ apr_array_header_t *varies;
+
+ varies = apr_array_make(r->pool, 5, sizeof(char *));
+
+ /* Extract all Vary fields from the headers_out, separate each into
+ * its comma-separated fieldname values, and then add them to varies
+ * if not already present in the array.
+ */
+ apr_table_do(uniq_field_values, varies, r->headers_out, "Vary", NULL);
+
+ /* If we found any, replace old Vary fields with unique-ified value */
+
+ if (varies->nelts > 0) {
+ apr_table_setn(r->headers_out, "Vary",
+ apr_array_pstrcat(r->pool, varies, ','));
+ }
+}
+
+static h2_headers *create_response(request_rec *r)
+{
+ const char *clheader;
+ const char *ctype;
+
+ /*
+ * Now that we are ready to send a response, we need to combine the two
+ * header field tables into a single table. If we don't do this, our
+ * later attempts to set or unset a given fieldname might be bypassed.
+ */
+ if (!apr_is_empty_table(r->err_headers_out)) {
+ r->headers_out = apr_table_overlay(r->pool, r->err_headers_out,
+ r->headers_out);
+ apr_table_clear(r->err_headers_out);
+ }
+
+ /*
+ * Remove the 'Vary' header field if the client can't handle it.
+ * Since this will have nasty effects on HTTP/1.1 caches, force
+ * the response into HTTP/1.0 mode.
+ */
+ if (apr_table_get(r->subprocess_env, "force-no-vary") != NULL) {
+ apr_table_unset(r->headers_out, "Vary");
+ r->proto_num = HTTP_VERSION(1,0);
+ apr_table_setn(r->subprocess_env, "force-response-1.0", "1");
+ }
+ else {
+ fix_vary(r);
+ }
+
+ /*
+ * Now remove any ETag response header field if earlier processing
+ * says so (such as a 'FileETag None' directive).
+ */
+ if (apr_table_get(r->notes, "no-etag") != NULL) {
+ apr_table_unset(r->headers_out, "ETag");
+ }
+
+ /* determine the protocol and whether we should use keepalives. */
+ ap_set_keepalive(r);
+
+ if (AP_STATUS_IS_HEADER_ONLY(r->status)) {
+ apr_table_unset(r->headers_out, "Transfer-Encoding");
+ apr_table_unset(r->headers_out, "Content-Length");
+ r->content_type = r->content_encoding = NULL;
+ r->content_languages = NULL;
+ r->clength = r->chunked = 0;
+ }
+ else if (r->chunked) {
+ apr_table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
+ apr_table_unset(r->headers_out, "Content-Length");
+ }
+
+ ctype = ap_make_content_type(r, r->content_type);
+ if (ctype) {
+ apr_table_setn(r->headers_out, "Content-Type", ctype);
+ }
+
+ if (r->content_encoding) {
+ apr_table_setn(r->headers_out, "Content-Encoding",
+ r->content_encoding);
+ }
+
+ if (!apr_is_empty_array(r->content_languages)) {
+ unsigned int i;
+ char *token;
+ char **languages = (char **)(r->content_languages->elts);
+ const char *field = apr_table_get(r->headers_out, "Content-Language");
+
+ while (field && (token = ap_get_list_item(r->pool, &field)) != NULL) {
+ for (i = 0; i < r->content_languages->nelts; ++i) {
+ if (!apr_strnatcasecmp(token, languages[i]))
+ break;
+ }
+ if (i == r->content_languages->nelts) {
+ *((char **) apr_array_push(r->content_languages)) = token;
+ }
+ }
+
+ field = apr_array_pstrcat(r->pool, r->content_languages, ',');
+ apr_table_setn(r->headers_out, "Content-Language", field);
+ }
+
+ /*
+ * Control cachability for non-cachable responses if not already set by
+ * some other part of the server configuration.
+ */
+ if (r->no_cache && !apr_table_get(r->headers_out, "Expires")) {
+ char *date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
+ ap_recent_rfc822_date(date, r->request_time);
+ apr_table_add(r->headers_out, "Expires", date);
+ }
+
+ /* This is a hack, but I can't find anyway around it. The idea is that
+ * we don't want to send out 0 Content-Lengths if it is a head request.
+ * This happens when modules try to outsmart the server, and return
+ * if they see a HEAD request. Apache 1.3 handlers were supposed to
+ * just return in that situation, and the core handled the HEAD. In
+ * 2.0, if a handler returns, then the core sends an EOS bucket down
+ * the filter stack, and the content-length filter computes a C-L of
+ * zero and that gets put in the headers, and we end up sending a
+ * zero C-L to the client. We can't just remove the C-L filter,
+ * because well behaved 2.0 handlers will send their data down the stack,
+ * and we will compute a real C-L for the head request. RBB
+ */
+ if (r->header_only
+ && (clheader = apr_table_get(r->headers_out, "Content-Length"))
+ && !strcmp(clheader, "0")) {
+ apr_table_unset(r->headers_out, "Content-Length");
+ }
+
+ /*
+ * keep the set-by-proxy server and date headers, otherwise
+ * generate a new server header / date header
+ */
+ if (r->proxyreq != PROXYREQ_NONE
+ && !apr_table_get(r->headers_out, "Date")) {
+ char *date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
+ ap_recent_rfc822_date(date, r->request_time);
+ apr_table_setn(r->headers_out, "Date", date );
+ }
+ if (r->proxyreq != PROXYREQ_NONE
+ && !apr_table_get(r->headers_out, "Server")) {
+ const char *us = ap_get_server_banner();
+ if (us) {
+ apr_table_setn(r->headers_out, "Server", us);
+ }
+ }
+
+ return h2_headers_rcreate(r, r->status, r->headers_out, r->pool);
+}
+
+typedef enum {
+ H2_RP_STATUS_LINE,
+ H2_RP_HEADER_LINE,
+ H2_RP_DONE
+} h2_rp_state_t;
+
+typedef struct h2_response_parser h2_response_parser;
+struct h2_response_parser {
+ const char *id;
+ h2_rp_state_t state;
+ conn_rec *c;
+ apr_pool_t *pool;
+ int http_status;
+ apr_array_header_t *hlines;
+ apr_bucket_brigade *tmp;
+ apr_bucket_brigade *saveto;
+};
+
+static apr_status_t parse_header(h2_response_parser *parser, char *line) {
+ const char *hline;
+ if (line[0] == ' ' || line[0] == '\t') {
+ char **plast;
+ /* continuation line from the header before this */
+ while (line[0] == ' ' || line[0] == '\t') {
+ ++line;
+ }
+
+ plast = apr_array_pop(parser->hlines);
+ if (plast == NULL) {
+ /* not well formed */
+ return APR_EINVAL;
+ }
+ hline = apr_psprintf(parser->pool, "%s %s", *plast, line);
+ }
+ else {
+ /* new header line */
+ hline = apr_pstrdup(parser->pool, line);
+ }
+ APR_ARRAY_PUSH(parser->hlines, const char*) = hline;
+ return APR_SUCCESS;
+}
+
+static apr_status_t get_line(h2_response_parser *parser, apr_bucket_brigade *bb,
+ char *line, apr_size_t len)
+{
+ apr_status_t status;
+
+ if (!parser->tmp) {
+ parser->tmp = apr_brigade_create(parser->pool, parser->c->bucket_alloc);
+ }
+ status = apr_brigade_split_line(parser->tmp, bb, APR_BLOCK_READ,
+ len);
+ if (status == APR_SUCCESS) {
+ --len;
+ status = apr_brigade_flatten(parser->tmp, line, &len);
+ if (status == APR_SUCCESS) {
+ /* we assume a non-0 containing line and remove trailing crlf. */
+ line[len] = '\0';
+ /*
+ * XXX: What to do if there is an LF but no CRLF?
+ * Should we error out?
+ */
+ if (len >= 2 && !strcmp(H2_CRLF, line + len - 2)) {
+ len -= 2;
+ line[len] = '\0';
+ apr_brigade_cleanup(parser->tmp);
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, parser->c,
+ "h2_c2(%s): read response line: %s",
+ parser->id, line);
+ }
+ else {
+ apr_off_t brigade_length;
+
+ /*
+ * If the brigade parser->tmp becomes longer than our buffer
+ * for flattening we never have a chance to get a complete
+ * line. This can happen if we are called multiple times after
+ * previous calls did not find a H2_CRLF and we returned
+ * APR_EAGAIN. In this case parser->tmp (correctly) grows
+ * with each call to apr_brigade_split_line.
+ *
+ * XXX: Currently a stack based buffer of HUGE_STRING_LEN is
+ * used. This means we cannot cope with lines larger than
+ * HUGE_STRING_LEN which might be an issue.
+ */
+ status = apr_brigade_length(parser->tmp, 0, &brigade_length);
+ if ((status != APR_SUCCESS) || (brigade_length > len)) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, parser->c, APLOGNO(10257)
+ "h2_c2(%s): read response, line too long",
+ parser->id);
+ return APR_ENOSPC;
+ }
+ /* this does not look like a complete line yet */
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, parser->c,
+ "h2_c2(%s): read response, incomplete line: %s",
+ parser->id, line);
+ if (!parser->saveto) {
+ parser->saveto = apr_brigade_create(parser->pool,
+ parser->c->bucket_alloc);
+ }
+ /*
+ * Be on the save side and save the parser->tmp brigade
+ * as it could contain transient buckets which could be
+ * invalid next time we are here.
+ *
+ * NULL for the filter parameter is ok since we
+ * provide our own brigade as second parameter
+ * and ap_save_brigade does not need to create one.
+ */
+ ap_save_brigade(NULL, &(parser->saveto), &(parser->tmp),
+ parser->tmp->p);
+ APR_BRIGADE_CONCAT(parser->tmp, parser->saveto);
+ return APR_EAGAIN;
+ }
+ }
+ }
+ apr_brigade_cleanup(parser->tmp);
+ return status;
+}
+
+static apr_table_t *make_table(h2_response_parser *parser)
+{
+ apr_array_header_t *hlines = parser->hlines;
+ if (hlines) {
+ apr_table_t *headers = apr_table_make(parser->pool, hlines->nelts);
+ int i;
+
+ for (i = 0; i < hlines->nelts; ++i) {
+ char *hline = ((char **)hlines->elts)[i];
+ char *sep = ap_strchr(hline, ':');
+ if (!sep) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, parser->c,
+ APLOGNO(02955) "h2_c2(%s): invalid header[%d] '%s'",
+ parser->id, i, (char*)hline);
+ /* not valid format, abort */
+ return NULL;
+ }
+ (*sep++) = '\0';
+ while (*sep == ' ' || *sep == '\t') {
+ ++sep;
+ }
+
+ if (!h2_util_ignore_header(hline)) {
+ apr_table_merge(headers, hline, sep);
+ }
+ }
+ return headers;
+ }
+ else {
+ return apr_table_make(parser->pool, 0);
+ }
+}
+
+static apr_status_t pass_response(h2_conn_ctx_t *conn_ctx, ap_filter_t *f,
+ h2_response_parser *parser)
+{
+ apr_bucket *b;
+ apr_status_t status;
+
+ h2_headers *response = h2_headers_create(parser->http_status,
+ make_table(parser),
+ NULL, 0, parser->pool);
+ apr_brigade_cleanup(parser->tmp);
+ b = h2_bucket_headers_create(parser->c->bucket_alloc, response);
+ APR_BRIGADE_INSERT_TAIL(parser->tmp, b);
+ b = apr_bucket_flush_create(parser->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(parser->tmp, b);
+ status = ap_pass_brigade(f->next, parser->tmp);
+ apr_brigade_cleanup(parser->tmp);
+
+ /* reset parser for possible next response */
+ parser->state = H2_RP_STATUS_LINE;
+ apr_array_clear(parser->hlines);
+
+ if (response->status >= 200) {
+ conn_ctx->has_final_response = 1;
+ }
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, parser->c,
+ APLOGNO(03197) "h2_c2(%s): passed response %d",
+ parser->id, response->status);
+ return status;
+}
+
+static apr_status_t parse_status(h2_response_parser *parser, char *line)
+{
+ int sindex = (apr_date_checkmask(line, "HTTP/#.# ###*")? 9 :
+ (apr_date_checkmask(line, "HTTP/# ###*")? 7 : 0));
+ if (sindex > 0) {
+ int k = sindex + 3;
+ char keepchar = line[k];
+ line[k] = '\0';
+ parser->http_status = atoi(&line[sindex]);
+ line[k] = keepchar;
+ parser->state = H2_RP_HEADER_LINE;
+
+ return APR_SUCCESS;
+ }
+ /* Seems like there is garbage on the connection. May be a leftover
+ * from a previous proxy request.
+ * This should only happen if the H2_RESPONSE filter is not yet in
+ * place (post_read_request has not been reached and the handler wants
+ * to write something. Probably just the interim response we are
+ * waiting for. But if there is other data hanging around before
+ * that, this needs to fail. */
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, parser->c, APLOGNO(03467)
+ "h2_c2(%s): unable to parse status line: %s",
+ parser->id, line);
+ return APR_EINVAL;
+}
+
+static apr_status_t parse_response(h2_response_parser *parser,
+ h2_conn_ctx_t *conn_ctx,
+ ap_filter_t* f, apr_bucket_brigade *bb)
+{
+ char line[HUGE_STRING_LEN];
+ apr_status_t status = APR_SUCCESS;
+
+ while (!APR_BRIGADE_EMPTY(bb) && status == APR_SUCCESS) {
+ switch (parser->state) {
+ case H2_RP_STATUS_LINE:
+ case H2_RP_HEADER_LINE:
+ status = get_line(parser, bb, line, sizeof(line));
+ if (status == APR_EAGAIN) {
+ /* need more data */
+ return APR_SUCCESS;
+ }
+ else if (status != APR_SUCCESS) {
+ return status;
+ }
+ if (parser->state == H2_RP_STATUS_LINE) {
+ /* instead of parsing, just take it directly */
+ status = parse_status(parser, line);
+ }
+ else if (line[0] == '\0') {
+ /* end of headers, pass response onward */
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, parser->c,
+ "h2_c2(%s): end of response", parser->id);
+ return pass_response(conn_ctx, f, parser);
+ }
+ else {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, parser->c,
+ "h2_c2(%s): response header %s", parser->id, line);
+ status = parse_header(parser, line);
+ }
+ break;
+
+ default:
+ return status;
+ }
+ }
+ return status;
+}
+
+apr_status_t h2_c2_filter_catch_h1_out(ap_filter_t* f, apr_bucket_brigade* bb)
+{
+ h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(f->c);
+ h2_response_parser *parser = f->ctx;
+ apr_status_t rv;
+
+ ap_assert(conn_ctx);
+ H2_FILTER_LOG("c2_catch_h1_out", f->c, APLOG_TRACE2, 0, "check", bb);
+
+ if (!conn_ctx->has_final_response) {
+ if (!parser) {
+ parser = apr_pcalloc(f->c->pool, sizeof(*parser));
+ parser->id = apr_psprintf(f->c->pool, "%s-%d", conn_ctx->id, conn_ctx->stream_id);
+ parser->pool = f->c->pool;
+ parser->c = f->c;
+ parser->state = H2_RP_STATUS_LINE;
+ parser->hlines = apr_array_make(parser->pool, 10, sizeof(char *));
+ f->ctx = parser;
+ }
+
+ if (!APR_BRIGADE_EMPTY(bb)) {
+ apr_bucket *b = APR_BRIGADE_FIRST(bb);
+ if (AP_BUCKET_IS_EOR(b)) {
+ /* TODO: Yikes, this happens when errors are encountered on input
+ * before anything from the repsonse has been processed. The
+ * ap_die_r() call will do nothing in certain conditions.
+ */
+ int result = ap_map_http_request_error(conn_ctx->last_err,
+ HTTP_INTERNAL_SERVER_ERROR);
+ request_rec *r = h2_create_request_rec(conn_ctx->request, f->c);
+ ap_die((result >= 400)? result : HTTP_INTERNAL_SERVER_ERROR, r);
+ b = ap_bucket_eor_create(f->c->bucket_alloc, r);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ }
+ }
+ /* There are cases where we need to parse a serialized http/1.1 response.
+ * One example is a 100-continue answer via a mod_proxy setup. */
+ while (bb && !f->c->aborted && !conn_ctx->has_final_response) {
+ rv = parse_response(parser, conn_ctx, f, bb);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, f->c,
+ "h2_c2(%s): parsed response", parser->id);
+ if (APR_BRIGADE_EMPTY(bb) || APR_SUCCESS != rv) {
+ return rv;
+ }
+ }
+ }
+
+ return ap_pass_brigade(f->next, bb);
+}
+
+apr_status_t h2_c2_filter_response_out(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(f->c);
+ request_rec *r = f->r;
+ apr_bucket *b, *bresp, *body_bucket = NULL, *next;
+ ap_bucket_error *eb = NULL;
+ h2_headers *response = NULL;
+ int headers_passing = 0;
+
+ H2_FILTER_LOG("c2_response_out", f->c, APLOG_TRACE1, 0, "called with", bb);
+
+ if (f->c->aborted || !conn_ctx || conn_ctx->has_final_response) {
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ if (!conn_ctx->has_final_response) {
+ /* check, if we need to send the response now. Until we actually
+ * see a DATA bucket or some EOS/EOR, we do not do so. */
+ for (b = APR_BRIGADE_FIRST(bb);
+ b != APR_BRIGADE_SENTINEL(bb);
+ b = APR_BUCKET_NEXT(b))
+ {
+ if (AP_BUCKET_IS_ERROR(b) && !eb) {
+ eb = b->data;
+ }
+ else if (AP_BUCKET_IS_EOC(b)) {
+ /* If we see an EOC bucket it is a signal that we should get out
+ * of the way doing nothing.
+ */
+ ap_remove_output_filter(f);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, f->c,
+ "h2_c2(%s): eoc bucket passed", conn_ctx->id);
+ return ap_pass_brigade(f->next, bb);
+ }
+ else if (H2_BUCKET_IS_HEADERS(b)) {
+ headers_passing = 1;
+ }
+ else if (!APR_BUCKET_IS_FLUSH(b)) {
+ body_bucket = b;
+ break;
+ }
+ }
+
+ if (eb) {
+ int st = eb->status;
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO(03047)
+ "h2_c2(%s): err bucket status=%d",
+ conn_ctx->id, st);
+ /* throw everything away and replace it with the error response
+ * generated by ap_die() */
+ apr_brigade_cleanup(bb);
+ ap_die(st, r);
+ return AP_FILTER_ERROR;
+ }
+
+ if (body_bucket || !headers_passing) {
+ /* time to insert the response bucket before the body or if
+ * no h2_headers is passed, e.g. the response is empty */
+ response = create_response(r);
+ if (response == NULL) {
+ ap_log_cerror(APLOG_MARK, APLOG_NOTICE, 0, f->c, APLOGNO(03048)
+ "h2_c2(%s): unable to create response", conn_ctx->id);
+ return APR_ENOMEM;
+ }
+
+ bresp = h2_bucket_headers_create(f->c->bucket_alloc, response);
+ if (body_bucket) {
+ APR_BUCKET_INSERT_BEFORE(body_bucket, bresp);
+ }
+ else {
+ APR_BRIGADE_INSERT_HEAD(bb, bresp);
+ }
+ conn_ctx->has_final_response = 1;
+ r->sent_bodyct = 1;
+ ap_remove_output_filter_byhandle(f->r->output_filters, "H2_C2_NET_CATCH_H1");
+ }
+ }
+
+ if (r->header_only || AP_STATUS_IS_HEADER_ONLY(r->status)) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+ "h2_c2(%s): headers only, cleanup output brigade", conn_ctx->id);
+ b = body_bucket? body_bucket : APR_BRIGADE_FIRST(bb);
+ while (b != APR_BRIGADE_SENTINEL(bb)) {
+ next = APR_BUCKET_NEXT(b);
+ if (APR_BUCKET_IS_EOS(b) || AP_BUCKET_IS_EOR(b)) {
+ break;
+ }
+ if (!H2_BUCKET_IS_HEADERS(b)) {
+ APR_BUCKET_REMOVE(b);
+ apr_bucket_destroy(b);
+ }
+ b = next;
+ }
+ }
+ if (conn_ctx->has_final_response) {
+ /* lets get out of the way, our task is done */
+ ap_remove_output_filter(f);
+ }
+ return ap_pass_brigade(f->next, bb);
+}
+
+
+struct h2_chunk_filter_t {
+ const char *id;
+ int eos_chunk_added;
+ apr_bucket_brigade *bbchunk;
+ apr_off_t chunked_total;
+};
+typedef struct h2_chunk_filter_t h2_chunk_filter_t;
+
+
+static void make_chunk(conn_rec *c, h2_chunk_filter_t *fctx, apr_bucket_brigade *bb,
+ apr_bucket *first, apr_off_t chunk_len,
+ apr_bucket *tail)
+{
+ /* Surround the buckets [first, tail[ with new buckets carrying the
+ * HTTP/1.1 chunked encoding format. If tail is NULL, the chunk extends
+ * to the end of the brigade. */
+ char buffer[128];
+ apr_bucket *b;
+ apr_size_t len;
+
+ len = (apr_size_t)apr_snprintf(buffer, H2_ALEN(buffer),
+ "%"APR_UINT64_T_HEX_FMT"\r\n", (apr_uint64_t)chunk_len);
+ b = apr_bucket_heap_create(buffer, len, NULL, bb->bucket_alloc);
+ APR_BUCKET_INSERT_BEFORE(first, b);
+ b = apr_bucket_immortal_create("\r\n", 2, bb->bucket_alloc);
+ if (tail) {
+ APR_BUCKET_INSERT_BEFORE(tail, b);
+ }
+ else {
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ }
+ fctx->chunked_total += chunk_len;
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+ "h2_c2(%s): added chunk %ld, total %ld",
+ fctx->id, (long)chunk_len, (long)fctx->chunked_total);
+}
+
+static int ser_header(void *ctx, const char *name, const char *value)
+{
+ apr_bucket_brigade *bb = ctx;
+ apr_brigade_printf(bb, NULL, NULL, "%s: %s\r\n", name, value);
+ return 1;
+}
+
+static apr_status_t read_and_chunk(ap_filter_t *f, h2_conn_ctx_t *conn_ctx,
+ apr_read_type_e block) {
+ h2_chunk_filter_t *fctx = f->ctx;
+ request_rec *r = f->r;
+ apr_status_t status = APR_SUCCESS;
+
+ if (!fctx->bbchunk) {
+ fctx->bbchunk = apr_brigade_create(r->pool, f->c->bucket_alloc);
+ }
+
+ if (APR_BRIGADE_EMPTY(fctx->bbchunk)) {
+ apr_bucket *b, *next, *first_data = NULL;
+ apr_bucket_brigade *tmp;
+ apr_off_t bblen = 0;
+
+ /* get more data from the lower layer filters. Always do this
+ * in larger pieces, since we handle the read modes ourself. */
+ status = ap_get_brigade(f->next, fctx->bbchunk,
+ AP_MODE_READBYTES, block, conn_ctx->mplx->stream_max_mem);
+ if (status != APR_SUCCESS) {
+ return status;
+ }
+
+ for (b = APR_BRIGADE_FIRST(fctx->bbchunk);
+ b != APR_BRIGADE_SENTINEL(fctx->bbchunk);
+ b = next) {
+ next = APR_BUCKET_NEXT(b);
+ if (APR_BUCKET_IS_METADATA(b)) {
+ if (first_data) {
+ make_chunk(f->c, fctx, fctx->bbchunk, first_data, bblen, b);
+ first_data = NULL;
+ }
+
+ if (H2_BUCKET_IS_HEADERS(b)) {
+ h2_headers *headers = h2_bucket_headers_get(b);
+
+ ap_assert(headers);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+ "h2_c2(%s-%d): receiving trailers",
+ conn_ctx->id, conn_ctx->stream_id);
+ tmp = apr_brigade_split_ex(fctx->bbchunk, b, NULL);
+ if (!apr_is_empty_table(headers->headers)) {
+ status = apr_brigade_puts(fctx->bbchunk, NULL, NULL, "0\r\n");
+ apr_table_do(ser_header, fctx->bbchunk, headers->headers, NULL);
+ status = apr_brigade_puts(fctx->bbchunk, NULL, NULL, "\r\n");
+ }
+ else {
+ status = apr_brigade_puts(fctx->bbchunk, NULL, NULL, "0\r\n\r\n");
+ }
+ r->trailers_in = apr_table_clone(r->pool, headers->headers);
+ APR_BUCKET_REMOVE(b);
+ apr_bucket_destroy(b);
+ APR_BRIGADE_CONCAT(fctx->bbchunk, tmp);
+ apr_brigade_destroy(tmp);
+ fctx->eos_chunk_added = 1;
+ }
+ else if (APR_BUCKET_IS_EOS(b)) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+ "h2_c2(%s-%d): receiving eos",
+ conn_ctx->id, conn_ctx->stream_id);
+ if (!fctx->eos_chunk_added) {
+ tmp = apr_brigade_split_ex(fctx->bbchunk, b, NULL);
+ status = apr_brigade_puts(fctx->bbchunk, NULL, NULL, "0\r\n\r\n");
+ APR_BRIGADE_CONCAT(fctx->bbchunk, tmp);
+ apr_brigade_destroy(tmp);
+ }
+ fctx->eos_chunk_added = 0;
+ }
+ }
+ else if (b->length == 0) {
+ APR_BUCKET_REMOVE(b);
+ apr_bucket_destroy(b);
+ }
+ else {
+ if (!first_data) {
+ first_data = b;
+ bblen = 0;
+ }
+ bblen += b->length;
+ }
+ }
+
+ if (first_data) {
+ make_chunk(f->c, fctx, fctx->bbchunk, first_data, bblen, NULL);
+ }
+ }
+ return status;
+}
+
+apr_status_t h2_c2_filter_request_in(ap_filter_t* f,
+ apr_bucket_brigade* bb,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes)
+{
+ h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(f->c);
+ h2_chunk_filter_t *fctx = f->ctx;
+ request_rec *r = f->r;
+ apr_status_t status = APR_SUCCESS;
+ apr_bucket *b, *next;
+ core_server_config *conf =
+ (core_server_config *) ap_get_module_config(r->server->module_config,
+ &core_module);
+ ap_assert(conn_ctx);
+
+ if (!fctx) {
+ fctx = apr_pcalloc(r->pool, sizeof(*fctx));
+ fctx->id = apr_psprintf(r->pool, "%s-%d", conn_ctx->id, conn_ctx->stream_id);
+ f->ctx = fctx;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, f->r,
+ "h2_c2(%s-%d): request input, exp=%d",
+ conn_ctx->id, conn_ctx->stream_id, r->expecting_100);
+ if (!conn_ctx->request->chunked) {
+ status = ap_get_brigade(f->next, bb, mode, block, readbytes);
+ /* pipe data through, just take care of trailers */
+ for (b = APR_BRIGADE_FIRST(bb);
+ b != APR_BRIGADE_SENTINEL(bb); b = next) {
+ next = APR_BUCKET_NEXT(b);
+ if (H2_BUCKET_IS_HEADERS(b)) {
+ h2_headers *headers = h2_bucket_headers_get(b);
+ ap_assert(headers);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+ "h2_c2(%s-%d): receiving trailers",
+ conn_ctx->id, conn_ctx->stream_id);
+ r->trailers_in = headers->headers;
+ if (conf && conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE) {
+ r->headers_in = apr_table_overlay(r->pool, r->headers_in,
+ r->trailers_in);
+ }
+ APR_BUCKET_REMOVE(b);
+ apr_bucket_destroy(b);
+ ap_remove_input_filter(f);
+
+ if (headers->raw_bytes && h2_c2_logio_add_bytes_in) {
+ h2_c2_logio_add_bytes_in(f->c, headers->raw_bytes);
+ }
+ break;
+ }
+ }
+ return status;
+ }
+
+ /* Things are more complicated. The standard HTTP input filter, which
+ * does a lot what we do not want to duplicate, also cares about chunked
+ * transfer encoding and trailers.
+ * We need to simulate chunked encoding for it to be happy.
+ */
+ if ((status = read_and_chunk(f, conn_ctx, block)) != APR_SUCCESS) {
+ return status;
+ }
+
+ if (mode == AP_MODE_EXHAUSTIVE) {
+ /* return all we have */
+ APR_BRIGADE_CONCAT(bb, fctx->bbchunk);
+ }
+ else if (mode == AP_MODE_READBYTES) {
+ status = h2_brigade_concat_length(bb, fctx->bbchunk, readbytes);
+ }
+ else if (mode == AP_MODE_SPECULATIVE) {
+ status = h2_brigade_copy_length(bb, fctx->bbchunk, readbytes);
+ }
+ else if (mode == AP_MODE_GETLINE) {
+ /* we are reading a single LF line, e.g. the HTTP headers.
+ * this has the nasty side effect to split the bucket, even
+ * though it ends with CRLF and creates a 0 length bucket */
+ status = apr_brigade_split_line(bb, fctx->bbchunk, block, HUGE_STRING_LEN);
+ if (APLOGctrace1(f->c)) {
+ char buffer[1024];
+ apr_size_t len = sizeof(buffer)-1;
+ apr_brigade_flatten(bb, buffer, &len);
+ buffer[len] = 0;
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
+ "h2_c2(%s-%d): getline: %s",
+ conn_ctx->id, conn_ctx->stream_id, buffer);
+ }
+ }
+ else {
+ /* Hmm, well. There is mode AP_MODE_EATCRLF, but we chose not
+ * to support it. Seems to work. */
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c,
+ APLOGNO(02942)
+ "h2_c2, unsupported READ mode %d", mode);
+ status = APR_ENOTIMPL;
+ }
+
+ h2_util_bb_log(f->c, conn_ctx->stream_id, APLOG_TRACE2, "returning input", bb);
+ return status;
+}
+
+apr_status_t h2_c2_filter_trailers_out(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(f->c);
+ request_rec *r = f->r;
+ apr_bucket *b, *e;
+
+ if (conn_ctx && r) {
+ /* Detect the EOS/EOR bucket and forward any trailers that may have
+ * been set to our h2_headers.
+ */
+ for (b = APR_BRIGADE_FIRST(bb);
+ b != APR_BRIGADE_SENTINEL(bb);
+ b = APR_BUCKET_NEXT(b))
+ {
+ if ((APR_BUCKET_IS_EOS(b) || AP_BUCKET_IS_EOR(b))
+ && r->trailers_out && !apr_is_empty_table(r->trailers_out)) {
+ h2_headers *headers;
+ apr_table_t *trailers;
+
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO(03049)
+ "h2_c2(%s-%d): sending trailers",
+ conn_ctx->id, conn_ctx->stream_id);
+ trailers = apr_table_clone(r->pool, r->trailers_out);
+ headers = h2_headers_rcreate(r, HTTP_OK, trailers, r->pool);
+ e = h2_bucket_headers_create(bb->bucket_alloc, headers);
+ APR_BUCKET_INSERT_BEFORE(b, e);
+ apr_table_clear(r->trailers_out);
+ ap_remove_output_filter(f);
+ break;
+ }
+ }
+ }
+
+ return ap_pass_brigade(f->next, bb);
+}
+
Added: httpd/httpd/trunk/modules/http2/h2_c2_filter.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_c2_filter.h?rev=1894163&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_c2_filter.h (added)
+++ httpd/httpd/trunk/modules/http2/h2_c2_filter.h Tue Oct 12 13:34:01 2021
@@ -0,0 +1,49 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_c2_filter__
+#define __mod_h2__h2_c2_filter__
+
+/**
+ * h2_from_h1 parses a HTTP/1.1 response into
+ * - response status
+ * - a list of header values
+ * - a series of bytes that represent the response body alone, without
+ * any meta data, such as inserted by chunked transfer encoding.
+ *
+ * All data is allocated from the stream memory pool.
+ *
+ * Again, see comments in h2_request: ideally we would take the headers
+ * and status from the httpd structures instead of parsing them here, but
+ * we need to have all handlers and filters involved in request/response
+ * processing, so this seems to be the way for now.
+ */
+struct h2_headers;
+struct h2_response_parser;
+
+apr_status_t h2_c2_filter_catch_h1_out(ap_filter_t* f, apr_bucket_brigade* bb);
+
+apr_status_t h2_c2_filter_response_out(ap_filter_t *f, apr_bucket_brigade *bb);
+
+apr_status_t h2_c2_filter_request_in(ap_filter_t* f,
+ apr_bucket_brigade* brigade,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes);
+
+apr_status_t h2_c2_filter_trailers_out(ap_filter_t *f, apr_bucket_brigade *bb);
+
+#endif /* defined(__mod_h2__h2_c2_filter__) */
Modified: httpd/httpd/trunk/modules/http2/h2_config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_config.c?rev=1894163&r1=1894162&r2=1894163&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_config.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_config.c Tue Oct 12 13:34:01 2021
@@ -30,11 +30,10 @@
#include <apr_strings.h>
#include "h2.h"
-#include "h2_alt_svc.h"
-#include "h2_ctx.h"
-#include "h2_conn.h"
+#include "h2_conn_ctx.h"
+#include "h2_c1.h"
#include "h2_config.h"
-#include "h2_h2.h"
+#include "h2_protocol.h"
#include "h2_private.h"
#define DEF_VAL (-1)
@@ -54,41 +53,37 @@
/* Apache httpd module configuration for h2. */
typedef struct h2_config {
const char *name;
- int h2_max_streams; /* max concurrent # streams (http2) */
- int h2_window_size; /* stream window size (http2) */
- int min_workers; /* min # of worker threads/child */
- int max_workers; /* max # of worker threads/child */
- int max_worker_idle_secs; /* max # of idle seconds for worker */
- int stream_max_mem_size; /* max # bytes held in memory/stream */
- apr_array_header_t *alt_svcs; /* h2_alt_svc specs for this server */
- int alt_svc_max_age; /* seconds clients can rely on alt-svc info*/
- int serialize_headers; /* Use serialized HTTP/1.1 headers for
- processing, better compatibility */
- int h2_direct; /* if mod_h2 is active directly */
- int modern_tls_only; /* Accept only modern TLS in HTTP/2 connections */
- int h2_upgrade; /* Allow HTTP/1 upgrade to h2/h2c */
- apr_int64_t tls_warmup_size; /* Amount of TLS data to send before going full write size */
- int tls_cooldown_secs; /* Seconds of idle time before going back to small TLS records */
- int h2_push; /* if HTTP/2 server push is enabled */
- struct apr_hash_t *priorities;/* map of content-type to h2_priority records */
-
- int push_diary_size; /* # of entries in push diary */
- int copy_files; /* if files shall be copied vs setaside on output */
- apr_array_header_t *push_list;/* list of h2_push_res configurations */
- int early_hints; /* support status code 103 */
+ int h2_max_streams; /* max concurrent # streams (http2) */
+ int h2_window_size; /* stream window size (http2) */
+ int min_workers; /* min # of worker threads/child */
+ int max_workers; /* max # of worker threads/child */
+ int max_worker_idle_secs; /* max # of idle seconds for worker */
+ int stream_max_mem_size; /* max # bytes held in memory/stream */
+ int h2_direct; /* if mod_h2 is active directly */
+ int modern_tls_only; /* Accept only modern TLS in HTTP/2 connections */
+ int h2_upgrade; /* Allow HTTP/1 upgrade to h2/h2c */
+ apr_int64_t tls_warmup_size; /* Amount of TLS data to send before going full write size */
+ int tls_cooldown_secs; /* Seconds of idle time before going back to small TLS records */
+ int h2_push; /* if HTTP/2 server push is enabled */
+ struct apr_hash_t *priorities; /* map of content-type to h2_priority records */
+
+ int push_diary_size; /* # of entries in push diary */
+ int copy_files; /* if files shall be copied vs setaside on output */
+ apr_array_header_t *push_list; /* list of h2_push_res configurations */
+ int early_hints; /* support status code 103 */
int padding_bits;
int padding_always;
int output_buffered;
+ apr_interval_time_t stream_timeout;/* beam timeout */
} h2_config;
typedef struct h2_dir_config {
const char *name;
- apr_array_header_t *alt_svcs; /* h2_alt_svc specs for this server */
- int alt_svc_max_age; /* seconds clients can rely on alt-svc info*/
- int h2_upgrade; /* Allow HTTP/1 upgrade to h2/h2c */
- int h2_push; /* if HTTP/2 server push is enabled */
- apr_array_header_t *push_list;/* list of h2_push_res configurations */
- int early_hints; /* support status code 103 */
+ int h2_upgrade; /* Allow HTTP/1 upgrade to h2/h2c */
+ int h2_push; /* if HTTP/2 server push is enabled */
+ apr_array_header_t *push_list; /* list of h2_push_res configurations */
+ int early_hints; /* support status code 103 */
+ apr_interval_time_t stream_timeout;/* beam timeout */
} h2_dir_config;
@@ -100,9 +95,6 @@ static h2_config defconf = {
-1, /* max workers */
10 * 60, /* max workers idle secs */
32 * 1024, /* stream max mem size */
- NULL, /* no alt-svcs */
- -1, /* alt-svc max age */
- 0, /* serialize headers */
-1, /* h2 direct mode */
1, /* modern TLS only */
-1, /* HTTP/1 Upgrade support */
@@ -116,17 +108,17 @@ static h2_config defconf = {
0, /* early hints, http status 103 */
0, /* padding bits */
1, /* padding always */
- 1, /* strean output buffered */
+ 1, /* stream output buffered */
+ -1, /* beam timeout */
};
static h2_dir_config defdconf = {
"default",
- NULL, /* no alt-svcs */
- -1, /* alt-svc max age */
-1, /* HTTP/1 Upgrade support */
-1, /* HTTP/2 server push enabled */
NULL, /* push list */
-1, /* early hints, http status 103 */
+ -1, /* beam timeout */
};
void h2_config_init(apr_pool_t *pool)
@@ -146,8 +138,6 @@ void *h2_config_create_svr(apr_pool_t *p
conf->max_workers = DEF_VAL;
conf->max_worker_idle_secs = DEF_VAL;
conf->stream_max_mem_size = DEF_VAL;
- conf->alt_svc_max_age = DEF_VAL;
- conf->serialize_headers = DEF_VAL;
conf->h2_direct = DEF_VAL;
conf->modern_tls_only = DEF_VAL;
conf->h2_upgrade = DEF_VAL;
@@ -162,6 +152,7 @@ void *h2_config_create_svr(apr_pool_t *p
conf->padding_bits = DEF_VAL;
conf->padding_always = DEF_VAL;
conf->output_buffered = DEF_VAL;
+ conf->stream_timeout = DEF_VAL;
return conf;
}
@@ -179,9 +170,6 @@ static void *h2_config_merge(apr_pool_t
n->max_workers = H2_CONFIG_GET(add, base, max_workers);
n->max_worker_idle_secs = H2_CONFIG_GET(add, base, max_worker_idle_secs);
n->stream_max_mem_size = H2_CONFIG_GET(add, base, stream_max_mem_size);
- n->alt_svcs = add->alt_svcs? add->alt_svcs : base->alt_svcs;
- n->alt_svc_max_age = H2_CONFIG_GET(add, base, alt_svc_max_age);
- n->serialize_headers = H2_CONFIG_GET(add, base, serialize_headers);
n->h2_direct = H2_CONFIG_GET(add, base, h2_direct);
n->modern_tls_only = H2_CONFIG_GET(add, base, modern_tls_only);
n->h2_upgrade = H2_CONFIG_GET(add, base, h2_upgrade);
@@ -206,6 +194,7 @@ static void *h2_config_merge(apr_pool_t
n->early_hints = H2_CONFIG_GET(add, base, early_hints);
n->padding_bits = H2_CONFIG_GET(add, base, padding_bits);
n->padding_always = H2_CONFIG_GET(add, base, padding_always);
+ n->stream_timeout = H2_CONFIG_GET(add, base, stream_timeout);
return n;
}
@@ -221,10 +210,10 @@ void *h2_config_create_dir(apr_pool_t *p
char *name = apr_pstrcat(pool, "dir[", s, "]", NULL);
conf->name = name;
- conf->alt_svc_max_age = DEF_VAL;
conf->h2_upgrade = DEF_VAL;
conf->h2_push = DEF_VAL;
conf->early_hints = DEF_VAL;
+ conf->stream_timeout = DEF_VAL;
return conf;
}
@@ -235,8 +224,6 @@ void *h2_config_merge_dir(apr_pool_t *po
h2_dir_config *n = (h2_dir_config *)apr_pcalloc(pool, sizeof(h2_dir_config));
n->name = apr_pstrcat(pool, "merged[", add->name, ", ", base->name, "]", NULL);
- n->alt_svcs = add->alt_svcs? add->alt_svcs : base->alt_svcs;
- n->alt_svc_max_age = H2_CONFIG_GET(add, base, alt_svc_max_age);
n->h2_upgrade = H2_CONFIG_GET(add, base, h2_upgrade);
n->h2_push = H2_CONFIG_GET(add, base, h2_push);
if (add->push_list && base->push_list) {
@@ -246,6 +233,7 @@ void *h2_config_merge_dir(apr_pool_t *po
n->push_list = add->push_list? add->push_list : base->push_list;
}
n->early_hints = H2_CONFIG_GET(add, base, early_hints);
+ n->stream_timeout = H2_CONFIG_GET(add, base, stream_timeout);
return n;
}
@@ -264,10 +252,6 @@ static apr_int64_t h2_srv_config_geti64(
return H2_CONFIG_GET(conf, &defconf, max_worker_idle_secs);
case H2_CONF_STREAM_MAX_MEM:
return H2_CONFIG_GET(conf, &defconf, stream_max_mem_size);
- case H2_CONF_ALT_SVC_MAX_AGE:
- return H2_CONFIG_GET(conf, &defconf, alt_svc_max_age);
- case H2_CONF_SER_HEADERS:
- return H2_CONFIG_GET(conf, &defconf, serialize_headers);
case H2_CONF_MODERN_TLS_ONLY:
return H2_CONFIG_GET(conf, &defconf, modern_tls_only);
case H2_CONF_UPGRADE:
@@ -292,6 +276,8 @@ static apr_int64_t h2_srv_config_geti64(
return H2_CONFIG_GET(conf, &defconf, padding_always);
case H2_CONF_OUTPUT_BUFFER:
return H2_CONFIG_GET(conf, &defconf, output_buffered);
+ case H2_CONF_STREAM_TIMEOUT:
+ return H2_CONFIG_GET(conf, &defconf, stream_timeout);
default:
return DEF_VAL;
}
@@ -318,12 +304,6 @@ static void h2_srv_config_seti(h2_config
case H2_CONF_STREAM_MAX_MEM:
H2_CONFIG_SET(conf, stream_max_mem_size, val);
break;
- case H2_CONF_ALT_SVC_MAX_AGE:
- H2_CONFIG_SET(conf, alt_svc_max_age, val);
- break;
- case H2_CONF_SER_HEADERS:
- H2_CONFIG_SET(conf, serialize_headers, val);
- break;
case H2_CONF_MODERN_TLS_ONLY:
H2_CONFIG_SET(conf, modern_tls_only, val);
break;
@@ -371,6 +351,9 @@ static void h2_srv_config_seti64(h2_conf
case H2_CONF_TLS_WARMUP_SIZE:
H2_CONFIG_SET(conf, tls_warmup_size, val);
break;
+ case H2_CONF_STREAM_TIMEOUT:
+ H2_CONFIG_SET(conf, stream_timeout, val);
+ break;
default:
h2_srv_config_seti(conf, var, (int)val);
break;
@@ -396,14 +379,14 @@ static const h2_dir_config *h2_config_rg
static apr_int64_t h2_dir_config_geti64(const h2_dir_config *conf, h2_config_var_t var)
{
switch(var) {
- case H2_CONF_ALT_SVC_MAX_AGE:
- return H2_CONFIG_GET(conf, &defdconf, alt_svc_max_age);
case H2_CONF_UPGRADE:
return H2_CONFIG_GET(conf, &defdconf, h2_upgrade);
case H2_CONF_PUSH:
return H2_CONFIG_GET(conf, &defdconf, h2_push);
case H2_CONF_EARLY_HINTS:
return H2_CONFIG_GET(conf, &defdconf, early_hints);
+ case H2_CONF_STREAM_TIMEOUT:
+ return H2_CONFIG_GET(conf, &defdconf, stream_timeout);
default:
return DEF_VAL;
@@ -415,9 +398,6 @@ static void h2_config_seti(h2_dir_config
int set_srv = !dconf;
if (dconf) {
switch(var) {
- case H2_CONF_ALT_SVC_MAX_AGE:
- H2_CONFIG_SET(dconf, alt_svc_max_age, val);
- break;
case H2_CONF_UPGRADE:
H2_CONFIG_SET(dconf, h2_upgrade, val);
break;
@@ -444,6 +424,9 @@ static void h2_config_seti64(h2_dir_conf
int set_srv = !dconf;
if (dconf) {
switch(var) {
+ case H2_CONF_STREAM_TIMEOUT:
+ H2_CONFIG_SET(dconf, stream_timeout, val);
+ break;
default:
/* not handled in dir_conf */
set_srv = 1;
@@ -458,18 +441,11 @@ static void h2_config_seti64(h2_dir_conf
static const h2_config *h2_config_get(conn_rec *c)
{
- h2_ctx *ctx = h2_ctx_get(c, 0);
+ h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c);
- if (ctx) {
- if (ctx->config) {
- return ctx->config;
- }
- else if (ctx->server) {
- ctx->config = h2_config_sget(ctx->server);
- return ctx->config;
- }
+ if (conn_ctx && conn_ctx->server) {
+ return h2_config_sget(conn_ctx->server);
}
-
return h2_config_sget(c->base_server);
}
@@ -526,18 +502,6 @@ apr_array_header_t *h2_config_push_list(
return sconf? sconf->push_list : NULL;
}
-apr_array_header_t *h2_config_alt_svcs(request_rec *r)
-{
- const h2_config *sconf;
- const h2_dir_config *conf = h2_config_rget(r);
-
- if (conf && conf->alt_svcs) {
- return conf->alt_svcs;
- }
- sconf = h2_config_sget(r->server);
- return sconf? sconf->alt_svcs : NULL;
-}
-
const struct h2_priority *h2_cconfig_get_priority(conn_rec *c, const char *content_type)
{
const h2_config *conf = h2_config_get(c);
@@ -615,41 +579,6 @@ static const char *h2_conf_set_stream_ma
return NULL;
}
-static const char *h2_add_alt_svc(cmd_parms *cmd,
- void *dirconf, const char *value)
-{
- if (value && *value) {
- h2_alt_svc *as = h2_alt_svc_parse(value, cmd->pool);
- if (!as) {
- return "unable to parse alt-svc specifier";
- }
-
- if (cmd->path) {
- h2_dir_config *dcfg = (h2_dir_config *)dirconf;
- if (!dcfg->alt_svcs) {
- dcfg->alt_svcs = apr_array_make(cmd->pool, 5, sizeof(h2_alt_svc*));
- }
- APR_ARRAY_PUSH(dcfg->alt_svcs, h2_alt_svc*) = as;
- }
- else {
- h2_config *cfg = (h2_config *)h2_config_sget(cmd->server);
- if (!cfg->alt_svcs) {
- cfg->alt_svcs = apr_array_make(cmd->pool, 5, sizeof(h2_alt_svc*));
- }
- APR_ARRAY_PUSH(cfg->alt_svcs, h2_alt_svc*) = as;
- }
- }
- return NULL;
-}
-
-static const char *h2_conf_set_alt_svc_max_age(cmd_parms *cmd,
- void *dirconf, const char *value)
-{
- int val = (int)apr_atoi64(value);
- CONFIG_CMD_SET(cmd, dirconf, H2_CONF_ALT_SVC_MAX_AGE, val);
- return NULL;
-}
-
static const char *h2_conf_set_session_extra_files(cmd_parms *cmd,
void *dirconf, const char *value)
{
@@ -661,18 +590,15 @@ static const char *h2_conf_set_session_e
return NULL;
}
-static const char *h2_conf_set_serialize_headers(cmd_parms *cmd,
+static const char *h2_conf_set_serialize_headers(cmd_parms *parms,
void *dirconf, const char *value)
{
if (!strcasecmp(value, "On")) {
- CONFIG_CMD_SET(cmd, dirconf, H2_CONF_SER_HEADERS, 1);
- return NULL;
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, parms->server, APLOGNO(10307)
+ "%s: this feature has been disabled and the directive "
+ "to enable it is ignored.", parms->cmd->name);
}
- else if (!strcasecmp(value, "Off")) {
- CONFIG_CMD_SET(cmd, dirconf, H2_CONF_SER_HEADERS, 0);
- return NULL;
- }
- return "value must be On or Off";
+ return NULL;
}
static const char *h2_conf_set_direct(cmd_parms *cmd,
@@ -928,6 +854,20 @@ static const char *h2_conf_set_output_bu
return "value must be On or Off";
}
+static const char *h2_conf_set_stream_timeout(cmd_parms *cmd,
+ void *dirconf, const char *value)
+{
+ apr_status_t rv;
+ apr_interval_time_t timeout;
+
+ rv = ap_timeout_parameter_parse(value, &timeout, "s");
+ if (rv != APR_SUCCESS) {
+ return "Invalid timeout value";
+ }
+ CONFIG_CMD_SET64(cmd, dirconf, H2_CONF_STREAM_TIMEOUT, timeout);
+ return NULL;
+}
+
void h2_get_num_workers(server_rec *s, int *minw, int *maxw)
{
int threads_per_child = 0;
@@ -966,12 +906,8 @@ const command_rec h2_cmds[] = {
RSRC_CONF, "maximum number of idle seconds before a worker shuts down"),
AP_INIT_TAKE1("H2StreamMaxMemSize", h2_conf_set_stream_max_mem_size, NULL,
RSRC_CONF, "maximum number of bytes buffered in memory for a stream"),
- AP_INIT_TAKE1("H2AltSvc", h2_add_alt_svc, NULL,
- RSRC_CONF, "adds an Alt-Svc for this server"),
- AP_INIT_TAKE1("H2AltSvcMaxAge", h2_conf_set_alt_svc_max_age, NULL,
- RSRC_CONF, "set the maximum age (in seconds) that client can rely on alt-svc information"),
AP_INIT_TAKE1("H2SerializeHeaders", h2_conf_set_serialize_headers, NULL,
- RSRC_CONF, "on to enable header serialization for compatibility"),
+ RSRC_CONF, "disabled, this directive has no longer an effect."),
AP_INIT_TAKE1("H2ModernTLSOnly", h2_conf_set_modern_tls_only, NULL,
RSRC_CONF, "off to not impose RFC 7540 restrictions on TLS"),
AP_INIT_TAKE1("H2Upgrade", h2_conf_set_upgrade, NULL,
@@ -1000,6 +936,8 @@ const command_rec h2_cmds[] = {
RSRC_CONF, "set payload padding"),
AP_INIT_TAKE1("H2OutputBuffering", h2_conf_set_output_buffer, NULL,
RSRC_CONF, "set stream output buffer on/off"),
+ AP_INIT_TAKE1("H2StreamTimeout", h2_conf_set_stream_timeout, NULL,
+ RSRC_CONF, "set stream timeout"),
AP_END_CMD
};
Modified: httpd/httpd/trunk/modules/http2/h2_config.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_config.h?rev=1894163&r1=1894162&r2=1894163&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_config.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_config.h Tue Oct 12 13:34:01 2021
@@ -30,9 +30,6 @@ typedef enum {
H2_CONF_MAX_WORKERS,
H2_CONF_MAX_WORKER_IDLE_SECS,
H2_CONF_STREAM_MAX_MEM,
- H2_CONF_ALT_SVCS,
- H2_CONF_ALT_SVC_MAX_AGE,
- H2_CONF_SER_HEADERS,
H2_CONF_DIRECT,
H2_CONF_MODERN_TLS_ONLY,
H2_CONF_UPGRADE,
@@ -45,6 +42,7 @@ typedef enum {
H2_CONF_PADDING_BITS,
H2_CONF_PADDING_ALWAYS,
H2_CONF_OUTPUT_BUFFER,
+ H2_CONF_STREAM_TIMEOUT,
} h2_config_var_t;
struct apr_hash_t;
@@ -88,7 +86,6 @@ int h2_config_rgeti(request_rec *r, h2_c
apr_int64_t h2_config_rgeti64(request_rec *r, h2_config_var_t var);
apr_array_header_t *h2_config_push_list(request_rec *r);
-apr_array_header_t *h2_config_alt_svcs(request_rec *r);
void h2_get_num_workers(server_rec *s, int *minw, int *maxw);
Added: httpd/httpd/trunk/modules/http2/h2_conn_ctx.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_conn_ctx.c?rev=1894163&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_conn_ctx.c (added)
+++ httpd/httpd/trunk/modules/http2/h2_conn_ctx.c Tue Oct 12 13:34:01 2021
@@ -0,0 +1,147 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_session.h"
+#include "h2_bucket_beam.h"
+#include "h2_c2.h"
+#include "h2_mplx.h"
+#include "h2_stream.h"
+#include "h2_util.h"
+#include "h2_conn_ctx.h"
+
+
+void h2_conn_ctx_detach(conn_rec *c)
+{
+ ap_set_module_config(c->conn_config, &http2_module, NULL);
+}
+
+static h2_conn_ctx_t *ctx_create(conn_rec *c, const char *id)
+{
+ h2_conn_ctx_t *conn_ctx = apr_pcalloc(c->pool, sizeof(*conn_ctx));
+ conn_ctx->id = id;
+ conn_ctx->server = c->base_server;
+ conn_ctx->started_at = apr_time_now();
+
+ ap_set_module_config(c->conn_config, &http2_module, conn_ctx);
+ return conn_ctx;
+}
+
+h2_conn_ctx_t *h2_conn_ctx_create_for_c1(conn_rec *c1, server_rec *s, const char *protocol)
+{
+ h2_conn_ctx_t *ctx;
+
+ ctx = ctx_create(c1, apr_psprintf(c1->pool, "%ld", c1->id));
+ ctx->server = s;
+ ctx->protocol = apr_pstrdup(c1->pool, protocol);
+
+ ctx->pfd_out_prod.desc_type = APR_POLL_SOCKET;
+ ctx->pfd_out_prod.desc.s = ap_get_conn_socket(c1);
+ apr_socket_opt_set(ctx->pfd_out_prod.desc.s, APR_SO_NONBLOCK, 1);
+ ctx->pfd_out_prod.reqevents = APR_POLLIN | APR_POLLERR | APR_POLLHUP;
+ ctx->pfd_out_prod.client_data = ctx;
+
+ return ctx;
+}
+
+apr_status_t h2_conn_ctx_init_for_c2(h2_conn_ctx_t **pctx, conn_rec *c2,
+ struct h2_mplx *mplx, struct h2_stream *stream)
+{
+ h2_conn_ctx_t *conn_ctx;
+ apr_status_t rv = APR_SUCCESS;
+
+ ap_assert(c2->master);
+ conn_ctx = h2_conn_ctx_get(c2);
+ if (!conn_ctx) {
+ h2_conn_ctx_t *c1_ctx;
+
+ c1_ctx = h2_conn_ctx_get(c2->master);
+ ap_assert(c1_ctx);
+ ap_assert(c1_ctx->session);
+
+ conn_ctx = ctx_create(c2, c1_ctx->id);
+ conn_ctx->server = c2->master->base_server;
+ }
+
+ conn_ctx->mplx = mplx;
+ conn_ctx->stream_id = stream->id;
+ apr_pool_create(&conn_ctx->req_pool, c2->pool);
+ apr_pool_tag(conn_ctx->req_pool, "H2_C2_REQ");
+ conn_ctx->request = stream->request;
+ conn_ctx->started_at = apr_time_now();
+ conn_ctx->done = 0;
+ conn_ctx->done_at = 0;
+
+ *pctx = conn_ctx;
+ return rv;
+}
+
+void h2_conn_ctx_clear_for_c2(conn_rec *c2)
+{
+ h2_conn_ctx_t *conn_ctx;
+
+ ap_assert(c2->master);
+ conn_ctx = h2_conn_ctx_get(c2);
+ conn_ctx->stream_id = -1;
+ conn_ctx->request = NULL;
+
+ if (conn_ctx->req_pool) {
+ apr_pool_destroy(conn_ctx->req_pool);
+ conn_ctx->req_pool = NULL;
+ conn_ctx->beam_out = NULL;
+ }
+ memset(&conn_ctx->pfd_in_drain, 0, sizeof(conn_ctx->pfd_in_drain));
+ memset(&conn_ctx->pfd_out_prod, 0, sizeof(conn_ctx->pfd_out_prod));
+ conn_ctx->beam_in = NULL;
+}
+
+void h2_conn_ctx_destroy(conn_rec *c)
+{
+ h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c);
+
+ if (conn_ctx) {
+ if (conn_ctx->mplx_pool) {
+ apr_pool_destroy(conn_ctx->mplx_pool);
+ conn_ctx->mplx_pool = NULL;
+ }
+ ap_set_module_config(c->conn_config, &http2_module, NULL);
+ }
+}
+
+void h2_conn_ctx_set_timeout(h2_conn_ctx_t *conn_ctx, apr_interval_time_t timeout)
+{
+ if (conn_ctx->beam_out) {
+ h2_beam_timeout_set(conn_ctx->beam_out, timeout);
+ }
+ if (conn_ctx->pipe_out_prod[H2_PIPE_OUT]) {
+ apr_file_pipe_timeout_set(conn_ctx->pipe_out_prod[H2_PIPE_OUT], timeout);
+ }
+
+ if (conn_ctx->beam_in) {
+ h2_beam_timeout_set(conn_ctx->beam_in, timeout);
+ }
+ if (conn_ctx->pipe_in_prod[H2_PIPE_OUT]) {
+ apr_file_pipe_timeout_set(conn_ctx->pipe_in_prod[H2_PIPE_OUT], timeout);
+ }
+}
Added: httpd/httpd/trunk/modules/http2/h2_conn_ctx.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_conn_ctx.h?rev=1894163&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_conn_ctx.h (added)
+++ httpd/httpd/trunk/modules/http2/h2_conn_ctx.h Tue Oct 12 13:34:01 2021
@@ -0,0 +1,97 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_conn_ctx__
+#define __mod_h2__h2_conn_ctx__
+
+struct h2_session;
+struct h2_stream;
+struct h2_mplx;
+struct h2_bucket_beam;
+struct h2_response_parser;
+
+#define H2_PIPE_OUT 0
+#define H2_PIPE_IN 1
+
+/**
+ * The h2 module context associated with a connection.
+ *
+ * It keeps track of the different types of connections:
+ * - those from clients that use HTTP/2 protocol
+ * - those from clients that do not use HTTP/2
+ * - those created by ourself to perform work on HTTP/2 streams
+ */
+struct h2_conn_ctx_t {
+ const char *id; /* c*: our identifier of this connection */
+ server_rec *server; /* c*: httpd server selected. */
+ const char *protocol; /* c1: the protocol negotiated */
+ struct h2_session *session; /* c1: the h2 session established */
+ struct h2_mplx *mplx; /* c2: the multiplexer */
+
+ int pre_conn_done; /* has pre_connection setup run? */
+ int stream_id; /* c1: 0, c2: stream id processed */
+ apr_pool_t *req_pool; /* c2: a c2 child pool for a request */
+ const struct h2_request *request; /* c2: the request to process */
+ struct h2_bucket_beam *beam_out; /* c2: data out, created from req_pool */
+ struct h2_bucket_beam *beam_in; /* c2: data in or NULL, borrowed from request stream */
+
+ apr_pool_t *mplx_pool; /* c2: an mplx child pool for safe use inside mplx lock */
+ apr_file_t *pipe_in_prod[2]; /* c2: input produced notification pipe */
+ apr_file_t *pipe_in_drain[2]; /* c2: input drained notification pipe */
+ apr_file_t *pipe_out_prod[2]; /* c2: output produced notification pipe */
+
+ apr_pollfd_t pfd_in_drain; /* c2: poll pipe_in_drain output */
+ apr_pollfd_t pfd_out_prod; /* c2: poll pipe_out_prod output */
+
+ int has_final_response; /* final HTTP response passed on out */
+ apr_status_t last_err; /* APR_SUCCES or last error encountered in filters */
+ struct h2_response_parser *parser; /* optional parser to catch H1 responses */
+
+ volatile int done; /* c2: processing has finished */
+ apr_time_t started_at; /* c2: when processing started */
+ apr_time_t done_at; /* c2: when processing was done */
+};
+typedef struct h2_conn_ctx_t h2_conn_ctx_t;
+
+/**
+ * Get the h2 connection context.
+ * @param c the connection to look at
+ * @return h2 context of this connection
+ */
+#define h2_conn_ctx_get(c) \
+ ((c)? (h2_conn_ctx_t*)ap_get_module_config((c)->conn_config, &http2_module) : NULL)
+
+/**
+ * Create the h2 connection context.
+ * @param c the connection to create it at
+ * @param s the server in use
+ * @param protocol the procotol selected
+ * @return created h2 context of this connection
+ */
+h2_conn_ctx_t *h2_conn_ctx_create_for_c1(conn_rec *c, server_rec *s, const char *protocol);
+
+apr_status_t h2_conn_ctx_init_for_c2(h2_conn_ctx_t **pctx, conn_rec *c,
+ struct h2_mplx *mplx, struct h2_stream *stream);
+
+void h2_conn_ctx_clear_for_c2(conn_rec *c2);
+
+void h2_conn_ctx_detach(conn_rec *c);
+
+void h2_conn_ctx_destroy(conn_rec *c);
+
+void h2_conn_ctx_set_timeout(h2_conn_ctx_t *conn_ctx, apr_interval_time_t timeout);
+
+#endif /* defined(__mod_h2__h2_conn_ctx__) */
Modified: httpd/httpd/trunk/modules/http2/h2_headers.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_headers.c?rev=1894163&r1=1894162&r2=1894163&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_headers.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_headers.c Tue Oct 12 13:34:01 2021
@@ -27,7 +27,7 @@
#include <nghttp2/nghttp2.h>
#include "h2_private.h"
-#include "h2_h2.h"
+#include "h2_protocol.h"
#include "h2_config.h"
#include "h2_util.h"
#include "h2_request.h"