1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-08 15:02:10 +03:00

mod_proxy: Add tunnel_forward hook.

* modules/proxy/mod_proxy.h, modules/proxy/mod_proxy.c:
  Declare/implement the hook.

* modules/proxy/proxy_util.c(proxy_transfer):
  Run tunnel_forward hooks when called by the tunneling loop.
  Simpler input/output brigade cleanup on exit.



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1893603 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yann Ylavic
2021-09-24 15:52:50 +00:00
parent b44b9b6b2a
commit 3d2842e915
5 changed files with 86 additions and 18 deletions

View File

@@ -0,0 +1,2 @@
*) mod_proxy: New tunnel_forward hooking mechanism allowing modules to handle
Upgrade(d) protocols data. [Yann Ylavic]

View File

@@ -1 +1 @@
10295 10297

View File

@@ -3474,6 +3474,7 @@ APR_HOOK_STRUCT(
APR_HOOK_LINK(post_request) APR_HOOK_LINK(post_request)
APR_HOOK_LINK(request_status) APR_HOOK_LINK(request_status)
APR_HOOK_LINK(check_trans) APR_HOOK_LINK(check_trans)
APR_HOOK_LINK(tunnel_forward)
) )
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler, APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler,
@@ -3517,3 +3518,9 @@ APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, request_status,
APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, detach_backend, APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, detach_backend,
(request_rec *r, proxy_conn_rec *backend), (request_rec *r, proxy_conn_rec *backend),
(r, backend), OK, DECLINED) (r, backend), OK, DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(proxy, PROXY, int, tunnel_forward,
(proxy_tunnel_rec *tunnel,
conn_rec *c_i, conn_rec *c_o,
apr_bucket_brigade *bb),
(tunnel, c_i, c_o, bb),
OK, DECLINED)

View File

