diff options
Diffstat (limited to 'modules/http2/mod_proxy_http2.c')
-rw-r--r-- | modules/http2/mod_proxy_http2.c | 148 |
1 files changed, 86 insertions, 62 deletions
diff --git a/modules/http2/mod_proxy_http2.c b/modules/http2/mod_proxy_http2.c index aa299b9..ebf8f61 100644 --- a/modules/http2/mod_proxy_http2.c +++ b/modules/http2/mod_proxy_http2.c @@ -50,8 +50,7 @@ static int (*is_h2)(conn_rec *c); typedef struct h2_proxy_ctx { const char *id; - conn_rec *master; - conn_rec *owner; + conn_rec *cfront; apr_pool_t *pool; server_rec *server; const char *proxy_func; @@ -66,10 +65,10 @@ typedef struct h2_proxy_ctx { unsigned is_ssl : 1; request_rec *r; /* the request processed in this ctx */ - apr_status_t r_status; /* status of request work */ + int r_status; /* status of request work */ int r_done; /* request was processed, not necessarily successfully */ int r_may_retry; /* request may be retried */ - h2_proxy_session *session; /* current http2 session against backend */ + int has_reusable_session; /* http2 session is live and clean */ } h2_proxy_ctx; static int h2_proxy_post_config(apr_pool_t *p, apr_pool_t *plog, @@ -154,29 +153,46 @@ static int proxy_http2_canon(request_rec *r, char *url) if (apr_table_get(r->notes, "proxy-nocanon")) { path = url; /* this is the raw path */ } + else if (apr_table_get(r->notes, "proxy-noencode")) { + path = url; /* this is the encoded path already */ + search = r->args; + } else { +#ifdef PROXY_CANONENC_NOENCODEDSLASHENCODING + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, (int)strlen(url), + enc_path, flags, r->proxyreq); +#else path = ap_proxy_canonenc(r->pool, url, (int)strlen(url), enc_path, 0, r->proxyreq); - search = r->args; - if (search && *(ap_scan_vchar_obstext(search))) { - /* - * We have a raw control character or a ' ' in r->args. - * Correct encoding was missed. - */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO() - "To be forwarded query string contains control " - "characters or spaces"); - return HTTP_FORBIDDEN; +#endif + if (!path) { + return HTTP_BAD_REQUEST; } + search = r->args; } break; case PROXYREQ_PROXY: path = url; break; } - - if (path == NULL) { - return HTTP_BAD_REQUEST; + /* + * If we have a raw control character or a ' ' in nocanon path or + * r->args, correct encoding was missed. + */ + if (path == url && *ap_scan_vchar_obstext(path)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10420) + "To be forwarded path contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + if (search && *ap_scan_vchar_obstext(search)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10412) + "To be forwarded query string contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; } if (port != def_port) { @@ -215,79 +231,81 @@ static apr_status_t add_request(h2_proxy_session *session, request_rec *r) } static void request_done(h2_proxy_ctx *ctx, request_rec *r, - apr_status_t status, int touched) + apr_status_t status, int touched, int error_code) { if (r == ctx->r) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, r->connection, - "h2_proxy_session(%s): request done, touched=%d", - ctx->id, touched); + "h2_proxy_session(%s): request done, touched=%d, error=%d", + ctx->id, touched, error_code); ctx->r_done = 1; if (touched) ctx->r_may_retry = 0; - ctx->r_status = ((status == APR_SUCCESS)? APR_SUCCESS - : HTTP_SERVICE_UNAVAILABLE); + ctx->r_status = error_code? HTTP_BAD_GATEWAY : + ((status == APR_SUCCESS)? OK : + ap_map_http_request_error(status, HTTP_SERVICE_UNAVAILABLE)); } } static void session_req_done(h2_proxy_session *session, request_rec *r, - apr_status_t status, int touched) + apr_status_t status, int touched, int error_code) { - request_done(session->user_data, r, status, touched); + request_done(session->user_data, r, status, touched, error_code); } static apr_status_t ctx_run(h2_proxy_ctx *ctx) { apr_status_t status = OK; + h2_proxy_session *session; int h2_front; /* Step Four: Send the Request in a new HTTP/2 stream and * loop until we got the response or encounter errors. */ - h2_front = is_h2? is_h2(ctx->owner) : 0; - ctx->session = h2_proxy_session_setup(ctx->id, ctx->p_conn, ctx->conf, - h2_front, 30, - h2_proxy_log2((int)ctx->req_buffer_size), - session_req_done); - if (!ctx->session) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, + ctx->has_reusable_session = 0; /* don't know yet */ + h2_front = is_h2? is_h2(ctx->cfront) : 0; + session = h2_proxy_session_setup(ctx->id, ctx->p_conn, ctx->conf, + h2_front, 30, + h2_proxy_log2((int)ctx->req_buffer_size), + session_req_done); + if (!session) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->cfront, APLOGNO(03372) "session unavailable"); return HTTP_SERVICE_UNAVAILABLE; } - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03373) - "eng(%s): run session %s", ctx->id, ctx->session->id); - ctx->session->user_data = ctx; + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->cfront, APLOGNO(03373) + "eng(%s): run session %s", ctx->id, session->id); + session->user_data = ctx; ctx->r_done = 0; - add_request(ctx->session, ctx->r); + add_request(session, ctx->r); - while (!ctx->owner->aborted && !ctx->r_done) { + while (!ctx->cfront->aborted && !ctx->r_done) { - status = h2_proxy_session_process(ctx->session); + status = h2_proxy_session_process(session); if (status != APR_SUCCESS) { /* Encountered an error during session processing */ - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->cfront, APLOGNO(03375) "eng(%s): end of session %s", - ctx->id, ctx->session->id); + ctx->id, session->id); /* Any open stream of that session needs to * a) be reopened on the new session iff safe to do so * b) reported as done (failed) otherwise */ - h2_proxy_session_cleanup(ctx->session, session_req_done); + h2_proxy_session_cleanup(session, session_req_done); goto out; } } out: - if (ctx->owner->aborted) { + if (ctx->cfront->aborted) { /* master connection gone */ - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->cfront, APLOGNO(03374) "eng(%s): master connection gone", ctx->id); /* cancel all ongoing requests */ - h2_proxy_session_cancel_all(ctx->session); - h2_proxy_session_process(ctx->session); + h2_proxy_session_cancel_all(session); + h2_proxy_session_process(session); } - - ctx->session->user_data = NULL; - ctx->session = NULL; + ctx->has_reusable_session = h2_proxy_session_is_reusable(session); + session->user_data = NULL; return status; } @@ -332,9 +350,8 @@ static int proxy_http2_handler(request_rec *r, } ctx = apr_pcalloc(r->pool, sizeof(*ctx)); - ctx->master = r->connection->master? r->connection->master : r->connection; - ctx->id = apr_psprintf(r->pool, "%ld", (long)ctx->master->id); - ctx->owner = r->connection; + ctx->id = apr_psprintf(r->pool, "%ld", (long)r->connection->id); + ctx->cfront = r->connection; ctx->pool = r->pool; ctx->server = r->server; ctx->proxy_func = proxy_func; @@ -347,7 +364,7 @@ static int proxy_http2_handler(request_rec *r, ctx->r_done = 0; ctx->r_may_retry = 1; - ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, ctx); + ap_set_module_config(ctx->cfront->conn_config, &proxy_http2_module, ctx); /* scheme says, this is for us. */ apr_table_setn(ctx->r->notes, H2_PROXY_REQ_URL_NOTE, url); @@ -355,7 +372,7 @@ static int proxy_http2_handler(request_rec *r, "H2: serving URL %s", url); run_connect: - if (ctx->owner->aborted) goto cleanup; + if (ctx->cfront->aborted) goto cleanup; /* Get a proxy_conn_rec from the worker, might be a new one, might * be one still open from another request, or it might fail if the @@ -383,7 +400,7 @@ run_connect: * backend->hostname. */ if (ap_proxy_connect_backend(ctx->proxy_func, ctx->p_conn, ctx->worker, ctx->server)) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03352) + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->cfront, APLOGNO(03352) "H2: failed to make connection to backend: %s", ctx->p_conn->hostname); goto cleanup; @@ -392,11 +409,11 @@ run_connect: /* Step Three: Create conn_rec for the socket we have open now. */ status = ap_proxy_connection_create_ex(ctx->proxy_func, ctx->p_conn, ctx->r); if (status != OK) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, APLOGNO(03353) + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->cfront, APLOGNO(03353) "setup new connection: is_ssl=%d %s %s %s", ctx->p_conn->is_ssl, ctx->p_conn->ssl_hostname, locurl, ctx->p_conn->hostname); - ctx->r_status = status; + ctx->r_status = ap_map_http_request_error(status, HTTP_SERVICE_UNAVAILABLE); goto cleanup; } @@ -407,10 +424,10 @@ run_connect: "proxy-request-alpn-protos", "h2"); } - if (ctx->owner->aborted) goto cleanup; + if (ctx->cfront->aborted) goto cleanup; status = ctx_run(ctx); - if (ctx->r_status != APR_SUCCESS && ctx->r_may_retry && !ctx->owner->aborted) { + if (ctx->r_status != OK && ctx->r_may_retry && !ctx->cfront->aborted) { /* Not successfully processed, but may retry, tear down old conn and start over */ if (ctx->p_conn) { ctx->p_conn->close = 1; @@ -424,15 +441,16 @@ run_connect: if (reconnects < 2) { goto run_connect; } - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(10023) + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->cfront, APLOGNO(10023) "giving up after %d reconnects, request-done=%d", reconnects, ctx->r_done); } cleanup: if (ctx->p_conn) { - if (status != APR_SUCCESS) { - /* close socket when errors happened or session shut down (EOF) */ + if (status != APR_SUCCESS || !ctx->has_reusable_session) { + /* close socket when errors happened or session is not "clean", + * meaning in a working condition with no open streams */ ctx->p_conn->close = 1; } #if AP_MODULE_MAGIC_AT_LEAST(20140207, 2) @@ -442,9 +460,15 @@ cleanup: ctx->p_conn = NULL; } - ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, NULL); - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, + ap_set_module_config(ctx->cfront->conn_config, &proxy_http2_module, NULL); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->cfront, APLOGNO(03377) "leaving handler"); + if (ctx->r_status != OK) { + ap_die(ctx->r_status, r); + } + else if (status != APR_SUCCESS) { + ap_die(ap_map_http_request_error(status, HTTP_SERVICE_UNAVAILABLE), r); + } return ctx->r_status; } |