Apache HTTPD
mod_proxy_http2.c
Go to the documentation of this file.
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 <nghttp2/nghttp2.h>
18
19#include <ap_mmn.h>
20#include <httpd.h>
21#include <mod_proxy.h>
22#include "mod_http2.h"
23
24
25#include "mod_proxy_http2.h"
26#include "h2.h"
27#include "h2_proxy_util.h"
28#include "h2_version.h"
29#include "h2_proxy_session.h"
30
31#define H2MIN(x,y) ((x) < (y) ? (x) : (y))
32
33static void register_hook(apr_pool_t *p);
34
37 NULL, /* create per-directory config structure */
38 NULL, /* merge per-directory config structures */
39 NULL, /* create per-server config structure */
40 NULL, /* merge per-server config structures */
41 NULL, /* command apr_table_t */
42 register_hook, /* register hooks */
43#if defined(AP_MODULE_FLAG_NONE)
45#endif
46};
47
48/* Optional functions from mod_http2 */
49static int (*is_h2)(conn_rec *c);
50
51typedef struct h2_proxy_ctx {
52 const char *id;
56 const char *proxy_func;
61
64
65 unsigned is_ssl : 1;
66
67 request_rec *r; /* the request processed in this ctx */
68 int r_status; /* status of request work */
69 int r_done; /* request was processed, not necessarily successfully */
70 int r_may_retry; /* request may be retried */
71 int has_reusable_session; /* http2 session is live and clean */
73
75 apr_pool_t *ptemp, server_rec *s)
76{
77 void *data = NULL;
78 const char *init_key = "mod_proxy_http2_init_counter";
79 nghttp2_info *ngh2;
81 (void)plog;(void)ptemp;
82
83 apr_pool_userdata_get(&data, init_key, s->process->pool);
84 if ( data == NULL ) {
85 apr_pool_userdata_set((const void *)1, init_key,
86 apr_pool_cleanup_null, s->process->pool);
87 return APR_SUCCESS;
88 }
89
90 ngh2 = nghttp2_version(0);
92 "mod_proxy_http2 (v%s, nghttp2 %s), initializing...",
93 MOD_HTTP2_VERSION, ngh2? ngh2->version_str : "unknown");
94
96
97 return status;
98}
99
105{
106 char *host, *path, sport[7];
107 char *search = NULL;
108 const char *err;
109 const char *scheme;
110 const char *http_scheme;
112
113 /* ap_port_of_scheme() */
114 if (ap_cstr_casecmpn(url, "h2c:", 4) == 0) {
115 url += 4;
116 scheme = "h2c";
117 http_scheme = "http";
118 }
119 else if (ap_cstr_casecmpn(url, "h2:", 3) == 0) {
120 url += 3;
121 scheme = "h2";
122 http_scheme = "https";
123 }
124 else {
125 return DECLINED;
126 }
128
130 "HTTP2: canonicalising URL %s", url);
131
132 /* do syntatic check.
133 * We break the URL into host, port, path, search
134 */
136 if (err) {
138 "error parsing URL %s: %s", url, err);
139 return HTTP_BAD_REQUEST;
140 }
141
142 /*
143 * now parse path/search args, according to rfc1738:
144 * process the path.
145 *
146 * In a reverse proxy, our URL has been processed, so canonicalise
147 * unless proxy-nocanon is set to say it's raw
148 * In a forward proxy, we have and MUST NOT MANGLE the original.
149 */
150 switch (r->proxyreq) {
151 default: /* wtf are we doing here? */
152 case PROXYREQ_REVERSE:
153 if (apr_table_get(r->notes, "proxy-nocanon")) {
154 path = url; /* this is the raw path */
155 }
156 else if (apr_table_get(r->notes, "proxy-noencode")) {
157 path = url; /* this is the encoded path already */
158 search = r->args;
159 }
160 else {
161#ifdef PROXY_CANONENC_NOENCODEDSLASHENCODING
163 int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0;
164
165 path = ap_proxy_canonenc_ex(r->pool, url, (int)strlen(url),
167#else
168 path = ap_proxy_canonenc(r->pool, url, (int)strlen(url),
169 enc_path, 0, r->proxyreq);
170#endif
171 if (!path) {
172 return HTTP_BAD_REQUEST;
173 }
174 search = r->args;
175 }
176 break;
177 case PROXYREQ_PROXY:
178 path = url;
179 break;
180 }
181 /*
182 * If we have a raw control character or a ' ' in nocanon path or
183 * r->args, correct encoding was missed.
184 */
185 if (path == url && *ap_scan_vchar_obstext(path)) {
187 "To be forwarded path contains control "
188 "characters or spaces");
189 return HTTP_FORBIDDEN;
190 }
193 "To be forwarded query string contains control "
194 "characters or spaces");
195 return HTTP_FORBIDDEN;
196 }
197
198 if (port != def_port) {
199 apr_snprintf(sport, sizeof(sport), ":%d", port);
200 }
201 else {
202 sport[0] = '\0';
203 }
204
205 if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
206 host = apr_pstrcat(r->pool, "[", host, "]", NULL);
207 }
208 r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
209 "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
210 return OK;
211}
212
214{
215 h2_proxy_ctx *ctx = session->user_data;
216 const char *url;
218
220 apr_table_setn(r->notes, "proxy-source-port", apr_psprintf(r->pool, "%hu",
221 ctx->p_conn->connection->local_addr->port));
222 status = h2_proxy_session_submit(session, url, r, 1);
223 if (status != APR_SUCCESS) {
225 "pass request body failed to %pI (%s) from %s (%s)",
226 ctx->p_conn->addr, ctx->p_conn->hostname ?
227 ctx->p_conn->hostname: "", session->c->client_ip,
228 session->c->remote_host ? session->c->remote_host: "");
229 }
230 return status;
231}
232
234 apr_status_t status, int touched, int error_code)
235{
236 if (r == ctx->r) {
238 "h2_proxy_session(%s): request done, touched=%d, error=%d",
239 ctx->id, touched, error_code);
240 ctx->r_done = 1;
241 if (touched) ctx->r_may_retry = 0;
242 ctx->r_status = error_code? HTTP_BAD_GATEWAY :
243 ((status == APR_SUCCESS)? OK :
245 }
246}
247
249 apr_status_t status, int touched, int error_code)
250{
251 request_done(session->user_data, r, status, touched, error_code);
252}
253
256 h2_proxy_session *session;
257 int h2_front;
258
259 /* Step Four: Send the Request in a new HTTP/2 stream and
260 * loop until we got the response or encounter errors.
261 */
262 ctx->has_reusable_session = 0; /* don't know yet */
263 h2_front = is_h2? is_h2(ctx->cfront) : 0;
264 session = h2_proxy_session_setup(ctx->id, ctx->p_conn, ctx->conf,
265 h2_front, 30,
266 h2_proxy_log2((int)ctx->req_buffer_size),
268 if (!session) {
270 APLOGNO(03372) "session unavailable");
272 }
273
274 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->cfront, APLOGNO(03373)
275 "eng(%s): run session %s", ctx->id, session->id);
276 session->user_data = ctx;
277
278 ctx->r_done = 0;
279 add_request(session, ctx->r);
280
281 while (!ctx->cfront->aborted && !ctx->r_done) {
282
284 if (status != APR_SUCCESS) {
285 /* Encountered an error during session processing */
287 APLOGNO(03375) "eng(%s): end of session %s",
288 ctx->id, session->id);
289 /* Any open stream of that session needs to
290 * a) be reopened on the new session iff safe to do so
291 * b) reported as done (failed) otherwise
292 */
294 goto out;
295 }
296 }
297
298out:
299 if (ctx->cfront->aborted) {
300 /* master connection gone */
302 APLOGNO(03374) "eng(%s): master connection gone", ctx->id);
303 /* cancel all ongoing requests */
306 }
307 ctx->has_reusable_session = h2_proxy_session_is_reusable(session);
308 session->user_data = NULL;
309 return status;
310}
311
313 proxy_worker *worker,
314 proxy_server_conf *conf,
315 char *url,
316 const char *proxyname,
318{
319 const char *proxy_func;
320 char *locurl, *u;
322 int is_ssl = 0;
326 int reconnects = 0;
327
328 /* find the scheme */
329 if ((url[0] != 'h' && url[0] != 'H') || url[1] != '2') {
330 return DECLINED;
331 }
332 u = strchr(url, ':');
333 if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0') {
334 return DECLINED;
335 }
336 slen = (u - url);
337 switch(slen) {
338 case 2:
339 proxy_func = "H2";
340 is_ssl = 1;
341 break;
342 case 3:
343 if (url[2] != 'c' && url[2] != 'C') {
344 return DECLINED;
345 }
346 proxy_func = "H2C";
347 break;
348 default:
349 return DECLINED;
350 }
351
352 ctx = apr_pcalloc(r->pool, sizeof(*ctx));
353 ctx->id = apr_psprintf(r->pool, "%ld", (long)r->connection->id);
354 ctx->cfront = r->connection;
355 ctx->pool = r->pool;
356 ctx->server = r->server;
357 ctx->proxy_func = proxy_func;
358 ctx->is_ssl = is_ssl;
359 ctx->worker = worker;
360 ctx->conf = conf;
361 ctx->req_buffer_size = (32*1024);
362 ctx->r = r;
363 ctx->r_status = status = HTTP_SERVICE_UNAVAILABLE;
364 ctx->r_done = 0;
365 ctx->r_may_retry = 1;
366
367 ap_set_module_config(ctx->cfront->conn_config, &proxy_http2_module, ctx);
368
369 /* scheme says, this is for us. */
372 "H2: serving URL %s", url);
373
375 if (ctx->cfront->aborted) goto cleanup;
376
377 /* Get a proxy_conn_rec from the worker, might be a new one, might
378 * be one still open from another request, or it might fail if the
379 * worker is stopped or in error. */
380 if ((status = ap_proxy_acquire_connection(ctx->proxy_func, &ctx->p_conn,
381 ctx->worker, ctx->server)) != OK) {
382 goto cleanup;
383 }
384
385 locurl = url;
386 ctx->p_conn->is_ssl = ctx->is_ssl;
387
388 /* Step One: Determine the URL to connect to (might be a proxy),
389 * initialize the backend accordingly and determine the server
390 * port string we can expect in responses. */
391 if ((status = ap_proxy_determine_connection(ctx->pool, ctx->r, conf, worker,
392 ctx->p_conn, &uri, &locurl,
394 ctx->server_portstr,
395 sizeof(ctx->server_portstr))) != OK) {
396 goto cleanup;
397 }
398
399 /* Step Two: Make the Connection (or check that an already existing
400 * socket is still usable). On success, we have a socket connected to
401 * backend->hostname. */
402 if (ap_proxy_connect_backend(ctx->proxy_func, ctx->p_conn, ctx->worker,
403 ctx->server)) {
404 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->cfront, APLOGNO(03352)
405 "H2: failed to make connection to backend: %s",
406 ctx->p_conn->hostname);
407 goto cleanup;
408 }
409
410 /* Step Three: Create conn_rec for the socket we have open now. */
411 status = ap_proxy_connection_create_ex(ctx->proxy_func, ctx->p_conn, ctx->r);
412 if (status != OK) {
414 "setup new connection: is_ssl=%d %s %s %s",
415 ctx->p_conn->is_ssl, ctx->p_conn->ssl_hostname,
416 locurl, ctx->p_conn->hostname);
418 goto cleanup;
419 }
420
421 if (!ctx->p_conn->data && ctx->is_ssl) {
422 /* New SSL connection: set a note on the connection about what
423 * protocol we need. */
424 apr_table_setn(ctx->p_conn->connection->notes,
425 "proxy-request-alpn-protos", "h2");
426 }
427
428 if (ctx->cfront->aborted) goto cleanup;
429 status = ctx_run(ctx);
430
431 if (ctx->r_status != OK && ctx->r_may_retry && !ctx->cfront->aborted) {
432 /* Not successfully processed, but may retry, tear down old conn and start over */
433 if (ctx->p_conn) {
434 ctx->p_conn->close = 1;
435#if AP_MODULE_MAGIC_AT_LEAST(20140207, 2)
437#endif
438 ap_proxy_release_connection(ctx->proxy_func, ctx->p_conn, ctx->server);
439 ctx->p_conn = NULL;
440 }
441 ++reconnects;
442 if (reconnects < 2) {
443 goto run_connect;
444 }
445 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->cfront, APLOGNO(10023)
446 "giving up after %d reconnects, request-done=%d",
447 reconnects, ctx->r_done);
448 }
449
450cleanup:
451 if (ctx->p_conn) {
452 if (status != APR_SUCCESS || !ctx->has_reusable_session) {
453 /* close socket when errors happened or session is not "clean",
454 * meaning in a working condition with no open streams */
455 ctx->p_conn->close = 1;
456 }
457#if AP_MODULE_MAGIC_AT_LEAST(20140207, 2)
458 proxy_run_detach_backend(ctx->r, ctx->p_conn);
459#endif
460 ap_proxy_release_connection(ctx->proxy_func, ctx->p_conn, ctx->server);
461 ctx->p_conn = NULL;
462 }
463
464 ap_set_module_config(ctx->cfront->conn_config, &proxy_http2_module, NULL);
466 APLOGNO(03377) "leaving handler");
467 if (ctx->r_status != OK) {
468 ap_die(ctx->r_status, r);
469 }
470 else if (status != APR_SUCCESS) {
472 }
473 return ctx->r_status;
474}
475
483
Module Magic Number.
apr_size_t const unsigned char unsigned int unsigned int d
Definition apr_siphash.h:72
#define AP_MODULE_FLAG_ALWAYS_MERGE
void ap_hook_post_config(ap_HOOK_post_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:105
#define AP_DECLARE_MODULE(foo)
#define ap_set_module_config(v, m, val)
request_rec int int apr_table_t const char * path
request_rec * r
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
#define ap_get_core_module_config(v)
Definition http_core.h:383
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_INFO
Definition http_log.h:70
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_error
Definition http_log.h:370
#define ap_log_cerror
Definition http_log.h:498
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_TRACE1
Definition http_log.h:72
#define APLOG_DEBUG
Definition http_log.h:71
int ap_map_http_request_error(apr_status_t rv, int status)
void ap_die(int type, request_rec *r)
const char apr_port_t port
Definition http_vhost.h:125
const char * host
Definition http_vhost.h:124
apr_brigade_flush void * ctx
const char apr_ssize_t int flags
Definition apr_encode.h:168
const char apr_ssize_t slen
Definition apr_encode.h:168
const char * url
Definition apr_escape.h:120
#define APR_HOOK_FIRST
Definition apr_hooks.h:301
#define APR_HOOK_MIDDLE
Definition apr_hooks.h:303
#define APR_RETRIEVE_OPTIONAL_FN(name)
const char * uri
Definition apr_uri.h:159
#define HTTP_BAD_REQUEST
Definition httpd.h:508
#define HTTP_SERVICE_UNAVAILABLE
Definition httpd.h:538
#define HTTP_BAD_GATEWAY
Definition httpd.h:537
#define HTTP_FORBIDDEN
Definition httpd.h:511
int ap_proxy_connect_backend(const char *proxy_function, proxy_conn_rec *conn, proxy_worker *worker, server_rec *s)
char * ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, char **passwordp, char **hostp, apr_port_t *port)
Definition proxy_util.c:342
int ap_proxy_release_connection(const char *proxy_function, proxy_conn_rec *conn, server_rec *s)
int ap_proxy_acquire_connection(const char *proxy_function, proxy_conn_rec **conn, proxy_worker *worker, server_rec *s)
char * ap_proxy_canonenc_ex(apr_pool_t *p, const char *x, int len, enum enctype t, int flags, int proxyreq)
Definition proxy_util.c:220
#define PROXY_CANONENC_NOENCODEDSLASHENCODING
Definition mod_proxy.h:81
void proxy_hook_canon_handler(proxy_HOOK_canon_handler_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition mod_proxy.c:3412
int ap_proxy_connection_create_ex(const char *proxy_function, proxy_conn_rec *conn, request_rec *r)
apr_port_t ap_proxy_port_of_scheme(const char *scheme)
int ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, proxy_server_conf *conf, proxy_worker *worker, proxy_conn_rec *conn, apr_uri_t *uri, char **url, const char *proxyname, apr_port_t proxyport, char *server_portstr, int server_portstr_size)
void proxy_hook_scheme_handler(proxy_HOOK_scheme_handler_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition mod_proxy.c:3406
char * ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t, int forcedec, int proxyreq)
Definition proxy_util.c:321
@ enc_path
Definition mod_proxy.h:76
#define STANDARD20_MODULE_STUFF
#define ap_strchr_c(s, c)
Definition httpd.h:2353
const char * ap_scan_vchar_obstext(const char *ptr)
Definition util.c:1674
int ap_cstr_casecmpn(const char *s1, const char *s2, apr_size_t n)
Definition util.c:3559
#define PROXYREQ_PROXY
Definition httpd.h:1134
#define PROXYREQ_REVERSE
Definition httpd.h:1135
apr_size_t size
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
void * data
void const char apr_status_t(* cleanup)(void *))
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_uint16_t apr_port_t
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
const char * s
Definition apr_strings.h:95
apr_int32_t apr_int32_t apr_int32_t err
int int status
int h2_proxy_session_is_reusable(h2_proxy_session *session)
apr_status_t h2_proxy_session_submit(h2_proxy_session *session, const char *url, request_rec *r, int standalone)
apr_status_t h2_proxy_session_process(h2_proxy_session *session)
void h2_proxy_session_cancel_all(h2_proxy_session *session)
h2_proxy_session * h2_proxy_session_setup(const char *id, proxy_conn_rec *p_conn, proxy_server_conf *conf, int h2_front, unsigned char window_bits_connection, unsigned char window_bits_stream, h2_proxy_request_done *done)
void h2_proxy_session_cleanup(h2_proxy_session *session, h2_proxy_request_done *done)
#define H2_PROXY_REQ_URL_NOTE
unsigned char h2_proxy_log2(int n)
#define MOD_HTTP2_VERSION
Definition h2_version.h:30
static const char * http_scheme(const request_rec *r)
Definition http_core.c:113
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
static int http2_is_h2(conn_rec *)
Definition mod_http2.c:314
static apr_file_t * out
Definition mod_info.c:85
Proxy Extension Module for Apache.
static int(* is_h2)(conn_rec *c)
static apr_status_t ctx_run(h2_proxy_ctx *ctx)
static void register_hook(apr_pool_t *p)
static apr_status_t add_request(h2_proxy_session *session, request_rec *r)
static int h2_proxy_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
static void session_req_done(h2_proxy_session *session, request_rec *r, apr_status_t status, int touched, int error_code)
static void request_done(h2_proxy_ctx *ctx, request_rec *r, apr_status_t status, int touched, int error_code)
static int proxy_http2_handler(request_rec *r, proxy_worker *worker, proxy_server_conf *conf, char *url, const char *proxyname, apr_port_t proxyport)
static int proxy_http2_canon(request_rec *r, char *url)
return NULL
Definition mod_so.c:359
static sed_label_t * search(sed_commands_t *commands)
Definition sed0.c:907
Structure to store things which are per connection.
Definition httpd.h:1152
apr_pool_t * pool
Definition httpd.h:1154
char * client_ip
Definition httpd.h:1171
char * remote_host
Definition httpd.h:1175
long id
Definition httpd.h:1187
Per-directory configuration.
Definition http_core.h:527
proxy_worker * worker
request_rec * r
server_rec * server
char server_portstr[32]
const char * id
proxy_server_conf * conf
const char * proxy_func
apr_pool_t * pool
apr_size_t req_buffer_size
conn_rec * cfront
proxy_conn_rec * p_conn
A structure that represents the current request.
Definition httpd.h:845
apr_table_t * notes
Definition httpd.h:985
apr_pool_t * pool
Definition httpd.h:847
char * filename
Definition httpd.h:1018
int proxyreq
Definition httpd.h:873
conn_rec * connection
Definition httpd.h:849
server_rec * server
Definition httpd.h:851
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
char * args
Definition httpd.h:1026
A structure to store information for each virtual server.
Definition httpd.h:1322
typedef int(WSAAPI *apr_winapi_fpt_WSAPoll)(IN OUT LPWSAPOLLFD fdArray