From: Brian Pane Date: Sun, 9 Oct 2005 04:49:52 +0000 (+0000) Subject: Refactored ap_process_request() so that in async MPMs, the MPM (and not X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e7d300d318df2ebcd23fa0c4bd5a0e96be4faf99;p=thirdparty%2Fapache%2Fhttpd.git Refactored ap_process_request() so that in async MPMs, the MPM (and not the httpd core) is responsible for write completion. Note that, with this commit, the Event MPM is still doing write completion synchronously. I'll add async write completion as a separate commit. Note also that this commit breaks the (experimental) Leader/Followers MPM on the (equally experimental) async-dev branch. The fix is to add either synchronous or asynchronous write completion to that MPM. Rather than hack in synchronous write completion, I'll revisit Leader/Followers once I have async working in Event. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/async-dev@307362 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 337c5ea8a5a..d2a71f7c07b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes in Apache 2.3.0 async-dev R&D branch + *) For asynchronous MPMs, moved responsibility for write completion from + the core to the MPM. [Brian Pane] + *) Added an End-Of-Request bucket type. The logging of a request and the freeing of its pool are now done when the EOR bucket is destroyed. This has the effect of delaying the logging until right after the last diff --git a/include/http_request.h b/include/http_request.h index b36e8266bd4..c76219bb43e 100644 --- a/include/http_request.h +++ b/include/http_request.h @@ -237,11 +237,20 @@ AP_DECLARE(void) ap_allow_standard_methods(request_rec *r, int reset, ...); #ifdef CORE_PRIVATE /** - * Process a top-level request from a client + * Process a top-level request from a client, and synchronously write + * the response to the client * @param r The current request */ void ap_process_request(request_rec *); +/** + * Process a top-level request from a client, allowing some or all of + * the response to remain buffered in the core output filter for later, + * asynchronous write completion + * @param r The current request + */ +void ap_process_async_request(request_rec *); + /** * Kill the current request * @param type Why the request is dieing diff --git a/modules/http/http_core.c b/modules/http/http_core.c index 968c35add76..1e943883603 100644 --- a/modules/http/http_core.c +++ b/modules/http/http_core.c @@ -130,19 +130,10 @@ static int ap_process_http_async_connection(conn_rec *c) if (ap_extended_status) ap_increment_counts(c->sbh, r); - if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted - || ap_graceful_stop_signalled()) { + if (cs->state != CONN_STATE_WRITE_COMPLETION) { + /* Something went wrong; close the connection */ cs->state = CONN_STATE_LINGER; } - else if (!c->data_in_input_filters) { - cs->state = CONN_STATE_CHECK_REQUEST_LINE_READABLE; - } - else { - /* else we are pipelining. Return to READ_REQUEST_LINE state - * and stay in the loop - */ - cs->state = CONN_STATE_READ_REQUEST_LINE; - } } else { /* ap_read_request failed - client may have closed */ cs->state = CONN_STATE_LINGER; diff --git a/modules/http/http_request.c b/modules/http/http_request.c index 27ccb313480..b8a3a2cc759 100644 --- a/modules/http/http_request.c +++ b/modules/http/http_request.c @@ -191,51 +191,29 @@ AP_DECLARE(void) ap_die(int type, request_rec *r) ap_send_error_response(r_1st_err, recursive_error); } -static void check_pipeline_flush(request_rec *r) +/* Check whether there's another request ready to be read + */ +static void check_pipeline(conn_rec *c) { - apr_bucket *e; - apr_bucket_brigade *bb; - conn_rec *c = r->connection; - /* ### if would be nice if we could PEEK without a brigade. that would - ### allow us to defer creation of the brigade to when we actually - ### need to send a FLUSH. */ - bb = apr_brigade_create(r->pool, c->bucket_alloc); - - /* Flush the filter contents if: - * - * 1) the connection will be closed - * 2) there isn't a request ready to be read - */ - /* ### shouldn't this read from the connection input filters? */ /* ### is zero correct? that means "read one line" */ - if (r->connection->keepalive != AP_CONN_CLOSE) { - if (ap_get_brigade(r->input_filters, bb, AP_MODE_EATCRLF, + if (c->keepalive != AP_CONN_CLOSE) { + apr_bucket_brigade *bb = apr_brigade_create(c->pool, c->bucket_alloc); + if (ap_get_brigade(c->input_filters, bb, AP_MODE_EATCRLF, APR_NONBLOCK_READ, 0) != APR_SUCCESS) { c->data_in_input_filters = 0; /* we got APR_EOF or an error */ } else { c->data_in_input_filters = 1; - return; /* don't flush */ } } - - e = apr_bucket_flush_create(c->bucket_alloc); - - /* We just send directly to the connection based filters. At - * this point, we know that we have seen all of the data - * (request finalization sent an EOS bucket, which empties all - * of the request filters). We just want to flush the buckets - * if something hasn't been sent to the network yet. - */ - APR_BRIGADE_INSERT_HEAD(bb, e); - ap_pass_brigade(r->connection->output_filters, bb); } -void ap_process_request(request_rec *r) +void ap_process_async_request(request_rec *r) { int access_status; apr_bucket_brigade *bb; apr_bucket *b; + conn_rec *c = r->connection; /* Give quick handlers a shot at serving the request on the fast * path, bypassing all of the other Apache hooks. @@ -281,18 +259,28 @@ void ap_process_request(request_rec *r) b = ap_bucket_eor_create(r->connection->bucket_alloc, r); APR_BRIGADE_INSERT_HEAD(bb, b); ap_pass_brigade(r->connection->output_filters, bb); + c->cs->state = CONN_STATE_WRITE_COMPLETION; + check_pipeline(c); +} - /* - * We want to flush the last packet if this isn't a pipelining connection - * *before* we start into logging. Suppose that the logging causes a DNS - * lookup to occur, which may have a high latency. If we hold off on - * this packet, then it'll appear like the link is stalled when really - * it's the application that's stalled. - */ - check_pipeline_flush(r); - ap_update_child_status(r->connection->sbh, SERVER_BUSY_LOG, r); - if (ap_extended_status) +void ap_process_request(request_rec *r) +{ + apr_bucket_brigade *bb; + apr_bucket *b; + conn_rec *c = r->connection; + + ap_process_async_request(r); + + if (!c->data_in_input_filters) { + bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + b = apr_bucket_flush_create(c->bucket_alloc); + APR_BRIGADE_INSERT_HEAD(bb, b); + ap_pass_brigade(r->connection->output_filters, bb); + ap_update_child_status(r->connection->sbh, SERVER_BUSY_LOG, r); + } + if (ap_extended_status) { ap_time_process_request(r->connection->sbh, STOP_PREQUEST); + } } static apr_table_t *rename_original_env(apr_pool_t *p, apr_table_t *t) diff --git a/server/mpm/experimental/event/event.c b/server/mpm/experimental/event/event.c index 72ba228b095..6bee16cb07f 100644 --- a/server/mpm/experimental/event/event.c +++ b/server/mpm/experimental/event/event.c @@ -636,6 +636,27 @@ static int process_socket(apr_pool_t * p, apr_socket_t * sock, cs->state = CONN_STATE_LINGER; } } + + if (cs->state == CONN_STATE_WRITE_COMPLETION) { + /* For now, do blocking writes in this thread to transfer the + * rest of the response. TODO: Hand off this connection to a + * pollset for asynchronous write completion. + */ + apr_bucket_brigade *bb = apr_brigade_create(c->pool, c->bucket_alloc); + apr_bucket *b = apr_bucket_flush_create(c->bucket_alloc); + APR_BRIGADE_INSERT_HEAD(bb, b); + ap_pass_brigade(c->output_filters, bb); + if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted || + ap_graceful_stop_signalled()) { + c->cs->state = CONN_STATE_LINGER; + } + else if (c->data_in_input_filters) { + cs->state = CONN_STATE_READ_REQUEST_LINE; + } + else { + cs->state = CONN_STATE_CHECK_REQUEST_LINE_READABLE; + } + } if (cs->state == CONN_STATE_LINGER) { ap_lingering_close(c);