--- modules/proxy/mod_proxy_fcgi.c +++ modules/proxy/mod_proxy_fcgi.c @@ -444,7 +444,7 @@ apr_uint16_t request_id, const char **err, int *bad_request, int *has_responded) { - apr_bucket_brigade *ib, *ob; + apr_bucket_brigade *ib, *ob, *cib; int seen_end_of_headers = 0, done = 0, ignore_body = 0; apr_status_t rv = APR_SUCCESS; int script_error_status = HTTP_OK; @@ -452,7 +452,7 @@ struct iovec vec[2]; ap_fcgi_header header; unsigned char farray[AP_FCGI_HEADER_LEN]; - apr_pollfd_t pfd; + apr_pollfd_t pfd[2]; int header_state = HDR_STATE_READING_HEADERS; char stack_iobuf[AP_IOBUFSIZE]; apr_size_t iobuf_size = AP_IOBUFSIZE; @@ -464,13 +464,21 @@ iobuf = apr_palloc(r->pool, iobuf_size); } - pfd.desc_type = APR_POLL_SOCKET; - pfd.desc.s = conn->sock; - pfd.p = r->pool; - pfd.reqevents = APR_POLLIN | APR_POLLOUT; + pfd[0].desc_type = APR_POLL_SOCKET; + pfd[0].desc.s = conn->sock; + pfd[0].p = r->pool; + pfd[0].reqevents = APR_POLLIN | APR_POLLOUT; + /* Specific pollfd to detect client connection aborts */ + pfd[1].desc_type = APR_POLL_SOCKET; + pfd[1].desc.s = (apr_socket_t*) ap_get_module_config(r->connection->conn_config, + &core_module); + pfd[1].p = r->connection->pool; + pfd[1].reqevents = APR_POLLIN; + ib = apr_brigade_create(r->pool, c->bucket_alloc); ob = apr_brigade_create(r->pool, c->bucket_alloc); + cib = apr_brigade_create(r->connection->pool, c->bucket_alloc); while (! done) { apr_interval_time_t timeout; @@ -481,7 +489,7 @@ * cause timeout errors. */ apr_socket_timeout_get(conn->sock, &timeout); - rv = apr_poll(&pfd, 1, &n, timeout); + rv = apr_poll(pfd, 2, &n, timeout); if (rv != APR_SUCCESS) { if (APR_STATUS_IS_EINTR(rv)) { continue; @@ -490,7 +498,7 @@ break; } - if (pfd.rtnevents & APR_POLLOUT) { + if (pfd[0].rtnevents & APR_POLLOUT) { apr_size_t to_send, writebuflen; int last_stdin = 0; char *iobuf_cursor; @@ -555,7 +563,7 @@ } if (last_stdin) { - pfd.reqevents = APR_POLLIN; /* Done with input data */ + pfd[0].reqevents = APR_POLLIN; /* Done with input data */ /* signal EOF (empty FCGI_STDIN) */ ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id, @@ -573,7 +581,26 @@ } } - if (pfd.rtnevents & APR_POLLIN) { + /* + * Client connection aborted before any attempt to flush + * data out has been made. This can happen for long running FCGI + * tasks. According to the FCGI specs, an FCGI_ABORT should be sent + * and the FCGI backend should respond with an END_REQUEST, but as of + * today (December 2016) there seems to be no FCGI backend implementation + * that really honor/care about this functionality. + */ + if (pfd[1].rtnevents & APR_POLLIN) { + rv = ap_get_brigade(r->connection->input_filters, cib, + AP_MODE_SPECULATIVE, APR_NONBLOCK_READ, 8); + if(rv == APR_EOF) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "EOF detected from the main client connection."); + r->connection->aborted = 1; + break; + } + } + + if (pfd[0].rtnevents & APR_POLLIN) { apr_size_t readbuflen; apr_uint16_t clen, rid; apr_bucket *b; @@ -802,6 +829,7 @@ apr_brigade_destroy(ib); apr_brigade_destroy(ob); + apr_brigade_destroy(cib); if (script_error_status != HTTP_OK) { ap_die(script_error_status, r); /* send ErrorDocument */