Actually I was working on these lands lately to implement some Upgrade
forwarding.
I ended up with the attached patch (still incomplete, but working)
which moves some mod_proxy_wstunnel parts (TCP-forwarding) to
proxy_util and finally uses the parsing/power of mod_proxy_http for
this achievement.
The patch is based on 2.4.18 and does not apply to trunk or 2.4.x due
latest changes wrt lifetime_transform buckets, but could be easily
adapted...
I think handling Upgrade in mod_proxy_http would "deprecate"
mod_proxy_wstunnel which, as Tijs noticed, can't handle conditional
Upgrade w/o some logic already in mod_proxy_http, and which does not
play well with mod_proxy_http either when the requested (sub)paths
collide.
I'm proposing it now (quite quickly) to see if the approach looks
relevant/suitable/viable (and I could work further on it), or if it is
simply not a good idea :)
Thoughts?
Index: include/http_protocol.h
===================================================================
--- include/http_protocol.h (revision 1734109)
+++ include/http_protocol.h (working copy)
@@ -976,10 +976,13 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_r
*/
AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub_r);
+#define AP_SEND_INTERIM_KEEP_HEADERS 0xdeadbeef
/**
* Send an interim (HTTP 1xx) response immediately.
* @param r The request
* @param send_headers Whether to send&clear headers in r->headers_out
+ * @remark send_headers == AP_SEND_INTERIM_KEEP_HEADERS allows to send
+ * but not clear r->headers_out
*/
AP_DECLARE(void) ap_send_interim_response(request_rec *r, int send_headers);
Index: modules/proxy/mod_proxy.h
===================================================================
--- modules/proxy/mod_proxy.h (revision 1734109)
+++ modules/proxy/mod_proxy.h (working copy)
@@ -1004,6 +1004,9 @@ PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucke
conn_rec *origin, apr_bucket_brigade *bb,
int flush);
+PROXY_DECLARE(int) ap_proxy_tunnel_request(apr_pool_t *p, request_rec *r,
+ proxy_conn_rec *backend);
+
/**
* Clear the headers referenced by the Connection header from the given
* table, and remove the Connection header.
Index: modules/proxy/mod_proxy_http.c
===================================================================
--- modules/proxy/mod_proxy_http.c (revision 1734109)
+++ modules/proxy/mod_proxy_http.c (working copy)
@@ -712,6 +712,7 @@ int ap_proxy_http_request(apr_pool_t *p, request_r
enum rb_methods rb_method = RB_INIT;
char *old_cl_val = NULL;
char *old_te_val = NULL;
+ const char *upgrade = NULL;
apr_off_t bytes_read = 0;
apr_off_t bytes;
int force10, rv;
@@ -723,6 +724,9 @@ int ap_proxy_http_request(apr_pool_t *p, request_r
}
force10 = 1;
} else {
+ if (apr_table_get(r->subprocess_env, "proxy-forward-upgrade")) {
+ upgrade = apr_table_get(r->headers_in, "Upgrade");
+ }
force10 = 0;
}
@@ -925,6 +929,10 @@ skip_body:
if (!ap_proxy_connection_reusable(p_conn)) {
buf = apr_pstrdup(p, "Connection: close" CRLF);
}
+ else if (upgrade) {
+ buf = apr_pstrcat(p, "Connection: Keep-Alive, Upgrade" CRLF
+ "Upgrade: ", upgrade, CRLF, NULL);
+ }
else {
buf = apr_pstrdup(p, "Connection: Keep-Alive" CRLF);
}
@@ -1248,7 +1256,6 @@ apr_status_t ap_proxy_http_process_response(apr_po
static const char *hop_by_hop_hdrs[] =
{"Keep-Alive", "Proxy-Authenticate", "TE", "Trailer", "Upgrade", NULL};
int i;
- const char *te = NULL;
int original_status = r->status;
int proxy_status = OK;
const char *original_status_line = r->status_line;
@@ -1295,6 +1302,7 @@ apr_status_t ap_proxy_http_process_response(apr_po
origin->local_addr->port));
tmp_bb = apr_brigade_create(p, c->bucket_alloc);
do {
+ const char *te = NULL, *upgrade = NULL;
apr_status_t rc;
apr_brigade_cleanup(bb);
@@ -1490,6 +1498,11 @@ apr_status_t ap_proxy_http_process_response(apr_po
*/
te = apr_table_get(r->headers_out, "Transfer-Encoding");
+ /* Likewise for Upgrade header */
+ if (r->status == HTTP_SWITCHING_PROTOCOLS) {
+ upgrade = apr_table_get(r->headers_out, "Upgrade");
+ }
+
/* strip connection listed hop-by-hop headers from response */
toclose = ap_proxy_clear_connection_fn(r, r->headers_out);
if (toclose) {
@@ -1583,10 +1596,27 @@ apr_status_t ap_proxy_http_process_response(apr_po
"proxy-interim-response");
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
"HTTP: received interim %d response", r->status);
- if (!policy
- || (!strcasecmp(policy, "RFC") && ((r->expecting_100 = 1)))) {
+ if (!policy || !strcasecmp(policy, "RFC")) {
+ if (upgrade) {
+ apr_table_setn(r->headers_out, "Upgrade", upgrade);
+ apr_table_setn(r->headers_out, "Connection", "upgrade");
+
+ /* Since after the Upgrade this is not HTTP anymore,
+ * consider the HTTP (final) response is this status 101,
+ * hence tell ap_send_interim_response() to send but still
+ * keep (not clear) the headers.
+ */
+ ap_send_interim_response(r, AP_SEND_INTERIM_KEEP_HEADERS);
+ }
+ else
ap_send_interim_response(r, 1);
}
+ else if (upgrade) {
+ return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+ apr_psprintf(p, "Unexpected status %i "
+ "returned by remote server",
+ proxy_status));
+ }
/* FIXME: refine this to be able to specify per-response-status
* policies and maybe also add option to bail out with 502
*/
@@ -1611,31 +1641,6 @@ apr_status_t ap_proxy_http_process_response(apr_po
}
}
- r->sent_bodyct = 1;
- /*
- * Is it an HTTP/0.9 response or did we maybe preread the 1st line of
- * the response? If so, load the extra data. These are 2 mutually
- * exclusive possibilities, that just happen to require very
- * similar behavior.
- */
- if (backasswards || pread_len) {
- apr_ssize_t cntr = (apr_ssize_t)pread_len;
- if (backasswards) {
- /*@@@FIXME:
- * At this point in response processing of a 0.9 response,
- * we don't know yet whether data is binary or not.
- * mod_charset_lite will get control later on, so it cannot
- * decide on the conversion of this buffer full of data.
- * However, chances are that we are not really talking to an
- * HTTP/0.9 server, but to some different protocol, therefore
- * the best guess IMHO is to always treat the buffer as "text/x":
- */
- ap_xlate_proto_to_ascii(buffer, len);
- cntr = (apr_ssize_t)len;
- }
- e = apr_bucket_heap_create(buffer, cntr, NULL, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- }
/* PR 41646: get HEAD right with ProxyErrorOverride */
if (ap_is_HTTP_ERROR(r->status) && dconf->error_override) {
/* clear r->status for override error, otherwise ErrorDocument
@@ -1669,6 +1674,43 @@ apr_status_t ap_proxy_http_process_response(apr_po
return proxy_status;
}
+ r->sent_bodyct = 1;
+ /*
+ * Is it an HTTP/0.9 response or did we maybe preread the 1st line of
+ * the response? If so, load the extra data. These are 2 mutually
+ * exclusive possibilities, that just happen to require very
+ * similar behavior.
+ */
+ if (backasswards || pread_len) {
+ apr_ssize_t cntr = (apr_ssize_t)pread_len;
+ if (backasswards) {
+ /*@@@FIXME:
+ * At this point in response processing of a 0.9 response,
+ * we don't know yet whether data is binary or not.
+ * mod_charset_lite will get control later on, so it cannot
+ * decide on the conversion of this buffer full of data.
+ * However, chances are that we are not really talking to an
+ * HTTP/0.9 server, but to some different protocol, therefore
+ * the best guess IMHO is to always treat the buffer as "text/x":
+ */
+ ap_xlate_proto_to_ascii(buffer, len);
+ cntr = (apr_ssize_t)len;
+ }
+ e = apr_bucket_heap_create(buffer, cntr, NULL, c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pass_bb, e);
+ }
+
+ if (upgrade) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "proxy: tunneling protocol %s", upgrade);
+ if (!APR_BRIGADE_EMPTY(pass_bb)) {
+ ap_fflush(c->output_filters, pass_bb);
+ apr_brigade_cleanup(pass_bb);
+ }
+ ap_proxy_tunnel_request(backend->r->pool, r, backend);
+ return OK;
+ }
+
/* send body - but only if a body is expected */
if ((!r->header_only) && /* not HEAD request */
!interim_response && /* not any 1xx response */
Index: modules/proxy/mod_proxy_wstunnel.c
===================================================================
--- modules/proxy/mod_proxy_wstunnel.c (revision 1734109)
+++ modules/proxy/mod_proxy_wstunnel.c (working copy)
@@ -90,60 +90,6 @@ static int proxy_wstunnel_canon(request_rec *r, ch
}
-static apr_status_t proxy_wstunnel_transfer(request_rec *r, conn_rec *c_i, conn_rec *c_o,
- apr_bucket_brigade *bb, char *name, int *sent)
-{
- apr_status_t rv;
-#ifdef DEBUGGING
- apr_off_t len;
-#endif
-
- do {
- apr_brigade_cleanup(bb);
- rv = ap_get_brigade(c_i->input_filters, bb, AP_MODE_READBYTES,
- APR_NONBLOCK_READ, AP_IOBUFSIZE);
- if (rv == APR_SUCCESS) {
- if (c_o->aborted) {
- return APR_EPIPE;
- }
- if (APR_BRIGADE_EMPTY(bb)) {
- break;
- }
-#ifdef DEBUGGING
- len = -1;
- apr_brigade_length(bb, 0, &len);
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02440)
- "read %" APR_OFF_T_FMT
- " bytes from %s", len, name);
-#endif
- if (sent) {
- *sent = 1;
- }
- rv = ap_pass_brigade(c_o->output_filters, bb);
- if (rv == APR_SUCCESS) {
- ap_fflush(c_o->output_filters, bb);
- }
- else {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02441)
- "error on %s - ap_pass_brigade",
- name);
- }
- } else if (!APR_STATUS_IS_EAGAIN(rv) && !APR_STATUS_IS_EOF(rv)) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(02442)
- "error on %s - ap_get_brigade",
- name);
- }
- } while (rv == APR_SUCCESS);
-
- ap_log_rerror(APLOG_MARK, APLOG_TRACE2, rv, r, "wstunnel_transfer complete");
-
- if (APR_STATUS_IS_EAGAIN(rv)) {
- rv = APR_SUCCESS;
- }
-
- return rv;
-}
-
/*
* process the request and write the response.
*/
@@ -155,13 +101,7 @@ static int proxy_wstunnel_request(apr_pool_t *p, r
char *url, char *server_portstr)
{
apr_status_t rv;
- apr_pollset_t *pollset;
- apr_pollfd_t pollfd;
- const apr_pollfd_t *signalled;
- apr_int32_t pollcnt, pi;
- apr_int16_t pollevent;
conn_rec *c = r->connection;
- apr_socket_t *sock = conn->sock;
conn_rec *backconn = conn->connection;
char *buf;
apr_bucket_brigade *header_brigade;
@@ -168,9 +108,6 @@ static int proxy_wstunnel_request(apr_pool_t *p, r
apr_bucket *e;
char *old_cl_val = NULL;
char *old_te_val = NULL;
- apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc);
- apr_socket_t *client_socket = ap_get_conn_socket(c);
- int done = 0, replied = 0;
header_brigade = apr_brigade_create(p, backconn->bucket_alloc);
@@ -183,7 +120,9 @@ static int proxy_wstunnel_request(apr_pool_t *p, r
return rv;
}
- buf = apr_pstrdup(p, "Upgrade: WebSocket" CRLF "Connection: Upgrade" CRLF CRLF);
+ buf = apr_pstrdup(p, "Upgrade: WebSocket" CRLF
+ "Connection: Upgrade" CRLF
+ CRLF);
ap_xlate_proto_to_ascii(buf, strlen(buf));
e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(header_brigade, e);
@@ -192,117 +131,7 @@ static int proxy_wstunnel_request(apr_pool_t *p, r
header_brigade, 1)) != OK)
return rv;
- ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "setting up poll()");
-
- if ((rv = apr_pollset_create(&pollset, 2, p, 0)) != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02443)
- "error apr_pollset_create()");
- return HTTP_INTERNAL_SERVER_ERROR;
- }
-
-#if 0
- apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1);
- apr_socket_opt_set(sock, APR_SO_KEEPALIVE, 1);
- apr_socket_opt_set(client_socket, APR_SO_NONBLOCK, 1);
- apr_socket_opt_set(client_socket, APR_SO_KEEPALIVE, 1);
-#endif
-
- pollfd.p = p;
- pollfd.desc_type = APR_POLL_SOCKET;
- pollfd.reqevents = APR_POLLIN | APR_POLLHUP;
- pollfd.desc.s = sock;
- pollfd.client_data = NULL;
- apr_pollset_add(pollset, &pollfd);
-
- pollfd.desc.s = client_socket;
- apr_pollset_add(pollset, &pollfd);
-
- ap_remove_input_filter_byhandle(c->input_filters, "reqtimeout");
-
- r->output_filters = c->output_filters;
- r->proto_output_filters = c->output_filters;
- r->input_filters = c->input_filters;
- r->proto_input_filters = c->input_filters;
-
- /* This handler should take care of the entire connection; make it so that
- * nothing else is attempted on the connection after returning. */
- c->keepalive = AP_CONN_CLOSE;
-
- do { /* Loop until done (one side closes the connection, or an error) */
- rv = apr_pollset_poll(pollset, -1, &pollcnt, &signalled);
- if (rv != APR_SUCCESS) {
- if (APR_STATUS_IS_EINTR(rv)) {
- continue;
- }
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02444) "error apr_poll()");
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02445)
- "woke from poll(), i=%d", pollcnt);
-
- for (pi = 0; pi < pollcnt; pi++) {
- const apr_pollfd_t *cur = &signalled[pi];
-
- if (cur->desc.s == sock) {
- pollevent = cur->rtnevents;
- if (pollevent & (APR_POLLIN | APR_POLLHUP)) {
- ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02446)
- "sock was readable");
- done |= proxy_wstunnel_transfer(r, backconn, c, bb,
- "sock", NULL) != APR_SUCCESS;
- }
- else if (pollevent & APR_POLLERR) {
- ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02447)
- "error on backconn");
- backconn->aborted = 1;
- done = 1;
- }
- else {
- ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02605)
- "unknown event on backconn %d", pollevent);
- done = 1;
- }
- }
- else if (cur->desc.s == client_socket) {
- pollevent = cur->rtnevents;
- if (pollevent & (APR_POLLIN | APR_POLLHUP)) {
- ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02448)
- "client was readable");
- done |= proxy_wstunnel_transfer(r, c, backconn, bb,
- "client", &replied) != APR_SUCCESS;
- }
- else if (pollevent & APR_POLLERR) {
- ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02607)
- "error on client conn");
- c->aborted = 1;
- done = 1;
- }
- else {
- ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02606)
- "unknown event on client conn %d", pollevent);
- done = 1;
- }
- }
- else {
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02449)
- "unknown socket in pollset");
- done = 1;
- }
-
- }
- } while (!done);
-
- ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
- "finished with poll() - cleaning up");
-
- if (!replied) {
- return HTTP_BAD_GATEWAY;
- }
- else {
- return OK;
- }
-
- return OK;
+ return ap_proxy_tunnel_request(r->pool, r, conn);
}
/*
@@ -315,9 +144,7 @@ static int proxy_wstunnel_handler(request_rec *r,
int status;
char server_portstr[32];
proxy_conn_rec *backend = NULL;
- const char *upgrade;
- char *scheme;
- int retry;
+ const char *scheme, *upgrade;
conn_rec *c = r->connection;
apr_pool_t *p = r->pool;
apr_uri_t *uri;
@@ -339,7 +166,7 @@ static int proxy_wstunnel_handler(request_rec *r,
if (!upgrade || strcasecmp(upgrade, "WebSocket") != 0) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02900)
"declining URL %s (not WebSocket)", url);
- return DECLINED;
+ return HTTP_UPGRADE_REQUIRED;
}
uri = apr_palloc(p, sizeof(*uri));
@@ -357,11 +184,12 @@ static int proxy_wstunnel_handler(request_rec *r,
}
backend->is_ssl = is_ssl;
- backend->close = 0;
- retry = 0;
- while (retry < 2) {
+ ap_remove_input_filter_byhandle(c->input_filters, "reqtimeout");
+
+ do {
char *locurl = url;
+
/* Step One: Determine Who To Connect To */
status = ap_proxy_determine_connection(p, r, conf, worker, backend,
uri, &locurl, proxyname, proxyport,
@@ -379,6 +207,7 @@ static int proxy_wstunnel_handler(request_rec *r,
status = HTTP_SERVICE_UNAVAILABLE;
break;
}
+
/* Step Three: Create conn_rec */
if (!backend->connection) {
if ((status = ap_proxy_connection_create(scheme, backend,
@@ -386,14 +215,10 @@ static int proxy_wstunnel_handler(request_rec *r,
break;
}
- backend->close = 1; /* must be after ap_proxy_determine_connection */
-
-
/* Step Three: Process the Request */
status = proxy_wstunnel_request(p, r, backend, worker, conf, uri, locurl,
server_portstr);
- break;
- }
+ } while (0);
/* Do not close the socket */
ap_proxy_release_connection(scheme, backend, r->server);
Index: modules/proxy/proxy_util.c
===================================================================
--- modules/proxy/proxy_util.c (revision 1734109)
+++ modules/proxy/proxy_util.c (working copy)
@@ -3421,6 +3421,7 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_poo
if (do_100_continue) {
const char *val;
+#if 0
if (!r->expecting_100) {
/* Don't forward any "100 Continue" response if the client is
* not expecting it.
@@ -3428,6 +3429,7 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_poo
apr_table_setn(r->subprocess_env, "proxy-interim-response",
"Suppress");
}
+#endif
/* Add the Expect header if not already there. */
if (((val = apr_table_get(r->headers_in, "Expect")) == NULL)
@@ -3598,6 +3600,210 @@ PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucke
return OK;
}
+static int proxy_transfer(request_rec *r, conn_rec *c_i, conn_rec *c_o,
+ apr_bucket_brigade *bb, const char *name, int *sent)
+{
+ apr_status_t rv;
+ int passed = 0;
+
+ do {
+ apr_brigade_cleanup(bb);
+ rv = ap_get_brigade(c_i->input_filters, bb, AP_MODE_READBYTES,
+ APR_NONBLOCK_READ, AP_IOBUFSIZE);
+ if (rv == APR_SUCCESS) {
+ if (c_o->aborted) {
+ rv = APR_EPIPE;
+ break;
+ }
+ if (APR_BRIGADE_EMPTY(bb)) {
+ break;
+ }
+ rv = ap_pass_brigade(c_o->output_filters, bb);
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02441)
+ "proxy: transfer for %s failed on write", name);
+ }
+ passed = 1;
+ }
+ else if (!APR_STATUS_IS_EAGAIN(rv) && !APR_STATUS_IS_EOF(rv)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(02442)
+ "proxy: transfer for %s failed on read",
+ name);
+ }
+ } while (rv == APR_SUCCESS);
+
+ if (passed && !c_o->aborted) {
+ apr_status_t tmp;
+ apr_brigade_cleanup(bb);
+ tmp = ap_fflush(c_o->output_filters, bb);
+ if (rv == APR_SUCCESS) {
+ rv = tmp;
+ }
+ }
+ if (sent) {
+ *sent = passed;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, rv, r,
+ "proxy: transfer for %s complete", name);
+
+ if (APR_STATUS_IS_EAGAIN(rv)) {
+ rv = APR_SUCCESS;
+ }
+ return rv;
+}
+
+/*
+ * Forward anything from<=>to the client<=>backend until one end closes.
+ */
+PROXY_DECLARE(int) ap_proxy_tunnel_request(apr_pool_t *p, request_rec *r,
+ proxy_conn_rec *backend)
+{
+ apr_status_t rv;
+ apr_pollfd_t *pollfds;
+ apr_pollset_t *pollset;
+ const apr_pollfd_t *signalled;
+ apr_int32_t pollcnt, pi;
+ apr_int16_t pollevent;
+ conn_rec *c = r->connection;
+ conn_rec *backend_c = backend->connection;
+ apr_socket_t *backend_socket = backend->sock;
+ apr_socket_t *client_socket = ap_get_conn_socket(c);
+ apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc);
+ apr_interval_time_t timeout = -1, backend_timeout = -1;
+ int done = 0, replied = 0;
+
+ rv = apr_pollset_create(&pollset, 2, p, APR_POLLSET_NOCOPY);
+ if (rv != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02443)
+ "proxy: tunnel: can't create pollset");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+#if 0
+ apr_socket_opt_set(backend_socket, APR_SO_NONBLOCK, 1);
+ apr_socket_opt_set(backend_socket, APR_SO_KEEPALIVE, 1);
+ apr_socket_opt_set(client_socket, APR_SO_NONBLOCK, 1);
+ apr_socket_opt_set(client_socket, APR_SO_KEEPALIVE, 1);
+#endif
+
+ pollfds = apr_pcalloc(p, sizeof(apr_pollfd_t) * 2);
+ pollfds[0].p = pollfds[1].p = p;
+ pollfds[0].desc_type = pollfds[1].desc_type = APR_POLL_SOCKET;
+ pollfds[0].reqevents = pollfds[1].reqevents = APR_POLLIN | APR_POLLHUP;
+ pollfds[0].desc.s = client_socket;
+ pollfds[1].desc.s = backend_socket;
+ apr_pollset_add(pollset, &pollfds[0]);
+ apr_pollset_add(pollset, &pollfds[1]);
+
+ r->output_filters = c->output_filters;
+ r->proto_output_filters = c->output_filters;
+ r->input_filters = c->input_filters;
+ r->proto_input_filters = c->input_filters;
+
+ /* This loop will take of the entire connections; make it so that
+ * nothing else is attempted on them after returning.
+ */
+ c->keepalive = AP_CONN_CLOSE;
+ backend->close = 1;
+
+ apr_socket_timeout_get(client_socket, &timeout);
+ apr_socket_timeout_get(backend_socket, &backend_timeout);
+ if (timeout < 0) {
+ timeout = backend_timeout;
+ }
+ else if (timeout > backend_timeout) {
+ timeout = backend_timeout;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "proxy: tunnel: starting (timeout %" APR_TIME_T_FMT ")",
+ apr_time_sec(timeout));
+
+ do { /* Loop until done (one side closes the connection, or an error) */
+ rv = apr_pollset_poll(pollset, timeout, &pollcnt, &signalled);
+ if (rv != APR_SUCCESS) {
+ if (APR_STATUS_IS_EINTR(rv)) {
+ continue;
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02444)
+ "proxy: tunnel: polling failed");
+ break;
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, APLOGNO(02445)
+ "proxy: tunnel: woken up, i=%d", pollcnt);
+
+ for (pi = 0; pi < pollcnt; pi++) {
+ const apr_pollfd_t *cur = &signalled[pi];
+
+ if (cur->desc.s == backend_socket) {
+ pollevent = cur->rtnevents;
+ if (pollevent & (APR_POLLIN | APR_POLLHUP)) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, APLOGNO(02446)
+ "proxy: tunnel: backend was readable");
+ done |= (proxy_transfer(r, backend_c, c, bb, "backend",
+ NULL) != APR_SUCCESS);
+ }
+ else if (pollevent & APR_POLLERR) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02447)
+ "proxy: tunnel: error on backend connection");
+ backend_c->aborted = 1;
+ done = 1;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02605)
+ "proxy: tunnel: unknown event %d on backend connection",
+ (int)pollevent);
+ done = 1;
+ }
+ }
+ else if (cur->desc.s == client_socket) {
+ pollevent = cur->rtnevents;
+ if (pollevent & (APR_POLLIN | APR_POLLHUP)) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, APLOGNO(02448)
+ "proxy: tunnel: client was readable");
+ done |= (proxy_transfer(r, c, backend_c, bb, "client",
+ &replied) != APR_SUCCESS);
+ }
+ else if (pollevent & APR_POLLERR) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, APLOGNO(02607)
+ "proxy: tunnel: error on client connection");
+ c->aborted = 1;
+ done = 1;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02606)
+ "proxy: tunnel: unknown event %d on client connection",
+ (int)pollevent);
+ done = 1;
+ }
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02449)
+ "proxy: tunnel: unknown socket in pollset");
+ done = 1;
+ }
+ }
+ } while (!done);
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "proxy: tunnel: finished");
+
+ apr_pollset_remove(pollset, &pollfds[1]);
+ apr_pollset_remove(pollset, &pollfds[0]);
+ apr_pollset_destroy(pollset);
+ apr_brigade_destroy(bb);
+
+ if (rv) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ if (!replied) {
+ return HTTP_BAD_GATEWAY;
+ }
+
+ return OK;
+}
+
/* Fill in unknown schemes from apr_uri_port_of_scheme() */
typedef struct proxy_schemes_t {
Index: server/protocol.c
===================================================================
--- server/protocol.c (revision 1734109)
+++ server/protocol.c (working copy)
@@ -1765,23 +1765,25 @@ AP_DECLARE(void) ap_send_interim_response(request_
"Status is %d - not sending interim response", r->status);
return;
}
- if ((r->status == HTTP_CONTINUE) && !r->expecting_100) {
- /*
- * Don't send 100-Continue when there was no Expect: 100-continue
- * in the request headers. For origin servers this is a SHOULD NOT
- * for proxies it is a MUST NOT according to RFC 2616 8.2.3
+ if (r->status == HTTP_CONTINUE) {
+ if (!r->expecting_100) {
+ /*
+ * Don't send 100-Continue when there was no Expect: 100-continue
+ * in the request headers. For origin servers this is a SHOULD NOT
+ * for proxies it is a MUST NOT according to RFC 2616 8.2.3
+ */
+ return;
+ }
+
+ /* if we send an interim response, we're no longer in a state of
+ * expecting one. Also, this could feasibly be in a subrequest,
+ * so we need to propagate the fact that we responded.
*/
- return;
+ for (rr = r; rr != NULL; rr = rr->main) {
+ rr->expecting_100 = 0;
+ }
}
- /* if we send an interim response, we're no longer in a state of
- * expecting one. Also, this could feasibly be in a subrequest,
- * so we need to propagate the fact that we responded.
- */
- for (rr = r; rr != NULL; rr = rr->main) {
- rr->expecting_100 = 0;
- }
-
status_line = apr_pstrcat(r->pool, AP_SERVER_PROTOCOL, " ", r->status_line, CRLF, NULL);
ap_xlate_proto_to_ascii(status_line, strlen(status_line));
@@ -1791,7 +1793,9 @@ AP_DECLARE(void) ap_send_interim_response(request_
ap_fputs(x.f, x.bb, status_line);
if (send_headers) {
apr_table_do(send_header, &x, r->headers_out, NULL);
- apr_table_clear(r->headers_out);
+ if (send_headers != AP_SEND_INTERIM_KEEP_HEADERS) {
+ apr_table_clear(r->headers_out);
+ }
}
ap_fputs(x.f, x.bb, CRLF_ASCII);
ap_fflush(x.f, x.bb);