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:
2
changes-entries/tunnel_forward_hook.txt
Normal file
2
changes-entries/tunnel_forward_hook.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*) mod_proxy: New tunnel_forward hooking mechanism allowing modules to handle
|
||||||
|
Upgrade(d) protocols data. [Yann Ylavic]
|
@@ -1 +1 @@
|
|||||||
10295
|
10297
|
||||||
|
@@ -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)
|
||||||
|
@@ -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.
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user