|
|
d8c8cd |
diff --git a/include/http_request.h b/include/http_request.h
|
|
|
d8c8cd |
index 8bfda78..cd093aa 100644
|
|
|
d8c8cd |
--- a/include/http_request.h
|
|
|
d8c8cd |
+++ b/include/http_request.h
|
|
|
d8c8cd |
@@ -337,6 +337,21 @@ void ap_process_async_request(request_rec *r);
|
|
|
d8c8cd |
*/
|
|
|
d8c8cd |
AP_DECLARE(void) ap_die(int type, request_rec *r);
|
|
|
d8c8cd |
|
|
|
d8c8cd |
+/**
|
|
|
d8c8cd |
+ * Check whether a connection is still established and has data available,
|
|
|
d8c8cd |
+ * optionnaly consuming blank lines ([CR]LF).
|
|
|
d8c8cd |
+ * @param c The current connection
|
|
|
d8c8cd |
+ * @param bb The brigade to filter
|
|
|
d8c8cd |
+ * @param max_blank_lines Max number of blank lines to consume, or zero
|
|
|
d8c8cd |
+ * to consider them as data (single read).
|
|
|
d8c8cd |
+ * @return APR_SUCCESS: connection established with data available,
|
|
|
d8c8cd |
+ * APR_EAGAIN: connection established and empty,
|
|
|
d8c8cd |
+ * APR_NOTFOUND: too much blank lines,
|
|
|
d8c8cd |
+ * APR_E*: connection/general error.
|
|
|
d8c8cd |
+ */
|
|
|
d8c8cd |
+AP_DECLARE(apr_status_t) ap_check_pipeline(conn_rec *c, apr_bucket_brigade *bb,
|
|
|
d8c8cd |
+ unsigned int max_blank_lines);
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
/* Hooks */
|
|
|
d8c8cd |
|
|
|
d8c8cd |
/**
|
|
|
d8c8cd |
diff --git a/include/httpd.h b/include/httpd.h
|
|
|
d8c8cd |
index a552358..d2a060e 100644
|
|
|
d8c8cd |
--- a/include/httpd.h
|
|
|
d8c8cd |
+++ b/include/httpd.h
|
|
|
d8c8cd |
@@ -200,6 +200,10 @@ extern "C" {
|
|
|
d8c8cd |
#ifndef DEFAULT_LIMIT_REQUEST_FIELDS
|
|
|
d8c8cd |
#define DEFAULT_LIMIT_REQUEST_FIELDS 100
|
|
|
d8c8cd |
#endif
|
|
|
d8c8cd |
+/** default/hard limit on number of leading/trailing empty lines */
|
|
|
d8c8cd |
+#ifndef DEFAULT_LIMIT_BLANK_LINES
|
|
|
d8c8cd |
+#define DEFAULT_LIMIT_BLANK_LINES 10
|
|
|
d8c8cd |
+#endif
|
|
|
d8c8cd |
|
|
|
d8c8cd |
/**
|
|
|
d8c8cd |
* The default default character set name to add if AddDefaultCharset is
|
|
|
d8c8cd |
diff --git a/modules/http/http_request.c b/modules/http/http_request.c
|
|
|
d8c8cd |
index 9885de4..c80dc78 100644
|
|
|
d8c8cd |
--- a/modules/http/http_request.c
|
|
|
d8c8cd |
+++ b/modules/http/http_request.c
|
|
|
d8c8cd |
@@ -238,25 +238,117 @@ AP_DECLARE(void) ap_die(int type, request_rec *r)
|
|
|
d8c8cd |
} \
|
|
|
d8c8cd |
} while(0)
|
|
|
d8c8cd |
|
|
|
d8c8cd |
-static void check_pipeline(conn_rec *c, apr_bucket_brigade *bb)
|
|
|
d8c8cd |
+AP_DECLARE(apr_status_t) ap_check_pipeline(conn_rec *c, apr_bucket_brigade *bb,
|
|
|
d8c8cd |
+ unsigned int max_blank_lines)
|
|
|
d8c8cd |
{
|
|
|
d8c8cd |
- if (c->keepalive != AP_CONN_CLOSE) {
|
|
|
d8c8cd |
- apr_status_t rv;
|
|
|
d8c8cd |
+ apr_status_t rv = APR_EOF;
|
|
|
d8c8cd |
+ ap_input_mode_t mode = AP_MODE_SPECULATIVE;
|
|
|
d8c8cd |
+ unsigned int num_blank_lines = 0;
|
|
|
d8c8cd |
+ apr_size_t cr = 0;
|
|
|
d8c8cd |
+ char buf[2];
|
|
|
d8c8cd |
|
|
|
d8c8cd |
- rv = ap_get_brigade(c->input_filters, bb, AP_MODE_SPECULATIVE,
|
|
|
d8c8cd |
- APR_NONBLOCK_READ, 1);
|
|
|
d8c8cd |
- if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb)) {
|
|
|
d8c8cd |
- /*
|
|
|
d8c8cd |
- * Error or empty brigade: There is no data present in the input
|
|
|
d8c8cd |
- * filter
|
|
|
d8c8cd |
- */
|
|
|
d8c8cd |
- c->data_in_input_filters = 0;
|
|
|
d8c8cd |
+ while (c->keepalive != AP_CONN_CLOSE && !c->aborted) {
|
|
|
d8c8cd |
+ apr_size_t len = cr + 1;
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+ apr_brigade_cleanup(bb);
|
|
|
d8c8cd |
+ rv = ap_get_brigade(c->input_filters, bb, mode,
|
|
|
d8c8cd |
+ APR_NONBLOCK_READ, len);
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+ if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb) || !max_blank_lines) {
|
|
|
d8c8cd |
+ if (mode == AP_MODE_READBYTES) {
|
|
|
d8c8cd |
+ /* Unexpected error, stop with this connection */
|
|
|
d8c8cd |
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(02967)
|
|
|
d8c8cd |
+ "Can't consume pipelined empty lines");
|
|
|
d8c8cd |
+ c->keepalive = AP_CONN_CLOSE;
|
|
|
d8c8cd |
+ rv = APR_EGENERAL;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb)) {
|
|
|
d8c8cd |
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
|
|
|
d8c8cd |
+ /* Pipe is dead */
|
|
|
d8c8cd |
+ c->keepalive = AP_CONN_CLOSE;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else {
|
|
|
d8c8cd |
+ /* Pipe is up and empty */
|
|
|
d8c8cd |
+ rv = APR_EAGAIN;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else {
|
|
|
d8c8cd |
+ apr_off_t n = 0;
|
|
|
d8c8cd |
+ /* Single read asked, (non-meta-)data available? */
|
|
|
d8c8cd |
+ rv = apr_brigade_length(bb, 0, &n);
|
|
|
d8c8cd |
+ if (rv == APR_SUCCESS && n <= 0) {
|
|
|
d8c8cd |
+ rv = APR_EAGAIN;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ break;
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
- else {
|
|
|
d8c8cd |
- c->data_in_input_filters = 1;
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+ /* Lookup and consume blank lines */
|
|
|
d8c8cd |
+ rv = apr_brigade_flatten(bb, buf, &len;;
|
|
|
d8c8cd |
+ if (rv != APR_SUCCESS || len != cr + 1) {
|
|
|
d8c8cd |
+ int log_level;
|
|
|
d8c8cd |
+ if (mode == AP_MODE_READBYTES) {
|
|
|
d8c8cd |
+ /* Unexpected error, stop with this connection */
|
|
|
d8c8cd |
+ c->keepalive = AP_CONN_CLOSE;
|
|
|
d8c8cd |
+ log_level = APLOG_ERR;
|
|
|
d8c8cd |
+ rv = APR_EGENERAL;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else {
|
|
|
d8c8cd |
+ /* Let outside (non-speculative/blocking) read determine
|
|
|
d8c8cd |
+ * where this possible failure comes from (metadata,
|
|
|
d8c8cd |
+ * morphed EOF socket => empty bucket? debug only here).
|
|
|
d8c8cd |
+ */
|
|
|
d8c8cd |
+ log_level = APLOG_DEBUG;
|
|
|
d8c8cd |
+ rv = APR_SUCCESS;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ ap_log_cerror(APLOG_MARK, log_level, rv, c, APLOGNO(02968)
|
|
|
d8c8cd |
+ "Can't check pipelined data");
|
|
|
d8c8cd |
+ break;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+ if (mode == AP_MODE_READBYTES) {
|
|
|
d8c8cd |
+ /* [CR]LF consumed, try next */
|
|
|
d8c8cd |
+ mode = AP_MODE_SPECULATIVE;
|
|
|
d8c8cd |
+ cr = 0;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else if (cr) {
|
|
|
d8c8cd |
+ AP_DEBUG_ASSERT(len == 2 && buf[0] == APR_ASCII_CR);
|
|
|
d8c8cd |
+ if (buf[1] == APR_ASCII_LF) {
|
|
|
d8c8cd |
+ /* consume this CRLF */
|
|
|
d8c8cd |
+ mode = AP_MODE_READBYTES;
|
|
|
d8c8cd |
+ num_blank_lines++;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else {
|
|
|
d8c8cd |
+ /* CR(?!LF) is data */
|
|
|
d8c8cd |
+ break;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else {
|
|
|
d8c8cd |
+ if (buf[0] == APR_ASCII_LF) {
|
|
|
d8c8cd |
+ /* consume this LF */
|
|
|
d8c8cd |
+ mode = AP_MODE_READBYTES;
|
|
|
d8c8cd |
+ num_blank_lines++;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else if (buf[0] == APR_ASCII_CR) {
|
|
|
d8c8cd |
+ cr = 1;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else {
|
|
|
d8c8cd |
+ /* Not [CR]LF, some data */
|
|
|
d8c8cd |
+ break;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+ if (num_blank_lines > max_blank_lines) {
|
|
|
d8c8cd |
+ /* Enough blank lines with this connection,
|
|
|
d8c8cd |
+ * stop and don't recycle it.
|
|
|
d8c8cd |
+ */
|
|
|
d8c8cd |
+ c->keepalive = AP_CONN_CLOSE;
|
|
|
d8c8cd |
+ rv = APR_NOTFOUND;
|
|
|
d8c8cd |
+ break;
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
- apr_brigade_cleanup(bb);
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+ return rv;
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
|
|
|
d8c8cd |
AP_DECLARE(void) ap_process_request_after_handler(request_rec *r)
|
|
|
d8c8cd |
@@ -264,6 +356,7 @@ AP_DECLARE(void) ap_process_request_after_handler(request_rec *r)
|
|
|
d8c8cd |
apr_bucket_brigade *bb;
|
|
|
d8c8cd |
apr_bucket *b;
|
|
|
d8c8cd |
conn_rec *c = r->connection;
|
|
|
d8c8cd |
+ apr_status_t rv;
|
|
|
d8c8cd |
|
|
|
d8c8cd |
/* Send an EOR bucket through the output filter chain. When
|
|
|
d8c8cd |
* this bucket is destroyed, the request will be logged and
|
|
|
d8c8cd |
@@ -284,7 +377,15 @@ AP_DECLARE(void) ap_process_request_after_handler(request_rec *r)
|
|
|
d8c8cd |
|
|
|
d8c8cd |
if (c->cs)
|
|
|
d8c8cd |
c->cs->state = CONN_STATE_WRITE_COMPLETION;
|
|
|
d8c8cd |
- check_pipeline(c, bb);
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+ /* Check pipeline consuming blank lines, they must not be interpreted as
|
|
|
d8c8cd |
+ * the next pipelined request, otherwise we would block on the next read
|
|
|
d8c8cd |
+ * without flushing data, and hence possibly delay pending response(s)
|
|
|
d8c8cd |
+ * until the next/real request comes in or the keepalive timeout expires.
|
|
|
d8c8cd |
+ */
|
|
|
d8c8cd |
+ rv = ap_check_pipeline(c, bb, DEFAULT_LIMIT_BLANK_LINES);
|
|
|
d8c8cd |
+ c->data_in_input_filters = (rv == APR_SUCCESS);
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
AP_PROCESS_REQUEST_RETURN((uintptr_t)r, r->uri, r->status);
|
|
|
d8c8cd |
if (ap_extended_status) {
|
|
|
d8c8cd |
ap_time_process_request(c->sbh, STOP_PREQUEST);
|
|
|
d8c8cd |
diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h
|
|
|
d8c8cd |
index f6f7069..dcc3dba 100644
|
|
|
d8c8cd |
--- a/modules/proxy/mod_proxy.h
|
|
|
d8c8cd |
+++ b/modules/proxy/mod_proxy.h
|
|
|
d8c8cd |
@@ -251,6 +251,10 @@ typedef struct {
|
|
|
d8c8cd |
* filter chain or not */
|
|
|
d8c8cd |
unsigned int inreslist:1; /* connection in apr_reslist? */
|
|
|
d8c8cd |
const char *uds_path; /* Unix domain socket path */
|
|
|
d8c8cd |
+ apr_bucket_brigade *tmp_bb;/* Temporary brigade created with the connection
|
|
|
d8c8cd |
+ * and its scpool/bucket_alloc (NULL before),
|
|
|
d8c8cd |
+ * must be left cleaned when used (locally).
|
|
|
d8c8cd |
+ */
|
|
|
d8c8cd |
} proxy_conn_rec;
|
|
|
d8c8cd |
|
|
|
d8c8cd |
typedef struct {
|
|
|
d8c8cd |
@@ -569,6 +573,7 @@ PROXY_DECLARE(int) ap_proxy_checkproxyblock2(request_rec *r, proxy_server_conf *
|
|
|
d8c8cd |
PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r);
|
|
|
d8c8cd |
/* DEPRECATED (will be replaced with ap_proxy_connect_backend */
|
|
|
d8c8cd |
PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **, const char *, apr_sockaddr_t *, const char *, proxy_server_conf *, request_rec *);
|
|
|
d8c8cd |
+/* DEPRECATED (will be replaced with ap_proxy_check_connection */
|
|
|
d8c8cd |
PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn,
|
|
|
d8c8cd |
request_rec *r);
|
|
|
d8c8cd |
PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c);
|
|
|
d8c8cd |
@@ -870,6 +875,28 @@ PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
|
|
|
d8c8cd |
PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
|
|
|
d8c8cd |
proxy_conn_rec *conn,
|
|
|
d8c8cd |
server_rec *s);
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+#define PROXY_CHECK_CONN_EMPTY (1 << 0)
|
|
|
d8c8cd |
+/**
|
|
|
d8c8cd |
+ * Check a connection to the backend
|
|
|
d8c8cd |
+ * @param scheme calling proxy scheme (http, ajp, ...)
|
|
|
d8c8cd |
+ * @param conn acquired connection
|
|
|
d8c8cd |
+ * @param server current server record
|
|
|
d8c8cd |
+ * @param max_blank_lines how many blank lines to consume,
|
|
|
d8c8cd |
+ * or zero for none (considered data)
|
|
|
d8c8cd |
+ * @param flags PROXY_CHECK_* bitmask
|
|
|
d8c8cd |
+ * @return APR_SUCCESS: connection established,
|
|
|
d8c8cd |
+ * APR_ENOTEMPTY: connection established with data,
|
|
|
d8c8cd |
+ * APR_ENOSOCKET: not connected,
|
|
|
d8c8cd |
+ * APR_EINVAL: worker in error state (unusable),
|
|
|
d8c8cd |
+ * other: connection closed/aborted (remotely)
|
|
|
d8c8cd |
+ */
|
|
|
d8c8cd |
+PROXY_DECLARE(apr_status_t) ap_proxy_check_connection(const char *scheme,
|
|
|
d8c8cd |
+ proxy_conn_rec *conn,
|
|
|
d8c8cd |
+ server_rec *server,
|
|
|
d8c8cd |
+ unsigned max_blank_lines,
|
|
|
d8c8cd |
+ int flags);
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
/**
|
|
|
d8c8cd |
* Make a connection to the backend
|
|
|
d8c8cd |
* @param proxy_function calling proxy scheme (http, ajp, ...)
|
|
|
d8c8cd |
diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c
|
|
|
d8c8cd |
index 81039bf..9b69a2e 100644
|
|
|
d8c8cd |
--- a/modules/proxy/mod_proxy_ajp.c
|
|
|
d8c8cd |
+++ b/modules/proxy/mod_proxy_ajp.c
|
|
|
d8c8cd |
@@ -753,7 +753,10 @@ static int proxy_ajp_handler(request_rec *r, proxy_worker *worker,
|
|
|
d8c8cd |
break;
|
|
|
d8c8cd |
|
|
|
d8c8cd |
/* Step Two: Make the Connection */
|
|
|
d8c8cd |
- if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) {
|
|
|
d8c8cd |
+ if (ap_proxy_check_connection(scheme, backend, r->server, 0,
|
|
|
d8c8cd |
+ PROXY_CHECK_CONN_EMPTY)
|
|
|
d8c8cd |
+ && ap_proxy_connect_backend(scheme, backend, worker,
|
|
|
d8c8cd |
+ r->server)) {
|
|
|
d8c8cd |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00896)
|
|
|
d8c8cd |
"failed to make connection to backend: %s",
|
|
|
d8c8cd |
backend->hostname);
|
|
|
d8c8cd |
diff --git a/modules/proxy/mod_proxy_fcgi.c b/modules/proxy/mod_proxy_fcgi.c
|
|
|
d8c8cd |
index 7889b0e..292b2c4 100644
|
|
|
d8c8cd |
--- a/modules/proxy/mod_proxy_fcgi.c
|
|
|
d8c8cd |
+++ b/modules/proxy/mod_proxy_fcgi.c
|
|
|
d8c8cd |
@@ -1007,7 +1007,10 @@ static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker,
|
|
|
d8c8cd |
backend->close = 1;
|
|
|
d8c8cd |
|
|
|
d8c8cd |
/* Step Two: Make the Connection */
|
|
|
d8c8cd |
- if (ap_proxy_connect_backend(FCGI_SCHEME, backend, worker, r->server)) {
|
|
|
d8c8cd |
+ if (ap_proxy_check_connection(FCGI_SCHEME, backend, r->server, 0,
|
|
|
d8c8cd |
+ PROXY_CHECK_CONN_EMPTY)
|
|
|
d8c8cd |
+ && ap_proxy_connect_backend(FCGI_SCHEME, backend, worker,
|
|
|
d8c8cd |
+ r->server)) {
|
|
|
d8c8cd |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01079)
|
|
|
d8c8cd |
"failed to make connection to backend: %s",
|
|
|
d8c8cd |
backend->hostname);
|
|
|
d8c8cd |
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
|
|
|
d8c8cd |
index bb5cdf9..6767c89 100644
|
|
|
d8c8cd |
--- a/modules/proxy/mod_proxy_http.c
|
|
|
d8c8cd |
+++ b/modules/proxy/mod_proxy_http.c
|
|
|
d8c8cd |
@@ -1228,7 +1228,7 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
|
|
|
d8c8cd |
const char *buf;
|
|
|
d8c8cd |
char keepchar;
|
|
|
d8c8cd |
apr_bucket *e;
|
|
|
d8c8cd |
- apr_bucket_brigade *bb, *tmp_bb;
|
|
|
d8c8cd |
+ apr_bucket_brigade *bb;
|
|
|
d8c8cd |
apr_bucket_brigade *pass_bb;
|
|
|
d8c8cd |
int len, backasswards;
|
|
|
d8c8cd |
int interim_response = 0; /* non-zero whilst interim 1xx responses
|
|
|
d8c8cd |
@@ -1284,16 +1284,17 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
|
|
|
d8c8cd |
backend->r->proxyreq = PROXYREQ_RESPONSE;
|
|
|
d8c8cd |
apr_table_setn(r->notes, "proxy-source-port", apr_psprintf(r->pool, "%hu",
|
|
|
d8c8cd |
origin->local_addr->port));
|
|
|
d8c8cd |
- tmp_bb = apr_brigade_create(p, c->bucket_alloc);
|
|
|
d8c8cd |
do {
|
|
|
d8c8cd |
apr_status_t rc;
|
|
|
d8c8cd |
|
|
|
d8c8cd |
apr_brigade_cleanup(bb);
|
|
|
d8c8cd |
|
|
|
d8c8cd |
- rc = ap_proxygetline(tmp_bb, buffer, sizeof(buffer), backend->r, 0, &len;;
|
|
|
d8c8cd |
+ rc = ap_proxygetline(backend->tmp_bb, buffer, sizeof(buffer),
|
|
|
d8c8cd |
+ backend->r, 0, &len;;
|
|
|
d8c8cd |
if (len == 0) {
|
|
|
d8c8cd |
/* handle one potential stray CRLF */
|
|
|
d8c8cd |
- rc = ap_proxygetline(tmp_bb, buffer, sizeof(buffer), backend->r, 0, &len;;
|
|
|
d8c8cd |
+ rc = ap_proxygetline(backend->tmp_bb, buffer, sizeof(buffer),
|
|
|
d8c8cd |
+ backend->r, 0, &len;;
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
if (len <= 0) {
|
|
|
d8c8cd |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(01102)
|
|
|
d8c8cd |
@@ -1944,13 +1945,8 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
|
|
|
d8c8cd |
worker, r->server)) != OK)
|
|
|
d8c8cd |
goto cleanup;
|
|
|
d8c8cd |
|
|
|
d8c8cd |
-
|
|
|
d8c8cd |
backend->is_ssl = is_ssl;
|
|
|
d8c8cd |
|
|
|
d8c8cd |
- if (is_ssl) {
|
|
|
d8c8cd |
- ap_proxy_ssl_connection_cleanup(backend, r);
|
|
|
d8c8cd |
- }
|
|
|
d8c8cd |
-
|
|
|
d8c8cd |
/*
|
|
|
d8c8cd |
* In the case that we are handling a reverse proxy connection and this
|
|
|
d8c8cd |
* is not a request that is coming over an already kept alive connection
|
|
|
d8c8cd |
@@ -1975,7 +1971,10 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
|
|
|
d8c8cd |
break;
|
|
|
d8c8cd |
|
|
|
d8c8cd |
/* Step Two: Make the Connection */
|
|
|
d8c8cd |
- if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) {
|
|
|
d8c8cd |
+ if (ap_proxy_check_connection(proxy_function, backend, r->server, 1,
|
|
|
d8c8cd |
+ PROXY_CHECK_CONN_EMPTY)
|
|
|
d8c8cd |
+ && ap_proxy_connect_backend(proxy_function, backend, worker,
|
|
|
d8c8cd |
+ r->server)) {
|
|
|
d8c8cd |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01114)
|
|
|
d8c8cd |
"HTTP: failed to make connection to backend: %s",
|
|
|
d8c8cd |
backend->hostname);
|
|
|
d8c8cd |
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
|
|
|
d8c8cd |
index e5862f8..4334001 100644
|
|
|
d8c8cd |
--- a/modules/proxy/proxy_util.c
|
|
|
d8c8cd |
+++ b/modules/proxy/proxy_util.c
|
|
|
d8c8cd |
@@ -1407,14 +1407,15 @@ static apr_status_t connection_cleanup(void *theconn)
|
|
|
d8c8cd |
static void socket_cleanup(proxy_conn_rec *conn)
|
|
|
d8c8cd |
{
|
|
|
d8c8cd |
conn->sock = NULL;
|
|
|
d8c8cd |
+ conn->tmp_bb = NULL;
|
|
|
d8c8cd |
conn->connection = NULL;
|
|
|
d8c8cd |
apr_pool_clear(conn->scpool);
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
|
|
|
d8c8cd |
+/* DEPRECATED */
|
|
|
d8c8cd |
PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn,
|
|
|
d8c8cd |
request_rec *r)
|
|
|
d8c8cd |
{
|
|
|
d8c8cd |
- apr_bucket_brigade *bb;
|
|
|
d8c8cd |
apr_status_t rv;
|
|
|
d8c8cd |
|
|
|
d8c8cd |
/*
|
|
|
d8c8cd |
@@ -1426,22 +1427,21 @@ PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn
|
|
|
d8c8cd |
* processed. We don't expect any data to be in the returned brigade.
|
|
|
d8c8cd |
*/
|
|
|
d8c8cd |
if (conn->sock && conn->connection) {
|
|
|
d8c8cd |
- bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
|
|
|
d8c8cd |
- rv = ap_get_brigade(conn->connection->input_filters, bb,
|
|
|
d8c8cd |
+ rv = ap_get_brigade(conn->connection->input_filters, conn->tmp_bb,
|
|
|
d8c8cd |
AP_MODE_READBYTES, APR_NONBLOCK_READ,
|
|
|
d8c8cd |
HUGE_STRING_LEN);
|
|
|
d8c8cd |
- if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) {
|
|
|
d8c8cd |
- socket_cleanup(conn);
|
|
|
d8c8cd |
- }
|
|
|
d8c8cd |
- if (!APR_BRIGADE_EMPTY(bb)) {
|
|
|
d8c8cd |
+ if (!APR_BRIGADE_EMPTY(conn->tmp_bb)) {
|
|
|
d8c8cd |
apr_off_t len;
|
|
|
d8c8cd |
|
|
|
d8c8cd |
- rv = apr_brigade_length(bb, 0, &len;;
|
|
|
d8c8cd |
+ rv = apr_brigade_length(conn->tmp_bb, 0, &len;;
|
|
|
d8c8cd |
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, rv, r,
|
|
|
d8c8cd |
"SSL cleanup brigade contained %"
|
|
|
d8c8cd |
APR_OFF_T_FMT " bytes of data.", len);
|
|
|
d8c8cd |
+ apr_brigade_cleanup(conn->tmp_bb);
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) {
|
|
|
d8c8cd |
+ socket_cleanup(conn);
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
- apr_brigade_destroy(bb);
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
return APR_SUCCESS;
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
@@ -2666,13 +2666,89 @@ static apr_status_t socket_connect_un(apr_socket_t *sock,
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
#endif
|
|
|
d8c8cd |
|
|
|
d8c8cd |
+PROXY_DECLARE(apr_status_t) ap_proxy_check_connection(const char *scheme,
|
|
|
d8c8cd |
+ proxy_conn_rec *conn,
|
|
|
d8c8cd |
+ server_rec *server,
|
|
|
d8c8cd |
+ unsigned max_blank_lines,
|
|
|
d8c8cd |
+ int flags)
|
|
|
d8c8cd |
+{
|
|
|
d8c8cd |
+ apr_status_t rv = APR_SUCCESS;
|
|
|
d8c8cd |
+ proxy_worker *worker = conn->worker;
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+ if (!PROXY_WORKER_IS_USABLE(worker)) {
|
|
|
d8c8cd |
+ /*
|
|
|
d8c8cd |
+ * The worker is in error likely done by a different thread / process
|
|
|
d8c8cd |
+ * e.g. for a timeout or bad status. We should respect this and should
|
|
|
d8c8cd |
+ * not continue with a connection via this worker even if we got one.
|
|
|
d8c8cd |
+ */
|
|
|
d8c8cd |
+ rv = APR_EINVAL;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else if (conn->connection) {
|
|
|
d8c8cd |
+ /* We have a conn_rec, check the full filter stack for things like
|
|
|
d8c8cd |
+ * SSL alert/shutdown, filters aside data...
|
|
|
d8c8cd |
+ */
|
|
|
d8c8cd |
+ rv = ap_check_pipeline(conn->connection, conn->tmp_bb,
|
|
|
d8c8cd |
+ max_blank_lines);
|
|
|
d8c8cd |
+ apr_brigade_cleanup(conn->tmp_bb);
|
|
|
d8c8cd |
+ if (rv == APR_SUCCESS) {
|
|
|
d8c8cd |
+ /* Some data available, the caller might not want them. */
|
|
|
d8c8cd |
+ if (flags & PROXY_CHECK_CONN_EMPTY) {
|
|
|
d8c8cd |
+ rv = APR_ENOTEMPTY;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else if (APR_STATUS_IS_EAGAIN(rv)) {
|
|
|
d8c8cd |
+ /* Filter chain is OK and empty, yet we can't determine from
|
|
|
d8c8cd |
+ * ap_check_pipeline (actually ap_core_input_filter) whether
|
|
|
d8c8cd |
+ * an empty non-blocking read is EAGAIN or EOF on the socket
|
|
|
d8c8cd |
+ * side (it's always SUCCESS), so check it explicitely here.
|
|
|
d8c8cd |
+ */
|
|
|
d8c8cd |
+ if (is_socket_connected(conn->sock)) {
|
|
|
d8c8cd |
+ rv = APR_SUCCESS;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else {
|
|
|
d8c8cd |
+ rv = APR_EPIPE;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else if (conn->sock) {
|
|
|
d8c8cd |
+ /* For modules working with sockets directly, check it. */
|
|
|
d8c8cd |
+ if (!is_socket_connected(conn->sock)) {
|
|
|
d8c8cd |
+ rv = APR_EPIPE;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else {
|
|
|
d8c8cd |
+ rv = APR_ENOSOCKET;
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+ if (rv == APR_SUCCESS) {
|
|
|
d8c8cd |
+ ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, server,
|
|
|
d8c8cd |
+ "%s: reusing backend connection %pI<>%pI",
|
|
|
d8c8cd |
+ scheme, conn->connection->local_addr,
|
|
|
d8c8cd |
+ conn->connection->client_addr);
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ else if (conn->sock) {
|
|
|
d8c8cd |
+ socket_cleanup(conn);
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+ if (rv != APR_ENOTEMPTY) {
|
|
|
d8c8cd |
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, APLOGNO(00951)
|
|
|
d8c8cd |
+ "%s: backend socket is disconnected.",
|
|
|
d8c8cd |
+ scheme);
|
|
|
d8c8cd |
+ } else {
|
|
|
d8c8cd |
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, server, APLOGNO(03408)
|
|
|
d8c8cd |
+ "%s: reusable backend connection is not empty: "
|
|
|
d8c8cd |
+ "forcibly closed", scheme);
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+ }
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+ return rv;
|
|
|
d8c8cd |
+}
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
|
|
|
d8c8cd |
proxy_conn_rec *conn,
|
|
|
d8c8cd |
proxy_worker *worker,
|
|
|
d8c8cd |
server_rec *s)
|
|
|
d8c8cd |
{
|
|
|
d8c8cd |
apr_status_t rv;
|
|
|
d8c8cd |
- int connected = 0;
|
|
|
d8c8cd |
int loglevel;
|
|
|
d8c8cd |
apr_sockaddr_t *backend_addr = conn->addr;
|
|
|
d8c8cd |
/* the local address to use for the outgoing connection */
|
|
|
d8c8cd |
@@ -2682,15 +2758,12 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
|
|
|
d8c8cd |
proxy_server_conf *conf =
|
|
|
d8c8cd |
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
|
|
|
d8c8cd |
|
|
|
d8c8cd |
- if (conn->sock) {
|
|
|
d8c8cd |
- if (!(connected = is_socket_connected(conn->sock))) {
|
|
|
d8c8cd |
- socket_cleanup(conn);
|
|
|
d8c8cd |
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00951)
|
|
|
d8c8cd |
- "%s: backend socket is disconnected.",
|
|
|
d8c8cd |
- proxy_function);
|
|
|
d8c8cd |
- }
|
|
|
d8c8cd |
+ rv = ap_proxy_check_connection(proxy_function, conn, s, 0, 0);
|
|
|
d8c8cd |
+ if (rv == APR_EINVAL) {
|
|
|
d8c8cd |
+ return DECLINED;
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
- while ((backend_addr || conn->uds_path) && !connected) {
|
|
|
d8c8cd |
+
|
|
|
d8c8cd |
+ while (rv != APR_SUCCESS && (backend_addr || conn->uds_path)) {
|
|
|
d8c8cd |
#if APR_HAVE_SYS_UN_H
|
|
|
d8c8cd |
if (conn->uds_path)
|
|
|
d8c8cd |
{
|
|
|
d8c8cd |
@@ -2867,8 +2940,6 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
-
|
|
|
d8c8cd |
- connected = 1;
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
|
|
|
d8c8cd |
if (PROXY_WORKER_IS_USABLE(worker)) {
|
|
|
d8c8cd |
@@ -2878,7 +2949,7 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
|
|
|
d8c8cd |
* Although some connections may be alive
|
|
|
d8c8cd |
* no further connections to the worker could be made
|
|
|
d8c8cd |
*/
|
|
|
d8c8cd |
- if (!connected) {
|
|
|
d8c8cd |
+ if (rv != APR_SUCCESS) {
|
|
|
d8c8cd |
if (!(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
|
|
|
d8c8cd |
worker->s->error_time = apr_time_now();
|
|
|
d8c8cd |
worker->s->status |= PROXY_WORKER_IN_ERROR;
|
|
|
d8c8cd |
@@ -2899,7 +2970,6 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
|
|
|
d8c8cd |
worker->s->error_time = 0;
|
|
|
d8c8cd |
worker->s->retries = 0;
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
- return connected ? OK : DECLINED;
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
else {
|
|
|
d8c8cd |
/*
|
|
|
d8c8cd |
@@ -2907,11 +2977,12 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
|
|
|
d8c8cd |
* e.g. for a timeout or bad status. We should respect this and should
|
|
|
d8c8cd |
* not continue with a connection via this worker even if we got one.
|
|
|
d8c8cd |
*/
|
|
|
d8c8cd |
- if (connected) {
|
|
|
d8c8cd |
+ if (rv == APR_SUCCESS) {
|
|
|
d8c8cd |
socket_cleanup(conn);
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
- return DECLINED;
|
|
|
d8c8cd |
+ rv = APR_EINVAL;
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
+ return rv == APR_SUCCESS ? OK : DECLINED;
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
|
|
|
d8c8cd |
static apr_status_t connection_shutdown(void *theconn)
|
|
|
d8c8cd |
@@ -2956,6 +3027,7 @@ PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
|
|
|
d8c8cd |
bucket_alloc = apr_bucket_alloc_create(conn->scpool);
|
|
|
d8c8cd |
+ conn->tmp_bb = apr_brigade_create(conn->scpool, bucket_alloc);
|
|
|
d8c8cd |
/*
|
|
|
d8c8cd |
* The socket is now open, create a new backend server connection
|
|
|
d8c8cd |
*/
|
|
|
d8c8cd |
diff --git a/server/protocol.c b/server/protocol.c
|
|
|
d8c8cd |
index a6aeb24..065efdf 100644
|
|
|
d8c8cd |
--- a/server/protocol.c
|
|
|
d8c8cd |
+++ b/server/protocol.c
|
|
|
d8c8cd |
@@ -594,14 +594,9 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
|
|
|
d8c8cd |
char *ll;
|
|
|
d8c8cd |
char *uri;
|
|
|
d8c8cd |
apr_size_t len;
|
|
|
d8c8cd |
- int num_blank_lines = 0;
|
|
|
d8c8cd |
core_server_config *conf = ap_get_core_module_config(r->server->module_config);
|
|
|
d8c8cd |
int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
|
|
|
d8c8cd |
- int max_blank_lines = r->server->limit_req_fields;
|
|
|
d8c8cd |
-
|
|
|
d8c8cd |
- if (max_blank_lines <= 0) {
|
|
|
d8c8cd |
- max_blank_lines = DEFAULT_LIMIT_REQUEST_FIELDS;
|
|
|
d8c8cd |
- }
|
|
|
d8c8cd |
+ int num_blank_lines = DEFAULT_LIMIT_BLANK_LINES;
|
|
|
d8c8cd |
|
|
|
d8c8cd |
/* Read past empty lines until we get a real request line,
|
|
|
d8c8cd |
* a read error, the connection closes (EOF), or we timeout.
|
|
|
d8c8cd |
@@ -648,7 +643,7 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
|
|
|
d8c8cd |
r->protocol = apr_pstrdup(r->pool, "HTTP/1.0");
|
|
|
d8c8cd |
return 0;
|
|
|
d8c8cd |
}
|
|
|
d8c8cd |
- } while ((len <= 0) && (++num_blank_lines < max_blank_lines));
|
|
|
d8c8cd |
+ } while ((len <= 0) && (--num_blank_lines >= 0));
|
|
|
d8c8cd |
|
|
|
d8c8cd |
if (APLOGrtrace5(r)) {
|
|
|
d8c8cd |
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r,
|