@@ -1399,6 +1399,24 @@ PROXY_DECLARE(apr_off_t) ap_proxy_tunnel_conn_bytes_in(
PROXY_DECLARE(apr_off_t) ap_proxy_tunnel_conn_bytes_out( PROXY_DECLARE(apr_off_t) ap_proxy_tunnel_conn_bytes_out(
const proxy_tunnel_conn_t *tc); const proxy_tunnel_conn_t *tc);
/**
* Tunnel forwarding hook
* Called for every brigade forwarded by a tunnel from/to the client to/from
* the origin. Each hook receives incoming buckets in bb and produces outgoing
* buckets in the same bb, much like an output filter.
* @param tunnel the tunnel
* @param c the connection the data are going to
* @param bb the incoming data
* @return OK/DECLINED to pass to the next hooks, DONE to not pass to
* the next hooks, an HTTP_ error on failure.
* @note A hook must not return DONE unless it consumes/sets-aside *all* the
* incoming buckets, and it must produce (non-meta-)data buckets only.
*/
PROXY_DECLARE_OPTIONAL_HOOK(proxy, PROXY, int, tunnel_forward,
(proxy_tunnel_rec *tunnel,
conn_rec *c_i, conn_rec *c_o,
apr_bucket_brigade *bb))
/** /**
* Clear the headers referenced by the Connection header from the given * Clear the headers referenced by the Connection header from the given
* table, and remove the Connection header. * table, and remove the Connection header.

View File

@@ -4531,7 +4531,8 @@ static apr_status_t proxy_transfer(request_rec *r,
apr_off_t bsize, apr_off_t bsize,
int flags, int flags,
apr_off_t *bytes_in, apr_off_t *bytes_in,
apr_off_t *bytes_out) apr_off_t *bytes_out,
proxy_tunnel_rec *tunnel)
{ {
apr_status_t rv; apr_status_t rv;
int flush_each = 0; int flush_each = 0;
@@ -4557,8 +4558,7 @@ static apr_status_t proxy_transfer(request_rec *r,
if (rv != APR_SUCCESS) { if (rv != APR_SUCCESS) {
if (!APR_STATUS_IS_EAGAIN(rv) && !APR_STATUS_IS_EOF(rv)) { if (!APR_STATUS_IS_EAGAIN(rv) && !APR_STATUS_IS_EOF(rv)) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(03308) ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(03308)
"proxy_transfer: error on %s - ap_get_brigade", "proxy_transfer: can't get data from %s", name);
name);
if (rv == APR_INCOMPLETE) { if (rv == APR_INCOMPLETE) {
/* Don't return APR_INCOMPLETE, it'd mean "should yield" /* Don't return APR_INCOMPLETE, it'd mean "should yield"
* for the caller, while it means "incomplete body" here * for the caller, while it means "incomplete body" here
@@ -4569,25 +4569,62 @@ static apr_status_t proxy_transfer(request_rec *r,
} }
break; break;
} }
if (c_o->aborted) { if (c_o->aborted) {
apr_brigade_cleanup(bb_i);
flags &= ~AP_PROXY_TRANSFER_FLUSH_AFTER;
rv = APR_EPIPE; rv = APR_EPIPE;
break; break;
} }
if (APR_BRIGADE_EMPTY(bb_i)) { if (APR_BRIGADE_EMPTY(bb_i)) {
break; break;
} }
len = -1; len = -1;
apr_brigade_length(bb_i, 0, &len); apr_brigade_length(bb_i, 0, &len);
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03306) ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
"proxy_transfer: read %" APR_OFF_T_FMT " bytes " "proxy_transfer: got %" APR_OFF_T_FMT " bytes "
"of %s", len, name); "from %s", len, name);
if (bytes_in && len > 0) { if (bytes_in && len > 0) {
*bytes_in += len; *bytes_in += len;
} }
ap_proxy_buckets_lifetime_transform(r, bb_i, bb_o);
rv = ap_proxy_buckets_lifetime_transform(r, bb_i, bb_o);
if (rv != APR_SUCCESS) {
break;
}
if (tunnel) {
int rc = proxy_run_tunnel_forward(tunnel, c_i, c_o, bb_o);
if (rc != OK && rc != DONE) {
if (!ap_is_HTTP_ERROR(rc)) {
/* SUSPENDED is not allowed for now */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10295)
"proxy: %s: invalid status %d returned by "
"tunnel forward hooks", tunnel->scheme, rc);
}
rv = APR_EGENERAL;
break;
}
if (APR_BRIGADE_EMPTY(bb_o)) {
/* Buckets retained by the hooks, next. */
continue;
}
if (rc == DONE) {
/* DONE with data is invalid because it'd mean that the next
* hooks wouldn't have a chance to see the data, hence no hook
* would be able to retain data.
*/
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10296)
"proxy: %s: invalid return value from tunnel "
" forward hook", tunnel->scheme);
rv = APR_EGENERAL;
break;
}
len = -1;
apr_brigade_length(bb_o, 0, &len);
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
"proxy_transfer: forward %" APR_OFF_T_FMT " bytes "
"from %s", len, name);
}
if (bytes_out && len > 0) { if (bytes_out && len > 0) {
*bytes_out += len; *bytes_out += len;
} }
@@ -4612,14 +4649,14 @@ static apr_status_t proxy_transfer(request_rec *r,
APR_BRIGADE_INSERT_TAIL(bb_o, b); APR_BRIGADE_INSERT_TAIL(bb_o, b);
} }
rv = ap_pass_brigade(c_o->output_filters, bb_o); rv = ap_pass_brigade(c_o->output_filters, bb_o);
apr_brigade_cleanup(bb_o);
if (rv != APR_SUCCESS) { if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(03307) ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(03307)
"proxy_transfer: error on %s - ap_pass_brigade", "proxy_transfer: can't pass %" APR_OFF_T_FMT
name); " bytes from %s", len, name);
flags &= ~AP_PROXY_TRANSFER_FLUSH_AFTER; flags &= ~AP_PROXY_TRANSFER_FLUSH_AFTER;
break; break;
} }
apr_brigade_cleanup(bb_o);
/* Yield if the output filters stack is full? This is to avoid /* Yield if the output filters stack is full? This is to avoid
* blocking and give the caller a chance to POLLOUT async. * blocking and give the caller a chance to POLLOUT async.
@@ -4644,11 +4681,14 @@ static apr_status_t proxy_transfer(request_rec *r,
} }
} }
if (flags & AP_PROXY_TRANSFER_FLUSH_AFTER) { /* bb_o first to avoid protential dangling buckets (transient) */
apr_brigade_cleanup(bb_o);
apr_brigade_cleanup(bb_i);
if ((flags & AP_PROXY_TRANSFER_FLUSH_AFTER) && !c_o->aborted) {
ap_fflush(c_o->output_filters, bb_o); ap_fflush(c_o->output_filters, bb_o);
apr_brigade_cleanup(bb_o); apr_brigade_cleanup(bb_o);
} }
apr_brigade_cleanup(bb_i);
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, rv, r, ap_log_rerror(APLOG_MARK, APLOG_TRACE2, rv, r,
"proxy_transfer complete (%s %pI)", "proxy_transfer complete (%s %pI)",
@@ -4675,7 +4715,7 @@ PROXY_DECLARE(apr_status_t) ap_proxy_transfer_between_connections(
{ {
apr_off_t bytes_out = 0; apr_off_t bytes_out = 0;
apr_status_t rv = proxy_transfer(r, c_i, c_o, bb_i, bb_o, name, bsize, apr_status_t rv = proxy_transfer(r, c_i, c_o, bb_i, bb_o, name, bsize,
flags, NULL, &bytes_out); flags, NULL, &bytes_out, NULL);
if (sent && bytes_out > 0) { if (sent && bytes_out > 0) {
*sent = 1; *sent = 1;
} }
@@ -4853,7 +4893,8 @@ static int proxy_tunnel_transfer(proxy_tunnel_rec *tunnel,
in->name, tunnel->read_buf_size, in->name, tunnel->read_buf_size,
AP_PROXY_TRANSFER_YIELD_PENDING | AP_PROXY_TRANSFER_YIELD_PENDING |
AP_PROXY_TRANSFER_YIELD_MAX_READS, AP_PROXY_TRANSFER_YIELD_MAX_READS,
&in->bytes_in, &out->bytes_out); &in->bytes_in, &out->bytes_out,
tunnel);
if (rv != APR_SUCCESS) { if (rv != APR_SUCCESS) {
if (APR_STATUS_IS_INCOMPLETE(rv)) { if (APR_STATUS_IS_INCOMPLETE(rv)) {
/* Pause POLLIN while waiting for POLLOUT on the other /* Pause POLLIN while waiting for POLLOUT on the other