From 93c609224e82134ddd12c27fcedf37f93b24317e Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 23 Dec 2015 14:50:54 +0000 Subject: [PATCH] fixed segfault in connection shutdown, added accept-push-policy support git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1721547 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 6 +++ modules/http2/h2_push.c | 93 ++++++++++++++++++++++++++++++-------- modules/http2/h2_push.h | 9 ++++ modules/http2/h2_request.c | 3 +- modules/http2/h2_request.h | 3 +- modules/http2/mod_http2.c | 3 +- 6 files changed, 95 insertions(+), 22 deletions(-) diff --git a/CHANGES b/CHANGES index c0323386f1..e6846a0f57 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,12 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 + *) mod_http2: Fixed segfault on connection shutdown, callback ran into a semi + dismantled session. Added support for experimental accept-push-policy draft + (https://tools.ietf.org/html/draft-ruellan-http-accept-push-policy-00). Clients + may now influence server pushes by sending accept-push-policy headers. + [Stefan Eissing] + *) mod_http2: On async MPMs (event), idle HTTP/2 connections will enter KEEPALIVE state and become eligible for MPM cleanup under load. Rewrote connection state handling. Defaults for H2KeepAliveTimeout shortened. Cleaned up logging. diff --git a/modules/http2/h2_push.c b/modules/http2/h2_push.c index 9997c5769c..1ba4ea6fb8 100644 --- a/modules/http2/h2_push.c +++ b/modules/http2/h2_push.c @@ -30,6 +30,19 @@ #include "h2_request.h" #include "h2_response.h" +static const char *policy_str(h2_push_policy policy) +{ + switch (policy) { + case H2_PUSH_NONE: + return "none"; + case H2_PUSH_FAST_LOAD: + return "fast-load"; + case H2_PUSH_HEAD: + return "head"; + default: + return "default"; + } +} typedef struct { const h2_request *req; @@ -269,6 +282,7 @@ static int add_push(link_ctx *ctx) if (apr_uri_parse(ctx->pool, ctx->link, &uri) == APR_SUCCESS) { if (uri.path && same_authority(ctx->req, &uri)) { char *path; + const char *method; apr_table_t *headers; h2_request *req; h2_push *push; @@ -283,6 +297,14 @@ static int add_push(link_ctx *ctx) push = apr_pcalloc(ctx->pool, sizeof(*push)); + switch (ctx->req->push_policy) { + case H2_PUSH_HEAD: + method = "HEAD"; + break; + default: + method = "GET"; + break; + } headers = apr_table_make(ctx->pool, 5); apr_table_do(set_header, headers, ctx->req->headers, "User-Agent", @@ -290,7 +312,7 @@ static int add_push(link_ctx *ctx) "Accept-Language", NULL); req = h2_request_createn(0, ctx->pool, ctx->req->config, - "GET", ctx->req->scheme, + method, ctx->req->scheme, ctx->req->authority, path, headers); /* atm, we do not push on pushes */ @@ -374,23 +396,58 @@ static int head_iter(void *ctx, const char *key, const char *value) apr_array_header_t *h2_push_collect(apr_pool_t *p, const h2_request *req, const h2_response *res) { - /* Collect push candidates from the request/response pair. - * - * One source for pushes are "rel=preload" link headers - * in the response. - * - * TODO: This may be extended in the future by hooks or callbacks - * where other modules can provide push information directly. - */ - if (res->headers) { - link_ctx ctx; - - memset(&ctx, 0, sizeof(ctx)); - ctx.req = req; - ctx.pool = p; - - apr_table_do(head_iter, &ctx, res->headers, NULL); - return ctx.pushes; + if (req && req->push_policy != H2_PUSH_NONE) { + /* Collect push candidates from the request/response pair. + * + * One source for pushes are "rel=preload" link headers + * in the response. + * + * TODO: This may be extended in the future by hooks or callbacks + * where other modules can provide push information directly. + */ + if (res->headers) { + link_ctx ctx; + + memset(&ctx, 0, sizeof(ctx)); + ctx.req = req; + ctx.pool = p; + + apr_table_do(head_iter, &ctx, res->headers, NULL); + if (ctx.pushes) { + apr_table_setn(res->headers, "push-policy", policy_str(req->push_policy)); + } + return ctx.pushes; + } } return NULL; } + +void h2_push_policy_determine(struct h2_request *req, apr_pool_t *p, int push_enabled) +{ + h2_push_policy policy = H2_PUSH_NONE; + if (push_enabled) { + const char *val = apr_table_get(req->headers, "accept-push-policy"); + if (val) { + if (ap_find_token(p, val, "fast-load")) { + policy = H2_PUSH_FAST_LOAD; + } + else if (ap_find_token(p, val, "head")) { + policy = H2_PUSH_HEAD; + } + else if (ap_find_token(p, val, "default")) { + policy = H2_PUSH_DEFAULT; + } + else if (ap_find_token(p, val, "none")) { + policy = H2_PUSH_NONE; + } + else { + /* nothing known found in this header, go by default */ + policy = H2_PUSH_DEFAULT; + } + } + else { + policy = H2_PUSH_DEFAULT; + } + } + req->push_policy = policy; +} diff --git a/modules/http2/h2_push.h b/modules/http2/h2_push.h index 871548cee3..f3a8601c34 100644 --- a/modules/http2/h2_push.h +++ b/modules/http2/h2_push.h @@ -19,6 +19,13 @@ struct h2_request; struct h2_response; struct h2_ngheader; +typedef enum { + H2_PUSH_NONE, + H2_PUSH_DEFAULT, + H2_PUSH_HEAD, + H2_PUSH_FAST_LOAD, +} h2_push_policy; + typedef struct h2_push { const struct h2_request *req; } h2_push; @@ -28,4 +35,6 @@ apr_array_header_t *h2_push_collect(apr_pool_t *p, const struct h2_request *req, const struct h2_response *res); +void h2_push_policy_determine(struct h2_request *req, apr_pool_t *p, int push_enabled); + #endif /* defined(__mod_h2__h2_push__) */ diff --git a/modules/http2/h2_request.c b/modules/http2/h2_request.c index 04227efea5..6fafa45f1a 100644 --- a/modules/http2/h2_request.c +++ b/modules/http2/h2_request.c @@ -32,6 +32,7 @@ #include "h2_private.h" #include "h2_config.h" #include "h2_mplx.h" +#include "h2_push.h" #include "h2_request.h" #include "h2_task.h" #include "h2_util.h" @@ -272,7 +273,7 @@ apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, } req->eoh = 1; - req->push = push; + h2_push_policy_determine(req, pool, push); /* In the presence of trailers, force behaviour of chunked encoding */ s = apr_table_get(req->headers, "Trailer"); diff --git a/modules/http2/h2_request.h b/modules/http2/h2_request.h index 561fed8dcf..cc01ed1238 100644 --- a/modules/http2/h2_request.h +++ b/modules/http2/h2_request.h @@ -44,8 +44,7 @@ struct h2_request { unsigned int chunked : 1; /* iff requst body needs to be forwarded as chunked */ unsigned int eoh : 1; /* iff end-of-headers has been seen and request is complete */ unsigned int body : 1; /* iff this request has a body */ - unsigned int push : 1; /* iff server push is possible for this request */ - + unsigned int push_policy; /* which push policy to use for this request */ const struct h2_config *config; }; diff --git a/modules/http2/mod_http2.c b/modules/http2/mod_http2.c index 8e95e519c8..53a460fe31 100644 --- a/modules/http2/mod_http2.c +++ b/modules/http2/mod_http2.c @@ -32,6 +32,7 @@ #include "h2_config.h" #include "h2_ctx.h" #include "h2_h2.h" +#include "h2_push.h" #include "h2_request.h" #include "h2_switch.h" #include "h2_version.h" @@ -171,7 +172,7 @@ static char *value_of_H2PUSH(apr_pool_t *p, server_rec *s, ctx = h2_ctx_rget(r); if (ctx) { h2_task *task = h2_ctx_get_task(ctx); - return task && task->request->push? "on" : "off"; + return (task && task->request->push_policy != H2_PUSH_NONE)? "on" : "off"; } } else if (c) {