1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
|
/* 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 <apr_optional.h>
#include <apr_optional_hooks.h>
#include <apr_strings.h>
#include <apr_time.h>
#include <apr_want.h>
#include <httpd.h>
#include <http_protocol.h>
#include <http_request.h>
#include <http_log.h>
#include <mpm_common.h>
#include "mod_http2.h"
#include <nghttp2/nghttp2.h>
#include "h2_stream.h"
#include "h2_c1.h"
#include "h2_c2.h"
#include "h2_session.h"
#include "h2_config.h"
#include "h2_conn_ctx.h"
#include "h2_protocol.h"
#include "h2_mplx.h"
#include "h2_push.h"
#include "h2_request.h"
#include "h2_switch.h"
#include "h2_version.h"
#include "h2_bucket_beam.h"
#include "h2_ws.h"
static void h2_hooks(apr_pool_t *pool);
AP_DECLARE_MODULE(http2) = {
STANDARD20_MODULE_STUFF,
h2_config_create_dir, /* func to create per dir config */
h2_config_merge_dir, /* func to merge per dir config */
h2_config_create_svr, /* func to create per server config */
h2_config_merge_svr, /* func to merge per server config */
h2_cmds, /* command handlers */
h2_hooks,
#if defined(AP_MODULE_FLAG_NONE)
AP_MODULE_FLAG_ALWAYS_MERGE
#endif
};
static int h2_h2_fixups(request_rec *r);
typedef struct {
unsigned int change_prio : 1;
unsigned int sha256 : 1;
unsigned int inv_headers : 1;
unsigned int dyn_windows : 1;
} features;
static features myfeats;
static int mpm_warned;
/* The module initialization. Called once as apache hook, before any multi
* processing (threaded or not) happens. It is typically at least called twice,
* see
* http://wiki.apache.org/httpd/ModuleLife
* Since the first run is just a "practise" run, we want to initialize for real
* only on the second try. This defeats the purpose of the first dry run a bit,
* since apache wants to verify that a new configuration actually will work.
* So if we have trouble with the configuration, this will only be detected
* when the server has already switched.
* On the other hand, when we initialize lib nghttp2, all possible crazy things
* might happen and this might even eat threads. So, better init on the real
* invocation, for now at least.
*/
static int h2_post_config(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
void *data = NULL;
const char *mod_h2_init_key = "mod_http2_init_counter";
nghttp2_info *ngh2;
apr_status_t status;
(void)plog;(void)ptemp;
#ifdef H2_NG2_CHANGE_PRIO
myfeats.change_prio = 1;
#endif
#ifdef H2_OPENSSL
myfeats.sha256 = 1;
#endif
#ifdef H2_NG2_INVALID_HEADER_CB
myfeats.inv_headers = 1;
#endif
#ifdef H2_NG2_LOCAL_WIN_SIZE
myfeats.dyn_windows = 1;
#endif
apr_pool_userdata_get(&data, mod_h2_init_key, s->process->pool);
if ( data == NULL ) {
ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03089)
"initializing post config dry run");
apr_pool_userdata_set((const void *)1, mod_h2_init_key,
apr_pool_cleanup_null, s->process->pool);
return APR_SUCCESS;
}
ngh2 = nghttp2_version(0);
ap_log_error( APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(03090)
"mod_http2 (v%s, feats=%s%s%s%s, nghttp2 %s), initializing...",
MOD_HTTP2_VERSION,
myfeats.change_prio? "CHPRIO" : "",
myfeats.sha256? "+SHA256" : "",
myfeats.inv_headers? "+INVHD" : "",
myfeats.dyn_windows? "+DWINS" : "",
ngh2? ngh2->version_str : "unknown");
if (!h2_mpm_supported() && !mpm_warned) {
mpm_warned = 1;
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10034)
"The mpm module (%s) is not supported by mod_http2. The mpm determines "
"how things are processed in your server. HTTP/2 has more demands in "
"this regard and the currently selected mpm will just not do. "
"This is an advisory warning. Your server will continue to work, but "
"the HTTP/2 protocol will be inactive.",
h2_conn_mpm_name());
}
status = h2_protocol_init(p, s);
if (status == APR_SUCCESS) {
status = h2_switch_init(p, s);
}
return status;
}
static char *http2_var_lookup(apr_pool_t *, server_rec *,
conn_rec *, request_rec *, char *name);
static int http2_is_h2(conn_rec *);
static void http2_get_num_workers(server_rec *s, int *minw, int *maxw)
{
apr_time_t tdummy;
h2_get_workers_config(s, minw, maxw, &tdummy);
}
/* Runs once per created child process. Perform any process
* related initionalization here.
*/
static void h2_child_init(apr_pool_t *pchild, server_rec *s)
{
apr_status_t rv;
/* Set up our connection processing */
rv = h2_c1_child_init(pchild, s);
if (APR_SUCCESS == rv) {
rv = h2_c2_child_init(pchild, s);
}
if (APR_SUCCESS != rv) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
APLOGNO(02949) "initializing connection handling");
}
}
/* Install this module into the apache2 infrastructure.
*/
static void h2_hooks(apr_pool_t *pool)
{
static const char *const mod_ssl[] = { "mod_ssl.c", NULL};
APR_REGISTER_OPTIONAL_FN(http2_is_h2);
APR_REGISTER_OPTIONAL_FN(http2_var_lookup);
APR_REGISTER_OPTIONAL_FN(http2_get_num_workers);
ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, "installing hooks");
/* Run once after configuration is set, but before mpm children initialize.
*/
ap_hook_post_config(h2_post_config, mod_ssl, NULL, APR_HOOK_MIDDLE);
/* Run once after a child process has been created.
*/
ap_hook_child_init(h2_child_init, NULL, NULL, APR_HOOK_MIDDLE);
#if AP_MODULE_MAGIC_AT_LEAST(20120211, 110)
ap_hook_child_stopping(h2_c1_child_stopping, NULL, NULL, APR_HOOK_MIDDLE);
#endif
h2_c1_register_hooks();
h2_switch_register_hooks();
h2_c2_register_hooks();
h2_ws_register_hooks();
/* Setup subprocess env for certain variables
*/
ap_hook_fixups(h2_h2_fixups, NULL,NULL, APR_HOOK_MIDDLE);
}
static const char *val_HTTP2(apr_pool_t *p, server_rec *s,
conn_rec *c, request_rec *r, h2_conn_ctx_t *ctx)
{
return ctx? "on" : "off";
}
static const char *val_H2_PUSH(apr_pool_t *p, server_rec *s,
conn_rec *c, request_rec *r,
h2_conn_ctx_t *conn_ctx)
{
if (conn_ctx) {
if (r) {
if (conn_ctx->stream_id) {
const h2_stream *stream = h2_mplx_c2_stream_get(conn_ctx->mplx, conn_ctx->stream_id);
if (stream && stream->push_policy != H2_PUSH_NONE) {
return "on";
}
}
}
else if (c && h2_session_push_enabled(conn_ctx->session)) {
return "on";
}
}
else if (s) {
if (h2_config_geti(r, s, H2_CONF_PUSH)) {
return "on";
}
}
return "off";
}
static const char *val_H2_PUSHED(apr_pool_t *p, server_rec *s,
conn_rec *c, request_rec *r,
h2_conn_ctx_t *conn_ctx)
{
if (conn_ctx) {
if (conn_ctx->stream_id && !H2_STREAM_CLIENT_INITIATED(conn_ctx->stream_id)) {
return "PUSHED";
}
}
return "";
}
static const char *val_H2_PUSHED_ON(apr_pool_t *p, server_rec *s,
conn_rec *c, request_rec *r,
h2_conn_ctx_t *conn_ctx)
{
if (conn_ctx) {
if (conn_ctx->stream_id && !H2_STREAM_CLIENT_INITIATED(conn_ctx->stream_id)) {
const h2_stream *stream = h2_mplx_c2_stream_get(conn_ctx->mplx, conn_ctx->stream_id);
if (stream) {
return apr_itoa(p, stream->initiated_on);
}
}
}
return "";
}
static const char *val_H2_STREAM_TAG(apr_pool_t *p, server_rec *s,
conn_rec *c, request_rec *r, h2_conn_ctx_t *ctx)
{
if (c) {
h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c);
if (conn_ctx) {
return conn_ctx->stream_id == 0? conn_ctx->id
: apr_psprintf(p, "%s-%d", conn_ctx->id, conn_ctx->stream_id);
}
}
return "";
}
static const char *val_H2_STREAM_ID(apr_pool_t *p, server_rec *s,
conn_rec *c, request_rec *r, h2_conn_ctx_t *ctx)
{
const char *cp = val_H2_STREAM_TAG(p, s, c, r, ctx);
if (cp && (cp = ap_strrchr_c(cp, '-'))) {
return ++cp;
}
return NULL;
}
typedef const char *h2_var_lookup(apr_pool_t *p, server_rec *s,
conn_rec *c, request_rec *r, h2_conn_ctx_t *ctx);
typedef struct h2_var_def {
const char *name;
h2_var_lookup *lookup;
unsigned int subprocess : 1; /* should be set in r->subprocess_env */
} h2_var_def;
static h2_var_def H2_VARS[] = {
{ "HTTP2", val_HTTP2, 1 },
{ "H2PUSH", val_H2_PUSH, 1 },
{ "H2_PUSH", val_H2_PUSH, 1 },
{ "H2_PUSHED", val_H2_PUSHED, 1 },
{ "H2_PUSHED_ON", val_H2_PUSHED_ON, 1 },
{ "H2_STREAM_ID", val_H2_STREAM_ID, 1 },
{ "H2_STREAM_TAG", val_H2_STREAM_TAG, 1 },
};
#ifndef H2_ALEN
#define H2_ALEN(a) (sizeof(a)/sizeof((a)[0]))
#endif
static int http2_is_h2(conn_rec *c)
{
return h2_conn_ctx_get(c->master? c->master : c) != NULL;
}
static char *http2_var_lookup(apr_pool_t *p, server_rec *s,
conn_rec *c, request_rec *r, char *name)
{
unsigned int i;
/* If the # of vars grow, we need to put definitions in a hash */
for (i = 0; i < H2_ALEN(H2_VARS); ++i) {
h2_var_def *vdef = &H2_VARS[i];
if (!strcmp(vdef->name, name)) {
h2_conn_ctx_t *ctx = (r? h2_conn_ctx_get(c) :
h2_conn_ctx_get(c->master? c->master : c));
return (char *)vdef->lookup(p, s, c, r, ctx);
}
}
return (char*)"";
}
static int h2_h2_fixups(request_rec *r)
{
if (r->connection->master) {
h2_conn_ctx_t *ctx = h2_conn_ctx_get(r->connection);
unsigned int i;
for (i = 0; ctx && i < H2_ALEN(H2_VARS); ++i) {
h2_var_def *vdef = &H2_VARS[i];
if (vdef->subprocess) {
apr_table_setn(r->subprocess_env, vdef->name,
vdef->lookup(r->pool, r->server, r->connection,
r, ctx));
}
}
}
return DECLINED;
}
|