]> granicus.if.org Git - apache/blob - modules/http2/mod_proxy_http2.c
mod_http2: mergine trunk+2.4.x code divergences back into a single source with proper...
[apache] / modules / http2 / mod_proxy_http2.c
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
33 static void register_hook(apr_pool_t *p);
34
35 AP_DECLARE_MODULE(proxy_http2) = {
36     STANDARD20_MODULE_STUFF,
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)
44     AP_MODULE_FLAG_ALWAYS_MERGE
45 #endif
46 };
47
48 /* Optional functions from mod_http2 */
49 static int (*is_h2)(conn_rec *c);
50 static apr_status_t (*req_engine_push)(const char *name, request_rec *r, 
51                                        http2_req_engine_init *einit);
52 static apr_status_t (*req_engine_pull)(h2_req_engine *engine, 
53                                        apr_read_type_e block, 
54                                        int capacity, 
55                                        request_rec **pr);
56 static void (*req_engine_done)(h2_req_engine *engine, conn_rec *r_conn,
57                                apr_status_t status);
58                                        
59 typedef struct h2_proxy_ctx {
60     conn_rec *owner;
61     apr_pool_t *pool;
62     request_rec *rbase;
63     server_rec *server;
64     const char *proxy_func;
65     char server_portstr[32];
66     proxy_conn_rec *p_conn;
67     proxy_worker *worker;
68     proxy_server_conf *conf;
69     
70     h2_req_engine *engine;
71     const char *engine_id;
72     const char *engine_type;
73     apr_pool_t *engine_pool;    
74     apr_size_t req_buffer_size;
75     h2_proxy_fifo *requests;
76     int capacity;
77     
78     unsigned standalone : 1;
79     unsigned is_ssl : 1;
80     unsigned flushall : 1;
81     
82     apr_status_t r_status;     /* status of our first request work */
83     h2_proxy_session *session; /* current http2 session against backend */
84 } h2_proxy_ctx;
85
86 static int h2_proxy_post_config(apr_pool_t *p, apr_pool_t *plog,
87                                 apr_pool_t *ptemp, server_rec *s)
88 {
89     void *data = NULL;
90     const char *init_key = "mod_proxy_http2_init_counter";
91     nghttp2_info *ngh2;
92     apr_status_t status = APR_SUCCESS;
93     (void)plog;(void)ptemp;
94     
95     apr_pool_userdata_get(&data, init_key, s->process->pool);
96     if ( data == NULL ) {
97         apr_pool_userdata_set((const void *)1, init_key,
98                               apr_pool_cleanup_null, s->process->pool);
99         return APR_SUCCESS;
100     }
101     
102     ngh2 = nghttp2_version(0);
103     ap_log_error( APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(03349)
104                  "mod_proxy_http2 (v%s, nghttp2 %s), initializing...",
105                  MOD_HTTP2_VERSION, ngh2? ngh2->version_str : "unknown");
106     
107     is_h2 = APR_RETRIEVE_OPTIONAL_FN(http2_is_h2);
108     req_engine_push = APR_RETRIEVE_OPTIONAL_FN(http2_req_engine_push);
109     req_engine_pull = APR_RETRIEVE_OPTIONAL_FN(http2_req_engine_pull);
110     req_engine_done = APR_RETRIEVE_OPTIONAL_FN(http2_req_engine_done);
111     
112     /* we need all of them */
113     if (!req_engine_push || !req_engine_pull || !req_engine_done) {
114         req_engine_push = NULL;
115         req_engine_pull = NULL;
116         req_engine_done = NULL;
117     }
118     
119     return status;
120 }
121
122 /**
123  * canonicalize the url into the request, if it is meant for us.
124  * slightly modified copy from mod_http
125  */
126 static int proxy_http2_canon(request_rec *r, char *url)
127 {
128     char *host, *path, sport[7];
129     char *search = NULL;
130     const char *err;
131     const char *scheme;
132     const char *http_scheme;
133     apr_port_t port, def_port;
134
135     /* ap_port_of_scheme() */
136     if (ap_cstr_casecmpn(url, "h2c:", 4) == 0) {
137         url += 4;
138         scheme = "h2c";
139         http_scheme = "http";
140     }
141     else if (ap_cstr_casecmpn(url, "h2:", 3) == 0) {
142         url += 3;
143         scheme = "h2";
144         http_scheme = "https";
145     }
146     else {
147         return DECLINED;
148     }
149     port = def_port = ap_proxy_port_of_scheme(http_scheme);
150
151     ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
152                   "HTTP2: canonicalising URL %s", url);
153
154     /* do syntatic check.
155      * We break the URL into host, port, path, search
156      */
157     err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
158     if (err) {
159         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03350)
160                       "error parsing URL %s: %s", url, err);
161         return HTTP_BAD_REQUEST;
162     }
163
164     /*
165      * now parse path/search args, according to rfc1738:
166      * process the path.
167      *
168      * In a reverse proxy, our URL has been processed, so canonicalise
169      * unless proxy-nocanon is set to say it's raw
170      * In a forward proxy, we have and MUST NOT MANGLE the original.
171      */
172     switch (r->proxyreq) {
173     default: /* wtf are we doing here? */
174     case PROXYREQ_REVERSE:
175         if (apr_table_get(r->notes, "proxy-nocanon")) {
176             path = url;   /* this is the raw path */
177         }
178         else {
179             path = ap_proxy_canonenc(r->pool, url, (int)strlen(url),
180                                      enc_path, 0, r->proxyreq);
181             search = r->args;
182         }
183         break;
184     case PROXYREQ_PROXY:
185         path = url;
186         break;
187     }
188
189     if (path == NULL) {
190         return HTTP_BAD_REQUEST;
191     }
192
193     if (port != def_port) {
194         apr_snprintf(sport, sizeof(sport), ":%d", port);
195     }
196     else {
197         sport[0] = '\0';
198     }
199
200     if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
201         host = apr_pstrcat(r->pool, "[", host, "]", NULL);
202     }
203     r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
204             "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
205     return OK;
206 }
207
208 static void out_consumed(void *baton, conn_rec *c, apr_off_t bytes)
209 {
210     h2_proxy_ctx *ctx = baton;
211     
212     if (ctx->session) {
213         h2_proxy_session_update_window(ctx->session, c, bytes);
214     }
215 }
216
217 static apr_status_t proxy_engine_init(h2_req_engine *engine, 
218                                         const char *id, 
219                                         const char *type,
220                                         apr_pool_t *pool, 
221                                         apr_size_t req_buffer_size,
222                                         request_rec *r,
223                                         http2_output_consumed **pconsumed,
224                                         void **pctx)
225 {
226     h2_proxy_ctx *ctx = ap_get_module_config(r->connection->conn_config, 
227                                              &proxy_http2_module);
228     if (!ctx) {
229         ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(03368)
230                       "h2_proxy_session, engine init, no ctx found");
231         return APR_ENOTIMPL;
232     }
233     
234     ctx->pool = pool;
235     ctx->engine = engine;
236     ctx->engine_id = id;
237     ctx->engine_type = type;
238     ctx->engine_pool = pool;
239     ctx->req_buffer_size = req_buffer_size;
240     ctx->capacity = H2MIN(100, h2_proxy_fifo_capacity(ctx->requests));
241     
242     *pconsumed = out_consumed;
243     *pctx = ctx;
244     return APR_SUCCESS;
245 }
246
247 static apr_status_t add_request(h2_proxy_session *session, request_rec *r)
248 {
249     h2_proxy_ctx *ctx = session->user_data;
250     const char *url;
251     apr_status_t status;
252
253     url = apr_table_get(r->notes, H2_PROXY_REQ_URL_NOTE);
254     apr_table_setn(r->notes, "proxy-source-port", apr_psprintf(r->pool, "%hu",
255                    ctx->p_conn->connection->local_addr->port));
256     status = h2_proxy_session_submit(session, url, r, ctx->standalone);
257     if (status != APR_SUCCESS) {
258         ap_log_cerror(APLOG_MARK, APLOG_ERR, status, r->connection, APLOGNO(03351)
259                       "pass request body failed to %pI (%s) from %s (%s)",
260                       ctx->p_conn->addr, ctx->p_conn->hostname ? 
261                       ctx->p_conn->hostname: "", session->c->client_ip, 
262                       session->c->remote_host ? session->c->remote_host: "");
263     }
264     return status;
265 }
266
267 static void request_done(h2_proxy_ctx *ctx, request_rec *r,
268                          apr_status_t status, int touched)
269 {   
270     const char *task_id = apr_table_get(r->connection->notes, H2_TASK_ID_NOTE);
271
272     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, r->connection, 
273                   "h2_proxy_session(%s): request done %s, touched=%d",
274                   ctx->engine_id, task_id, touched);
275     if (status != APR_SUCCESS) {
276         if (!touched) {
277             /* untouched request, need rescheduling */
278             status = h2_proxy_fifo_push(ctx->requests, r);
279             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, r->connection, 
280                           APLOGNO(03369)
281                           "h2_proxy_session(%s): rescheduled request %s",
282                           ctx->engine_id, task_id);
283             return;
284         }
285         else {
286             const char *uri;
287             uri = apr_uri_unparse(r->pool, &r->parsed_uri, 0);
288             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, r->connection, 
289                           APLOGNO(03471) "h2_proxy_session(%s): request %s -> %s "
290                           "not complete, cannot repeat", 
291                           ctx->engine_id, task_id, uri);
292         }
293     }
294     
295     if (r == ctx->rbase) {
296         ctx->r_status = ((status == APR_SUCCESS)? APR_SUCCESS
297                          : HTTP_SERVICE_UNAVAILABLE);
298     }
299     
300     if (req_engine_done && ctx->engine) {
301         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, r->connection, 
302                       APLOGNO(03370)
303                       "h2_proxy_session(%s): finished request %s",
304                       ctx->engine_id, task_id);
305         req_engine_done(ctx->engine, r->connection, status);
306     }
307 }    
308
309 static void session_req_done(h2_proxy_session *session, request_rec *r,
310                              apr_status_t status, int touched)
311 {
312     request_done(session->user_data, r, status, touched);
313 }
314
315 static apr_status_t next_request(h2_proxy_ctx *ctx, int before_leave)
316 {
317     if (h2_proxy_fifo_count(ctx->requests) > 0) {
318         return APR_SUCCESS;
319     }
320     else if (req_engine_pull && ctx->engine) {
321         apr_status_t status;
322         request_rec *r = NULL;
323         
324         status = req_engine_pull(ctx->engine, before_leave? 
325                                  APR_BLOCK_READ: APR_NONBLOCK_READ, 
326                                  ctx->capacity, &r);
327         if (status == APR_SUCCESS && r) {
328             ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, ctx->owner, 
329                           "h2_proxy_engine(%s): pulled request (%s) %s", 
330                           ctx->engine_id, 
331                           before_leave? "before leave" : "regular", 
332                           r->the_request);
333             h2_proxy_fifo_push(ctx->requests, r);
334         }
335         return APR_STATUS_IS_EAGAIN(status)? APR_SUCCESS : status;
336     }
337     return APR_EOF;
338 }
339
340 static apr_status_t proxy_engine_run(h2_proxy_ctx *ctx) {
341     apr_status_t status = OK;
342     int h2_front;
343     request_rec *r;
344     
345     /* Step Four: Send the Request in a new HTTP/2 stream and
346      * loop until we got the response or encounter errors.
347      */
348     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->owner, 
349                   "eng(%s): setup session", ctx->engine_id);
350     h2_front = is_h2? is_h2(ctx->owner) : 0;
351     ctx->session = h2_proxy_session_setup(ctx->engine_id, ctx->p_conn, ctx->conf,
352                                           h2_front, 30, 
353                                           h2_proxy_log2((int)ctx->req_buffer_size), 
354                                           session_req_done);
355     if (!ctx->session) {
356         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, 
357                       APLOGNO(03372) "session unavailable");
358         return HTTP_SERVICE_UNAVAILABLE;
359     }
360     
361     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03373)
362                   "eng(%s): run session %s", ctx->engine_id, ctx->session->id);
363     ctx->session->user_data = ctx;
364     
365     while (!ctx->owner->aborted) {
366         if (APR_SUCCESS == h2_proxy_fifo_try_pull(ctx->requests, (void**)&r)) {
367             add_request(ctx->session, r);
368         }
369         
370         status = h2_proxy_session_process(ctx->session);
371         
372         if (status == APR_SUCCESS) {
373             apr_status_t s2;
374             /* ongoing processing, call again */
375             if (ctx->session->remote_max_concurrent > 0
376                 && ctx->session->remote_max_concurrent != ctx->capacity) {
377                 ctx->capacity = H2MIN((int)ctx->session->remote_max_concurrent, 
378                                       h2_proxy_fifo_capacity(ctx->requests));
379             }
380             s2 = next_request(ctx, 0);
381             if (s2 == APR_ECONNABORTED) {
382                 /* master connection gone */
383                 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, s2, ctx->owner, 
384                               APLOGNO(03374) "eng(%s): pull request", 
385                               ctx->engine_id);
386                 /* give notice that we're leaving and cancel all ongoing
387                  * streams. */
388                 next_request(ctx, 1); 
389                 h2_proxy_session_cancel_all(ctx->session);
390                 h2_proxy_session_process(ctx->session);
391                 status = ctx->r_status = APR_SUCCESS;
392                 break;
393             }
394             if ((h2_proxy_fifo_count(ctx->requests) == 0) 
395                 && h2_proxy_ihash_empty(ctx->session->streams)) {
396                 break;
397             }
398         }
399         else {
400             /* end of processing, maybe error */
401             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, 
402                           APLOGNO(03375) "eng(%s): end of session %s", 
403                           ctx->engine_id, ctx->session->id);
404             /*
405              * Any open stream of that session needs to
406              * a) be reopened on the new session iff safe to do so
407              * b) reported as done (failed) otherwise
408              */
409             h2_proxy_session_cleanup(ctx->session, session_req_done);
410             break;
411         }
412     }
413     
414     ctx->session->user_data = NULL;
415     ctx->session = NULL;
416     
417     return status;
418 }
419
420 static apr_status_t push_request_somewhere(h2_proxy_ctx *ctx, request_rec *r)
421 {
422     conn_rec *c = ctx->owner;
423     const char *engine_type, *hostname;
424     
425     hostname = (ctx->p_conn->ssl_hostname? 
426                 ctx->p_conn->ssl_hostname : ctx->p_conn->hostname);
427     engine_type = apr_psprintf(ctx->pool, "proxy_http2 %s%s", hostname, 
428                                ctx->server_portstr);
429     
430     if (c->master && req_engine_push && r && is_h2 && is_h2(c)) {
431         /* If we are have req_engine capabilities, push the handling of this
432          * request (e.g. slave connection) to a proxy_http2 engine which 
433          * uses the same backend. We may be called to create an engine 
434          * ourself. */
435         if (req_engine_push(engine_type, r, proxy_engine_init) == APR_SUCCESS) {
436             if (ctx->engine == NULL) {
437                 /* request has been assigned to an engine in another thread */
438                 return SUSPENDED;
439             }
440         }
441     }
442     
443     if (!ctx->engine) {
444         /* No engine was available or has been initialized, handle this
445          * request just by ourself. */
446         ctx->engine_id = apr_psprintf(ctx->pool, "eng-proxy-%ld", c->id);
447         ctx->engine_type = engine_type;
448         ctx->engine_pool = ctx->pool;
449         ctx->req_buffer_size = (32*1024);
450         ctx->standalone = 1;
451         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, 
452                       "h2_proxy_http2(%ld): setup standalone engine for type %s", 
453                       c->id, engine_type);
454     }
455     else {
456         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, 
457                       "H2: hosting engine %s", ctx->engine_id);
458     }
459
460     return h2_proxy_fifo_push(ctx->requests, r);
461 }
462
463 static int proxy_http2_handler(request_rec *r, 
464                                proxy_worker *worker,
465                                proxy_server_conf *conf,
466                                char *url, 
467                                const char *proxyname,
468                                apr_port_t proxyport)
469 {
470     const char *proxy_func;
471     char *locurl = url, *u;
472     apr_size_t slen;
473     int is_ssl = 0;
474     apr_status_t status;
475     h2_proxy_ctx *ctx;
476     apr_uri_t uri;
477     int reconnects = 0;
478     
479     /* find the scheme */
480     if ((url[0] != 'h' && url[0] != 'H') || url[1] != '2') {
481        return DECLINED;
482     }
483     u = strchr(url, ':');
484     if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0') {
485        return DECLINED;
486     }
487     slen = (u - url);
488     switch(slen) {
489         case 2:
490             proxy_func = "H2";
491             is_ssl = 1;
492             break;
493         case 3:
494             if (url[2] != 'c' && url[2] != 'C') {
495                 return DECLINED;
496             }
497             proxy_func = "H2C";
498             break;
499         default:
500             return DECLINED;
501     }
502     
503     ctx = apr_pcalloc(r->pool, sizeof(*ctx));
504     ctx->owner      = r->connection;
505     ctx->pool       = r->pool;
506     ctx->rbase      = r;
507     ctx->server     = r->server;
508     ctx->proxy_func = proxy_func;
509     ctx->is_ssl     = is_ssl;
510     ctx->worker     = worker;
511     ctx->conf       = conf;
512     ctx->flushall   = apr_table_get(r->subprocess_env, "proxy-flushall")? 1 : 0;
513     ctx->r_status   = HTTP_SERVICE_UNAVAILABLE;
514     
515     h2_proxy_fifo_set_create(&ctx->requests, ctx->pool, 100);
516     
517     ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, ctx);
518
519     /* scheme says, this is for us. */
520     apr_table_setn(ctx->rbase->notes, H2_PROXY_REQ_URL_NOTE, url);
521     ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->rbase, 
522                   "H2: serving URL %s", url);
523     
524 run_connect:    
525     /* Get a proxy_conn_rec from the worker, might be a new one, might
526      * be one still open from another request, or it might fail if the
527      * worker is stopped or in error. */
528     if ((status = ap_proxy_acquire_connection(ctx->proxy_func, &ctx->p_conn,
529                                               ctx->worker, ctx->server)) != OK) {
530         goto cleanup;
531     }
532
533     ctx->p_conn->is_ssl = ctx->is_ssl;
534
535     /* Step One: Determine the URL to connect to (might be a proxy),
536      * initialize the backend accordingly and determine the server 
537      * port string we can expect in responses. */
538     if ((status = ap_proxy_determine_connection(ctx->pool, ctx->rbase, conf, worker, 
539                                                 ctx->p_conn, &uri, &locurl, 
540                                                 proxyname, proxyport, 
541                                                 ctx->server_portstr,
542                                                 sizeof(ctx->server_portstr))) != OK) {
543         goto cleanup;
544     }
545     
546     /* If we are not already hosting an engine, try to push the request 
547      * to an already existing engine or host a new engine here. */
548     if (r && !ctx->engine) {
549         ctx->r_status = push_request_somewhere(ctx, r);
550         r = NULL;
551         if (ctx->r_status == SUSPENDED) {
552             /* request was pushed to another thread, leave processing here */
553             goto cleanup;
554         }
555     }
556     
557     /* Step Two: Make the Connection (or check that an already existing
558      * socket is still usable). On success, we have a socket connected to
559      * backend->hostname. */
560     if (ap_proxy_connect_backend(ctx->proxy_func, ctx->p_conn, ctx->worker, 
561                                  ctx->server)) {
562         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03352)
563                       "H2: failed to make connection to backend: %s",
564                       ctx->p_conn->hostname);
565         goto reconnect;
566     }
567     
568     /* Step Three: Create conn_rec for the socket we have open now. */
569     if (!ctx->p_conn->connection) {
570         status = ap_proxy_connection_create_ex(ctx->proxy_func,
571                                                ctx->p_conn, ctx->rbase);
572         if (status != OK) {
573             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, APLOGNO(03353)
574                           "setup new connection: is_ssl=%d %s %s %s", 
575                           ctx->p_conn->is_ssl, ctx->p_conn->ssl_hostname, 
576                           locurl, ctx->p_conn->hostname);
577             goto reconnect;
578         }
579         
580         if (!ctx->p_conn->data && ctx->is_ssl) {
581             /* New SSL connection: set a note on the connection about what
582              * protocol we want.
583              */
584             apr_table_setn(ctx->p_conn->connection->notes,
585                            "proxy-request-alpn-protos", "h2");
586         }
587     }
588
589 run_session:
590     status = proxy_engine_run(ctx);
591     if (status == APR_SUCCESS) {
592         /* session and connection still ok */
593         if (next_request(ctx, 1) == APR_SUCCESS) {
594             /* more requests, run again */
595             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03376)
596                           "run_session, again");
597             goto run_session;
598         }
599         /* done */
600         ctx->engine = NULL;
601     }
602
603 reconnect:
604     if (next_request(ctx, 1) == APR_SUCCESS) {
605         /* Still more to do, tear down old conn and start over */
606         if (ctx->p_conn) {
607             ctx->p_conn->close = 1;
608 #if AP_MODULE_MAGIC_AT_LEAST(20140207, 2)
609             proxy_run_detach_backend(r, ctx->p_conn);
610 #endif
611             ap_proxy_release_connection(ctx->proxy_func, ctx->p_conn, ctx->server);
612             ctx->p_conn = NULL;
613         }
614         ++reconnects;
615         if (reconnects < 5 && !ctx->owner->aborted) {
616             goto run_connect;
617         } 
618         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(10023)
619                       "giving up after %d reconnects, %d requests todo",
620                       reconnects, h2_proxy_fifo_count(ctx->requests));
621     }
622     
623 cleanup:
624     if (ctx->p_conn) {
625         if (status != APR_SUCCESS) {
626             /* close socket when errors happened or session shut down (EOF) */
627             ctx->p_conn->close = 1;
628         }
629 #if AP_MODULE_MAGIC_AT_LEAST(20140207, 2)
630         proxy_run_detach_backend(ctx->rbase, ctx->p_conn);
631 #endif
632         ap_proxy_release_connection(ctx->proxy_func, ctx->p_conn, ctx->server);
633         ctx->p_conn = NULL;
634     }
635
636     /* Any requests will still have need to fail */
637     while (APR_SUCCESS == h2_proxy_fifo_try_pull(ctx->requests, (void**)&r)) {
638         request_done(ctx, r, HTTP_SERVICE_UNAVAILABLE, 1);
639     }
640     
641     ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, NULL);
642     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, 
643                   APLOGNO(03377) "leaving handler");
644     return ctx->r_status;
645 }
646
647 static void register_hook(apr_pool_t *p)
648 {
649     ap_hook_post_config(h2_proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE);
650
651     proxy_hook_scheme_handler(proxy_http2_handler, NULL, NULL, APR_HOOK_FIRST);
652     proxy_hook_canon_handler(proxy_http2_canon, NULL, NULL, APR_HOOK_FIRST);
653 }
654