* dav_success_proppatch.
* 20160608.4 (2.5.0-dev) Add dav_acl_provider, dav_acl_provider_register
* dav_get_acl_providers.
- * 20160608.5 (2.5.0-dev) Add ap_proxy_check_backend(), and tmp_bb to struct
- * proxy_conn_rec.
+ * 20160608.5 (2.5.0-dev) Add ap_proxy_check_connection(), and tmp_bb to
+ * struct proxy_conn_rec.
* 20160608.6 (2.5.0-dev) Add ap_scan_http_field_content, ap_scan_http_token
* and ap_get_http_token
+ * 20160608.7 (2.5.0-dev) Add ap_check_pipeline().
*/
#define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
#ifndef MODULE_MAGIC_NUMBER_MAJOR
#define MODULE_MAGIC_NUMBER_MAJOR 20160608
#endif
-#define MODULE_MAGIC_NUMBER_MINOR 6 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 7 /* 0...n */
/**
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
*/
AP_DECLARE(void) ap_die(int type, request_rec *r);
+/**
+ * Check whether a connection is still established and has data available,
+ * optionnaly consuming blank lines ([CR]LF).
+ * @param c The current connection
+ * @param bb The brigade to filter
+ * @param max_blank_lines Max number of blank lines to consume, or zero
+ * to consider them as data (single read).
+ * @return APR_SUCCESS: connection established with data available,
+ * APR_EAGAIN: connection established and empty,
+ * APR_NOTFOUND: too much blank lines,
+ * APR_E*: connection/general error.
+ */
+AP_DECLARE(apr_status_t) ap_check_pipeline(conn_rec *c, apr_bucket_brigade *bb,
+ unsigned int max_blank_lines);
+
/* Hooks */
/**
ap_die_r(type, r, r->status);
}
-static void check_pipeline(conn_rec *c, apr_bucket_brigade *bb)
+AP_DECLARE(apr_status_t) ap_check_pipeline(conn_rec *c, apr_bucket_brigade *bb,
+ unsigned int max_blank_lines)
{
- apr_status_t rv;
- int num_blank_lines = DEFAULT_LIMIT_BLANK_LINES;
+ apr_status_t rv = APR_EOF;
ap_input_mode_t mode = AP_MODE_SPECULATIVE;
+ unsigned int num_blank_lines = 0;
apr_size_t cr = 0;
char buf[2];
- c->data_in_input_filters = 0;
while (c->keepalive != AP_CONN_CLOSE && !c->aborted) {
apr_size_t len = cr + 1;
apr_brigade_cleanup(bb);
rv = ap_get_brigade(c->input_filters, bb, mode,
APR_NONBLOCK_READ, len);
- if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb)) {
- /*
- * Error or empty brigade: There is no data present in the input
- * filter
- */
+ if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb) || !max_blank_lines) {
if (mode == AP_MODE_READBYTES) {
/* Unexpected error, stop with this connection */
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(02967)
"Can't consume pipelined empty lines");
c->keepalive = AP_CONN_CLOSE;
+ rv = APR_EGENERAL;
+ }
+ else if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb)) {
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
+ /* Pipe is dead */
+ c->keepalive = AP_CONN_CLOSE;
+ }
+ else {
+ /* Pipe is up and empty */
+ rv = APR_EAGAIN;
+ }
+ }
+ else {
+ apr_off_t n = 0;
+ /* Single read asked, (non-meta-)data available? */
+ rv = apr_brigade_length(bb, 0, &n);
+ if (rv == APR_SUCCESS && n <= 0) {
+ rv = APR_EAGAIN;
+ }
}
break;
}
- /* Ignore trailing blank lines (which must not be interpreted as
- * pipelined requests) up to the limit, otherwise we would block
- * on the next read without flushing data, and hence possibly delay
- * pending response(s) until the next/real request comes in or the
- * keepalive timeout expires.
- */
+ /* Lookup and consume blank lines */
rv = apr_brigade_flatten(bb, buf, &len);
if (rv != APR_SUCCESS || len != cr + 1) {
int log_level;
/* Unexpected error, stop with this connection */
c->keepalive = AP_CONN_CLOSE;
log_level = APLOG_ERR;
+ rv = APR_EGENERAL;
}
else {
/* Let outside (non-speculative/blocking) read determine
* where this possible failure comes from (metadata,
- * morphed EOF socket => empty bucket? debug only here).
+ * morphed EOF socket, ...). Debug only here.
*/
- c->data_in_input_filters = 1;
log_level = APLOG_DEBUG;
+ rv = APR_SUCCESS;
}
ap_log_cerror(APLOG_MARK, log_level, rv, c, APLOGNO(02968)
"Can't check pipelined data");
}
if (mode == AP_MODE_READBYTES) {
+ /* [CR]LF consumed, try next */
mode = AP_MODE_SPECULATIVE;
cr = 0;
}
else if (cr) {
AP_DEBUG_ASSERT(len == 2 && buf[0] == APR_ASCII_CR);
if (buf[1] == APR_ASCII_LF) {
+ /* consume this CRLF */
mode = AP_MODE_READBYTES;
- num_blank_lines--;
+ num_blank_lines++;
}
else {
- c->data_in_input_filters = 1;
+ /* CR(?!LF) is data */
break;
}
}
else {
if (buf[0] == APR_ASCII_LF) {
+ /* consume this LF */
mode = AP_MODE_READBYTES;
- num_blank_lines--;
+ num_blank_lines++;
}
else if (buf[0] == APR_ASCII_CR) {
cr = 1;
}
else {
- c->data_in_input_filters = 1;
+ /* Not [CR]LF, some data */
break;
}
}
- /* Enough blank lines with this connection?
- * Stop and don't recycle it.
- */
- if (num_blank_lines < 0) {
+ if (num_blank_lines > max_blank_lines) {
+ /* Enough blank lines with this connection,
+ * stop and don't recycle it.
+ */
c->keepalive = AP_CONN_CLOSE;
+ rv = APR_NOTFOUND;
+ break;
}
}
+
+ return rv;
}
apr_bucket_brigade *bb;
apr_bucket *b;
conn_rec *c = r->connection;
+ apr_status_t rv;
/* Send an EOR bucket through the output filter chain. When
* this bucket is destroyed, the request will be logged and
* already by the EOR bucket's cleanup function.
*/
- check_pipeline(c, bb);
+ /* Check pipeline consuming blank lines, they must not be interpreted as
+ * the next pipelined request, otherwise we would block on the next read
+ * without flushing data, and hence possibly delay pending response(s)
+ * until the next/real request comes in or the keepalive timeout expires.
+ */
+ rv = ap_check_pipeline(c, bb, DEFAULT_LIMIT_BLANK_LINES);
+ c->data_in_input_filters = (rv == APR_SUCCESS);
apr_brigade_destroy(bb);
+
if (c->cs)
c->cs->state = (c->aborted) ? CONN_STATE_LINGER
: CONN_STATE_WRITE_COMPLETION;
PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r);
/* DEPRECATED (will be replaced with ap_proxy_connect_backend */
PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **, const char *, apr_sockaddr_t *, const char *, proxy_server_conf *, request_rec *);
-/* DEPRECATED (will be replaced with ap_proxy_check_backend */
+/* DEPRECATED (will be replaced with ap_proxy_check_connection */
PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn,
request_rec *r);
PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c);
PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
proxy_conn_rec *conn,
server_rec *s);
+
+#define PROXY_CHECK_CONN_EMPTY (1 << 0)
/**
* Check a connection to the backend
- * @param proxy_function calling proxy scheme (http, ajp, ...)
- * @param conn acquired connection
- * @param s current server record
- * @param expect_empty whether to check for empty (no data available) or not
- * @return APR_SUCCESS or,
- * APR_ENOTSOCK: not connected,
- * APR_NOTFOUND: worker in error state (unusable),
- * APR_ENOTEMPTY: expect_empty set but the connection has data,
- * other: connection closed/aborted (remotely)
+ * @param scheme calling proxy scheme (http, ajp, ...)
+ * @param conn acquired connection
+ * @param server current server record
+ * @param max_blank_lines how many blank lines to consume,
+ * or zero for none (considered data)
+ * @param flags PROXY_CHECK_* bitmask
+ * @return APR_SUCCESS: connection established,
+ * APR_ENOTEMPTY: connection established with data,
+ * APR_ENOSOCKET: not connected,
+ * APR_EINVAL: worker in error state (unusable),
+ * other: connection closed/aborted (remotely)
*/
-PROXY_DECLARE(apr_status_t) ap_proxy_check_backend(const char *proxy_function,
- proxy_conn_rec *conn,
- server_rec *s,
- int expect_empty);
+PROXY_DECLARE(apr_status_t) ap_proxy_check_connection(const char *scheme,
+ proxy_conn_rec *conn,
+ server_rec *server,
+ unsigned max_blank_lines,
+ int flags);
/**
* Make a connection to the backend
break;
/* Step Two: Make the Connection */
- if (ap_proxy_check_backend(scheme, backend, r->server, 1) &&
- ap_proxy_connect_backend(scheme, backend, worker, r->server)) {
+ if (ap_proxy_check_connection(scheme, backend, r->server, 0,
+ PROXY_CHECK_CONN_EMPTY)
+ && ap_proxy_connect_backend(scheme, backend, worker,
+ r->server)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00896)
"failed to make connection to backend: %s",
backend->hostname);
}
/* Step Two: Make the Connection */
- if ((backend->close || ap_proxy_check_backend(FCGI_SCHEME, backend,
- r->server, 1)) &&
- ap_proxy_connect_backend(FCGI_SCHEME, backend, worker,
- r->server)) {
+ if (ap_proxy_check_connection(FCGI_SCHEME, backend, r->server, 0,
+ PROXY_CHECK_CONN_EMPTY)
+ && ap_proxy_connect_backend(FCGI_SCHEME, backend, worker,
+ r->server)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01079)
"failed to make connection to backend: %s",
backend->hostname);
}
/* Step Two: Make the Connection */
- if (ap_proxy_check_backend(proxy_function, backend, r->server, 1) &&
- ap_proxy_connect_backend(proxy_function, backend, worker,
- r->server)) {
+ if (ap_proxy_check_connection(proxy_function, backend, r->server, 1,
+ PROXY_CHECK_CONN_EMPTY)
+ && ap_proxy_connect_backend(proxy_function, backend, worker,
+ r->server)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01114)
"HTTP: failed to make connection to backend: %s",
backend->hostname);
#endif
}
-PROXY_DECLARE(apr_status_t) ap_proxy_check_backend(const char *proxy_function,
- proxy_conn_rec *conn,
- server_rec *s,
- int expect_empty)
+PROXY_DECLARE(apr_status_t) ap_proxy_check_connection(const char *scheme,
+ proxy_conn_rec *conn,
+ server_rec *server,
+ unsigned max_blank_lines,
+ int flags)
{
apr_status_t rv = APR_SUCCESS;
proxy_worker *worker = conn->worker;
* e.g. for a timeout or bad status. We should respect this and should
* not continue with a connection via this worker even if we got one.
*/
- rv = APR_NOTFOUND;
- }
- else if (!conn->sock) {
- rv = APR_ENOTSOCK;
+ rv = APR_EINVAL;
}
else if (conn->connection) {
- conn_rec *c = conn->connection;
- rv = ap_get_brigade(c->input_filters, conn->tmp_bb,
- AP_MODE_SPECULATIVE, APR_NONBLOCK_READ, 1);
- if (APR_STATUS_IS_EAGAIN(rv)) {
- rv = APR_SUCCESS;
- }
- else if (rv == APR_SUCCESS && expect_empty) {
- apr_off_t len = 0;
- apr_brigade_length(conn->tmp_bb, 0, &len);
- if (len) {
+ /* We have a conn_rec, check the full filter stack for things like
+ * SSL alert/shutdown, filters aside data...
+ */
+ rv = ap_check_pipeline(conn->connection, conn->tmp_bb,
+ max_blank_lines);
+ apr_brigade_cleanup(conn->tmp_bb);
+ if (rv == APR_SUCCESS) {
+ /* Some data available, the caller might not want them. */
+ if (flags & PROXY_CHECK_CONN_EMPTY) {
rv = APR_ENOTEMPTY;
}
}
- apr_brigade_cleanup(conn->tmp_bb);
+ else if (APR_STATUS_IS_EAGAIN(rv)) {
+ /* Filter chain is OK and empty, yet we can't determine from
+ * ap_check_pipeline (actually ap_core_input_filter) whether
+ * an empty non-blocking read is EAGAIN or EOF on the socket
+ * side (it's always SUCCESS), so check it explicitely here.
+ */
+ if (ap_proxy_is_socket_connected(conn->sock)) {
+ rv = APR_SUCCESS;
+ }
+ else {
+ rv = APR_EPIPE;
+ }
+ }
}
- else if (!ap_proxy_is_socket_connected(conn->sock)) {
- rv = APR_EPIPE;
+ else if (conn->sock) {
+ /* For modules working with sockets directly, check it. */
+ if (!ap_proxy_is_socket_connected(conn->sock)) {
+ rv = APR_EPIPE;
+ }
+ }
+ else {
+ rv = APR_ENOSOCKET;
}
- if (rv != APR_SUCCESS && conn->sock) {
+ if (rv == APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, server,
+ "%s: reusing backend connection %pI<>%pI",
+ scheme, conn->connection->local_addr,
+ conn->connection->client_addr);
+ }
+ else if (conn->sock) {
/* This clears conn->scpool (and associated data), so backup and
* restore any ssl_hostname for this connection set earlier by
* ap_proxy_determine_connection().
*/
char ssl_hostname[PROXY_WORKER_RFC1035_NAME_SIZE];
- if (rv == APR_NOTFOUND
+ if (rv == APR_EINVAL
|| !conn->ssl_hostname
|| PROXY_STRNCPY(ssl_hostname, conn->ssl_hostname)) {
ssl_hostname[0] = '\0';
socket_cleanup(conn);
if (rv != APR_ENOTEMPTY) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00951)
- "%s: backend socket is disconnected.",
- proxy_function);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, APLOGNO(00951)
+ "%s: backend socket is disconnected.", scheme);
}
else {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03408)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, server, APLOGNO(03408)
"%s: reusable backend connection is not empty: "
- "forcibly closed", proxy_function);
+ "forcibly closed", scheme);
}
if (ssl_hostname[0]) {
proxy_server_conf *conf =
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
- rv = ap_proxy_check_backend(proxy_function, conn, s, 0);
- if (rv == APR_NOTFOUND) {
+ rv = ap_proxy_check_connection(proxy_function, conn, s, 0, 0);
+ if (rv == APR_EINVAL) {
return DECLINED;
}
if (rv == APR_SUCCESS) {
socket_cleanup(conn);
}
- rv = APR_NOTFOUND;
+ rv = APR_EINVAL;
}
return rv == APR_SUCCESS ? OK : DECLINED;