"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "mod_http2.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
1.15.16_vs_1.15.17.
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <apr_optional.h>
18 #include <apr_optional_hooks.h>
19 #include <apr_strings.h>
20 #include <apr_time.h>
21 #include <apr_want.h>
22
23 #include <httpd.h>
24 #include <http_protocol.h>
25 #include <http_request.h>
26 #include <http_log.h>
27
28 #include "mod_http2.h"
29
30 #include <nghttp2/nghttp2.h>
31 #include "h2_stream.h"
32 #include "h2_alt_svc.h"
33 #include "h2_conn.h"
34 #include "h2_filter.h"
35 #include "h2_task.h"
36 #include "h2_session.h"
37 #include "h2_config.h"
38 #include "h2_ctx.h"
39 #include "h2_h2.h"
40 #include "h2_mplx.h"
41 #include "h2_push.h"
42 #include "h2_request.h"
43 #include "h2_switch.h"
44 #include "h2_version.h"
45
46
47 static void h2_hooks(apr_pool_t *pool);
48
49 AP_DECLARE_MODULE(http2) = {
50 STANDARD20_MODULE_STUFF,
51 h2_config_create_dir, /* func to create per dir config */
52 h2_config_merge_dir, /* func to merge per dir config */
53 h2_config_create_svr, /* func to create per server config */
54 h2_config_merge_svr, /* func to merge per server config */
55 h2_cmds, /* command handlers */
56 h2_hooks,
57 #if defined(AP_MODULE_FLAG_NONE)
58 AP_MODULE_FLAG_ALWAYS_MERGE
59 #endif
60 };
61
62 static int h2_h2_fixups(request_rec *r);
63
64 typedef struct {
65 unsigned int change_prio : 1;
66 unsigned int sha256 : 1;
67 unsigned int inv_headers : 1;
68 unsigned int dyn_windows : 1;
69 } features;
70
71 static features myfeats;
72 static int mpm_warned;
73
74 /* The module initialization. Called once as apache hook, before any multi
75 * processing (threaded or not) happens. It is typically at least called twice,
76 * see
77 * http://wiki.apache.org/httpd/ModuleLife
78 * Since the first run is just a "practise" run, we want to initialize for real
79 * only on the second try. This defeats the purpose of the first dry run a bit,
80 * since apache wants to verify that a new configuration actually will work.
81 * So if we have trouble with the configuration, this will only be detected
82 * when the server has already switched.
83 * On the other hand, when we initialize lib nghttp2, all possible crazy things
84 * might happen and this might even eat threads. So, better init on the real
85 * invocation, for now at least.
86 */
87 static int h2_post_config(apr_pool_t *p, apr_pool_t *plog,
88 apr_pool_t *ptemp, server_rec *s)
89 {
90 void *data = NULL;
91 const char *mod_h2_init_key = "mod_http2_init_counter";
92 nghttp2_info *ngh2;
93 apr_status_t status;
94
95 (void)plog;(void)ptemp;
96 #ifdef H2_NG2_CHANGE_PRIO
97 myfeats.change_prio = 1;
98 #endif
99 #ifdef H2_OPENSSL
100 myfeats.sha256 = 1;
101 #endif
102 #ifdef H2_NG2_INVALID_HEADER_CB
103 myfeats.inv_headers = 1;
104 #endif
105 #ifdef H2_NG2_LOCAL_WIN_SIZE
106 myfeats.dyn_windows = 1;
107 #endif
108
109 apr_pool_userdata_get(&data, mod_h2_init_key, s->process->pool);
110 if ( data == NULL ) {
111 ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03089)
112 "initializing post config dry run");
113 apr_pool_userdata_set((const void *)1, mod_h2_init_key,
114 apr_pool_cleanup_null, s->process->pool);
115 return APR_SUCCESS;
116 }
117
118 ngh2 = nghttp2_version(0);
119 ap_log_error( APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(03090)
120 "mod_http2 (v%s, feats=%s%s%s%s, nghttp2 %s), initializing...",
121 MOD_HTTP2_VERSION,
122 myfeats.change_prio? "CHPRIO" : "",
123 myfeats.sha256? "+SHA256" : "",
124 myfeats.inv_headers? "+INVHD" : "",
125 myfeats.dyn_windows? "+DWINS" : "",
126 ngh2? ngh2->version_str : "unknown");
127
128 switch (h2_conn_mpm_type()) {
129 case H2_MPM_SIMPLE:
130 case H2_MPM_MOTORZ:
131 case H2_MPM_NETWARE:
132 case H2_MPM_WINNT:
133 /* not sure we need something extra for those. */
134 break;
135 case H2_MPM_EVENT:
136 case H2_MPM_WORKER:
137 /* all fine, we know these ones */
138 break;
139 case H2_MPM_PREFORK:
140 /* ok, we now know how to handle that one */
141 break;
142 case H2_MPM_UNKNOWN:
143 /* ??? */
144 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03091)
145 "post_config: mpm type unknown");
146 break;
147 }
148
149 if (!h2_mpm_supported() && !mpm_warned) {
150 mpm_warned = 1;
151 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10034)
152 "The mpm module (%s) is not supported by mod_http2. The mpm determines "
153 "how things are processed in your server. HTTP/2 has more demands in "
154 "this regard and the currently selected mpm will just not do. "
155 "This is an advisory warning. Your server will continue to work, but "
156 "the HTTP/2 protocol will be inactive.",
157 h2_conn_mpm_name());
158 }
159
160 status = h2_h2_init(p, s);
161 if (status == APR_SUCCESS) {
162 status = h2_switch_init(p, s);
163 }
164 if (status == APR_SUCCESS) {
165 status = h2_task_init(p, s);
166 }
167
168 return status;
169 }
170
171 static char *http2_var_lookup(apr_pool_t *, server_rec *,
172 conn_rec *, request_rec *, char *name);
173 static int http2_is_h2(conn_rec *);
174
175 static void http2_get_num_workers(server_rec *s, int *minw, int *maxw)
176 {
177 h2_get_num_workers(s, minw, maxw);
178 }
179
180 /* Runs once per created child process. Perform any process
181 * related initionalization here.
182 */
183 static void h2_child_init(apr_pool_t *pchild, server_rec *s)
184 {
185 apr_allocator_t *allocator;
186 apr_thread_mutex_t *mutex;
187 apr_status_t status;
188
189 /* The allocator of pchild has no mutex with MPM prefork, but we need one
190 * for h2 workers threads synchronization. Even though mod_http2 shouldn't
191 * be used with prefork, better be safe than sorry, so forcibly set the
192 * mutex here. For MPM event/worker, pchild has no allocator so pconf's
193 * is used, with its mutex.
194 */
195 allocator = apr_pool_allocator_get(pchild);
196 if (allocator) {
197 mutex = apr_allocator_mutex_get(allocator);
198 if (!mutex) {
199 apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, pchild);
200 apr_allocator_mutex_set(allocator, mutex);
201 }
202 }
203
204 /* Set up our connection processing */
205 status = h2_conn_child_init(pchild, s);
206 if (status != APR_SUCCESS) {
207 ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
208 APLOGNO(02949) "initializing connection handling");
209 }
210 }
211
212 /* Install this module into the apache2 infrastructure.
213 */
214 static void h2_hooks(apr_pool_t *pool)
215 {
216 static const char *const mod_ssl[] = { "mod_ssl.c", NULL};
217
218 APR_REGISTER_OPTIONAL_FN(http2_is_h2);
219 APR_REGISTER_OPTIONAL_FN(http2_var_lookup);
220 APR_REGISTER_OPTIONAL_FN(http2_get_num_workers);
221
222 ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, "installing hooks");
223
224 /* Run once after configuration is set, but before mpm children initialize.
225 */
226 ap_hook_post_config(h2_post_config, mod_ssl, NULL, APR_HOOK_MIDDLE);
227
228 /* Run once after a child process has been created.
229 */
230 ap_hook_child_init(h2_child_init, NULL, NULL, APR_HOOK_MIDDLE);
231
232 h2_h2_register_hooks();
233 h2_switch_register_hooks();
234 h2_task_register_hooks();
235
236 h2_alt_svc_register_hooks();
237
238 /* Setup subprocess env for certain variables
239 */
240 ap_hook_fixups(h2_h2_fixups, NULL,NULL, APR_HOOK_MIDDLE);
241
242 /* test http2 connection status handler */
243 ap_hook_handler(h2_filter_h2_status_handler, NULL, NULL, APR_HOOK_MIDDLE);
244 }
245
246 static const char *val_HTTP2(apr_pool_t *p, server_rec *s,
247 conn_rec *c, request_rec *r, h2_ctx *ctx)
248 {
249 return ctx? "on" : "off";
250 }
251
252 static const char *val_H2_PUSH(apr_pool_t *p, server_rec *s,
253 conn_rec *c, request_rec *r, h2_ctx *ctx)
254 {
255 if (ctx) {
256 if (r) {
257 if (ctx->task) {
258 h2_stream *stream = h2_mplx_t_stream_get(ctx->task->mplx, ctx->task);
259 if (stream && stream->push_policy != H2_PUSH_NONE) {
260 return "on";
261 }
262 }
263 }
264 else if (c && h2_session_push_enabled(ctx->session)) {
265 return "on";
266 }
267 }
268 else if (s) {
269 if (h2_config_geti(r, s, H2_CONF_PUSH)) {
270 return "on";
271 }
272 }
273 return "off";
274 }
275
276 static const char *val_H2_PUSHED(apr_pool_t *p, server_rec *s,
277 conn_rec *c, request_rec *r, h2_ctx *ctx)
278 {
279 if (ctx) {
280 if (ctx->task && !H2_STREAM_CLIENT_INITIATED(ctx->task->stream_id)) {
281 return "PUSHED";
282 }
283 }
284 return "";
285 }
286
287 static const char *val_H2_PUSHED_ON(apr_pool_t *p, server_rec *s,
288 conn_rec *c, request_rec *r, h2_ctx *ctx)
289 {
290 if (ctx) {
291 if (ctx->task && !H2_STREAM_CLIENT_INITIATED(ctx->task->stream_id)) {
292 h2_stream *stream = h2_mplx_t_stream_get(ctx->task->mplx, ctx->task);
293 if (stream) {
294 return apr_itoa(p, stream->initiated_on);
295 }
296 }
297 }
298 return "";
299 }
300
301 static const char *val_H2_STREAM_TAG(apr_pool_t *p, server_rec *s,
302 conn_rec *c, request_rec *r, h2_ctx *ctx)
303 {
304 if (ctx) {
305 if (ctx->task) {
306 return ctx->task->id;
307 }
308 }
309 return "";
310 }
311
312 static const char *val_H2_STREAM_ID(apr_pool_t *p, server_rec *s,
313 conn_rec *c, request_rec *r, h2_ctx *ctx)
314 {
315 const char *cp = val_H2_STREAM_TAG(p, s, c, r, ctx);
316 if (cp && (cp = ap_strchr_c(cp, '-'))) {
317 return ++cp;
318 }
319 return NULL;
320 }
321
322 typedef const char *h2_var_lookup(apr_pool_t *p, server_rec *s,
323 conn_rec *c, request_rec *r, h2_ctx *ctx);
324 typedef struct h2_var_def {
325 const char *name;
326 h2_var_lookup *lookup;
327 unsigned int subprocess : 1; /* should be set in r->subprocess_env */
328 } h2_var_def;
329
330 static h2_var_def H2_VARS[] = {
331 { "HTTP2", val_HTTP2, 1 },
332 { "H2PUSH", val_H2_PUSH, 1 },
333 { "H2_PUSH", val_H2_PUSH, 1 },
334 { "H2_PUSHED", val_H2_PUSHED, 1 },
335 { "H2_PUSHED_ON", val_H2_PUSHED_ON, 1 },
336 { "H2_STREAM_ID", val_H2_STREAM_ID, 1 },
337 { "H2_STREAM_TAG", val_H2_STREAM_TAG, 1 },
338 };
339
340 #ifndef H2_ALEN
341 #define H2_ALEN(a) (sizeof(a)/sizeof((a)[0]))
342 #endif
343
344
345 static int http2_is_h2(conn_rec *c)
346 {
347 return h2_ctx_get(c->master? c->master : c, 0) != NULL;
348 }
349
350 static char *http2_var_lookup(apr_pool_t *p, server_rec *s,
351 conn_rec *c, request_rec *r, char *name)
352 {
353 int i;
354 /* If the # of vars grow, we need to put definitions in a hash */
355 for (i = 0; i < H2_ALEN(H2_VARS); ++i) {
356 h2_var_def *vdef = &H2_VARS[i];
357 if (!strcmp(vdef->name, name)) {
358 h2_ctx *ctx = (r? h2_ctx_get(c, 0) :
359 h2_ctx_get(c->master? c->master : c, 0));
360 return (char *)vdef->lookup(p, s, c, r, ctx);
361 }
362 }
363 return (char*)"";
364 }
365
366 static int h2_h2_fixups(request_rec *r)
367 {
368 if (r->connection->master) {
369 h2_ctx *ctx = h2_ctx_get(r->connection, 0);
370 int i;
371
372 for (i = 0; ctx && i < H2_ALEN(H2_VARS); ++i) {
373 h2_var_def *vdef = &H2_VARS[i];
374 if (vdef->subprocess) {
375 apr_table_setn(r->subprocess_env, vdef->name,
376 vdef->lookup(r->pool, r->server, r->connection,
377 r, ctx));
378 }
379 }
380 }
381 return DECLINED;
382 }