Something I've been hacking on (in the pejorative sense of the word 'hack'. Look at the patch and you will see what I mean :-). This should apply and serve pages on Linux, though the event_loop is clearly broken as it does not timeout keep-alive connections and will hang on the apr_poll() (and hang the server) if a client leaves a keep-alive connection active but does not send anything on it. Scoreboard is broken, code structure is poor, yadda yadda. I plan to reimplement some of this more cleanly but no idea when I'll get around to it. Key points:
1. routines to read requests must be able to handl getting APR_EAGAIN (or APR_EWOULDBLOCK): 2. ap_process_http_connection reimplemented to be state driven 3. event loop in worker_mpm to wait for pending i/o Bill Index: include/http_protocol.h =================================================================== RCS file: /home/cvs/httpd-2.0/include/http_protocol.h,v retrieving revision 1.83 diff -u -r1.83 http_protocol.h --- include/http_protocol.h 11 Jul 2002 19:53:04 -0000 1.83 +++ include/http_protocol.h 15 Oct 2002 14:54:01 -0000 @@ -92,6 +92,13 @@ request_rec *ap_read_request(conn_rec *c); /** + * Read a request and fill in the fields. + * @param c The current connection + * @return The new request_rec + */ +request_rec *ap_create_request(conn_rec *c); + +/** * Read the mime-encoded headers. * @param r The current request */ @@ -103,8 +110,8 @@ * @param r The current request * @param bb temp brigade */ -AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, - apr_bucket_brigade *bb); +AP_DECLARE(apr_status_t) ap_get_mime_headers_core(request_rec *r, + http_state_t *hs); /* Finish up stuff after a request */ @@ -582,6 +589,7 @@ * @param r The request * @param fold Whether to merge continuation lines * @param bb Working brigade to use when reading buckets + * @param block block or non block * @return APR_SUCCESS, if successful * APR_ENOSPC, if the line is too big to fit in the buffer * Other errors where appropriate @@ -590,15 +598,17 @@ AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n, apr_size_t *read, request_rec *r, int fold, - apr_bucket_brigade *bb); + apr_bucket_brigade *bb, + apr_read_type_e block); #else /* ASCII box */ -#define ap_rgetline(s, n, read, r, fold, bb) \ - ap_rgetline_core((s), (n), (read), (r), (fold), (bb)) +#define ap_rgetline(s, n, read, r, fold, bb, block) \ + ap_rgetline_core((s), (n), (read), (r), (fold), (bb), (block)) #endif AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, apr_size_t *read, request_rec *r, int fold, - apr_bucket_brigade *bb); + apr_bucket_brigade *bb, + apr_read_type_e block); /** * Get the method number associated with the given string, assumed to Index: include/httpd.h =================================================================== RCS file: /home/cvs/httpd-2.0/include/httpd.h,v retrieving revision 1.189 diff -u -r1.189 httpd.h --- include/httpd.h 1 Jul 2002 17:49:53 -0000 1.189 +++ include/httpd.h 15 Oct 2002 14:54:03 -0000 @@ -684,6 +684,8 @@ /** A structure that represents the current request */ typedef struct request_rec request_rec; +typedef struct http_state_t http_state_t; + /* ### would be nice to not include this from httpd.h ... */ /* This comes after we have defined the request_rec type */ #include "apr_uri.h" @@ -1017,8 +1019,32 @@ void *sbh; /** The bucket allocator to use for all bucket/brigade creations */ struct apr_bucket_alloc_t *bucket_alloc; + + /* request rec */ + request_rec *r; + http_state_t *hs; }; +typedef enum { + HTTP_STATE_NEW_CONNECTION, + HTTP_STATE_READ_REQUEST_LINE, + HTTP_STATE_PARSE_REQUEST_LINE, + HTTP_STATE_READ_MIME_HEADERS, + HTTP_STATE_WRITE_RESPONSE, + HTTP_STATE_LINGER, + HTTP_STATE_DONE, + HTTP_STATE_ERROR +} http_state_e; + +struct http_state_t { + http_state_e state; + conn_rec *c; + request_rec *r; + apr_bucket_brigade *bb; + apr_table_t *headers; + apr_pool_t *p; + apr_socket_t *sock; +}; /* Per-vhost config... */ /** Index: modules/http/http_core.c =================================================================== RCS file: /home/cvs/httpd-2.0/modules/http/http_core.c,v retrieving revision 1.307 diff -u -r1.307 http_core.c --- modules/http/http_core.c 15 Jul 2002 08:05:11 -0000 1.307 +++ modules/http/http_core.c 15 Oct 2002 14:54:04 -0000 @@ -56,8 +56,10 @@ * University of Illinois, Urbana-Champaign. */ +#include "apr.h" #include "apr_strings.h" #include "apr_thread_proc.h" /* for RLIMIT stuff */ +#include "apr_lib.h" #define APR_WANT_STRFUNC #include "apr_want.h" @@ -76,6 +78,8 @@ #include "scoreboard.h" #include "mod_core.h" +#include "http_vhost.h" +#include "http_log.h" /* Handles for core filters */ AP_DECLARE_DATA ap_filter_rec_t *ap_http_input_filter_handle; @@ -281,7 +285,6 @@ * Read and process each request found on our connection * until no requests are left or we decide to close. */ - ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL); while ((r = ap_read_request(c)) != NULL) { @@ -313,7 +316,277 @@ return OK; } +static int ap_parse_request_line(request_rec *r) +{ + int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol */ + apr_size_t len = strlen(r->the_request); + const char *ll; + const char *uri; + const char *pro; + +#if 0 + if (r->status == HTTP_REQUEST_URI_TOO_LARGE) { + /* + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "request failed: URI too long"); + */ + ap_send_error_response(r, 0); + ap_run_log_transaction(r); + apr_brigade_destroy(hs->bb); + return DONE; + } +#endif + /* we've probably got something to do, ignore graceful restart requests */ + r->request_time = apr_time_now(); + ll = r->the_request; + r->method = ap_getword_white(r->pool, &ll); + + uri = ap_getword_white(r->pool, &ll); + + /* Provide quick information about the request method as soon as known */ + r->method_number = ap_method_number_of(r->method); + if (r->method_number == M_GET && r->method[0] == 'H') { + r->header_only = 1; + } + + ap_parse_uri(r, uri); + + /* ap_getline returns (size of max buffer - 1) if it fills up the + * buffer before finding the end-of-line. This is only going to + * happen if it exceeds the configured limit for a request-line. + * The cast is safe, limit_req_line cannot be negative + */ + if (len > (apr_size_t)r->server->limit_req_line) { + r->status = HTTP_REQUEST_URI_TOO_LARGE; + r->proto_num = HTTP_VERSION(1,0); + r->protocol = apr_pstrdup(r->pool, "HTTP/1.0"); + /* Need response here */ + + return DONE; + } + + if (ll[0]) { + r->assbackwards = 0; + pro = ll; + len = strlen(ll); + } else { + r->assbackwards = 1; + pro = "HTTP/0.9"; + len = 8; + } + r->protocol = apr_pstrmemdup(r->pool, pro, len); + + /* Avoid sscanf in the common case */ + if (len == 8 + && pro[0] == 'H' && pro[1] == 'T' && pro[2] == 'T' && pro[3] == 'P' + && pro[4] == '/' && apr_isdigit(pro[5]) && pro[6] == '.' + && apr_isdigit(pro[7])) { + r->proto_num = HTTP_VERSION(pro[5] - '0', pro[7] - '0'); + } + else if (2 == sscanf(r->protocol, "HTTP/%u.%u", &major, &minor) + && minor < HTTP_VERSION(1, 0)) /* don't allow HTTP/0.1000 */ + r->proto_num = HTTP_VERSION(major, minor); + else + r->proto_num = HTTP_VERSION(1, 0); + +#if 0 + if (r->assbackwards && r->header_only) { + /* + * Client asked for headers only with HTTP/0.9, which doesn't send + * headers! Have to dink things just to make sure the error message + * comes through... + */ + // ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + // "client sent invalid HTTP/0.9 request: HEAD %s", + // r->uri); + r->header_only = 0; + r->status = HTTP_BAD_REQUEST; + ap_send_error_response(r, 0); + ap_run_log_transaction(r); + apr_brigade_destroy(hs->bb); + hs->state = HTTP_STATE_ERROR; + + } +#endif + return OK; +} +static int ap_process_http_async_connection(conn_rec *c) +{ + int rc = OK; + request_rec *r; + int csd_set = 0; + apr_socket_t *csd = NULL; + http_state_t *hs = c->hs; + http_state_e next_state; + const char *expect; + int access_status; + int len; + + /* Every hook or call that does network i/o must handle and return + * APR_EWOULDBLOCK + */ + while (1) { + switch (hs->state) { + case HTTP_STATE_NEW_CONNECTION: + { + ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL); + r = ap_create_request(c); + hs->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + hs->headers = apr_table_make(r->pool, 50); + hs->r = r; + hs->state = HTTP_STATE_READ_REQUEST_LINE; + } + case HTTP_STATE_READ_REQUEST_LINE: + { + r = hs->r; + r->the_request = NULL; + do { + rc = ap_rgetline(&(r->the_request), DEFAULT_LIMIT_REQUEST_LINE + 2, + &len, r, 0, hs->bb, APR_NONBLOCK_READ); + if (rc == APR_EAGAIN) { + return OK; + } + if (rc != APR_SUCCESS || c->aborted) { + r->request_time = apr_time_now(); + hs->state = HTTP_STATE_ERROR; + return OK; + } + } while (len <= 0); + + hs->state = HTTP_STATE_PARSE_REQUEST_LINE; + } + case HTTP_STATE_PARSE_REQUEST_LINE: + { + rc = ap_parse_request_line(r); + if (rc != OK) { + hs->state = HTTP_STATE_ERROR; + /* todo: Log error message if appropriate */ + return OK; + } + hs->state = HTTP_STATE_READ_MIME_HEADERS; + } + case HTTP_STATE_READ_MIME_HEADERS: + { + r = hs->r; + /* This read will be a blocking read for now */ + rc = ap_get_mime_headers_core(r, hs); + if (rc == APR_EAGAIN) { + return OK; + } + else if (rc != APR_SUCCESS) { + hs->state = HTTP_STATE_LINGER; + return OK; + } + apr_brigade_destroy(hs->bb); + hs->bb = NULL; + r->status = HTTP_OK; /* Until further notice. */ + + /* update what we think the virtual host is based on the headers we've + * now read. may update status. + */ + ap_update_vhost_from_headers(r); + + /* we may have switched to another server */ + r->per_dir_config = r->server->lookup_defaults; + + if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1))) + || ((r->proto_num == HTTP_VERSION(1, 1)) + && !apr_table_get(r->headers_in, "Host"))) { + /* + * Client sent us an HTTP/1.1 or later request without telling us the + * hostname, either with a full URL or a Host: header. We therefore + * need to (as per the 1.1 spec) send an error. As a special case, + * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain + * a Host: header, and the server MUST respond with 400 if it doesn't. + */ + r->status = HTTP_BAD_REQUEST; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "client sent HTTP/1.1 request without hostname " + "(see RFC2616 section 14.23): %s", r->uri); + } + + if (r->status != HTTP_OK) { + ap_send_error_response(r, 0); + ap_run_log_transaction(r); + hs->state = HTTP_STATE_LINGER; + return OK; + } + + if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL) + && (expect[0] != '\0')) { + /* + * The Expect header field was added to HTTP/1.1 after RFC 2068 + * as a means to signal when a 100 response is desired and, + * unfortunately, to signal a poor man's mandatory extension that + * the server must understand or return 417 Expectation Failed. + */ + if (strcasecmp(expect, "100-continue") == 0) { + r->expecting_100 = 1; + } + else { + r->status = HTTP_EXPECTATION_FAILED; + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "client sent an unrecognized expectation value of " + "Expect: %s", expect); + ap_send_error_response(r, 0); + ap_run_log_transaction(r); + hs->state = HTTP_STATE_LINGER; + return OK; + } + } + + ap_add_input_filter_handle(ap_http_input_filter_handle, + NULL, r, r->connection); + + if ((rc = ap_run_post_read_request(r))) { + ap_die(access_status, r); + ap_run_log_transaction(r); + hs->state = HTTP_STATE_LINGER; + return OK; + } + + rc = OK; + hs->state = HTTP_STATE_WRITE_RESPONSE; + } + case HTTP_STATE_WRITE_RESPONSE: + { + r = hs->r; + c->keepalive = AP_CONN_UNKNOWN; + + ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, r); + + if (r->status == HTTP_OK) + ap_process_request(r); + + if (ap_extended_status) + ap_increment_counts(c->sbh, r); + + if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted) { + hs->state = HTTP_STATE_LINGER; + return OK; + } + + ap_update_child_status(c->sbh, SERVER_BUSY_KEEPALIVE, r); + apr_pool_destroy(r->pool); + + if (ap_graceful_stop_signalled()) { + hs->state = HTTP_STATE_LINGER; + break; + } + hs->state = HTTP_STATE_NEW_CONNECTION; + continue; + + break; + } + default: + hs->state = HTTP_STATE_LINGER; + return OK; + break; + } + } + return OK; +} static int http_create_request(request_rec *r) { if (!r->main && !r->prev) { @@ -330,7 +603,7 @@ static void register_hooks(apr_pool_t *p) { - ap_hook_process_connection(ap_process_http_connection,NULL,NULL, + ap_hook_process_connection(ap_process_http_async_connection,NULL,NULL, APR_HOOK_REALLY_LAST); ap_hook_map_to_storage(ap_send_http_trace,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_http_method(http_method,NULL,NULL,APR_HOOK_REALLY_LAST); Index: server/core.c =================================================================== RCS file: /home/cvs/httpd-2.0/server/core.c,v retrieving revision 1.201 diff -u -r1.201 core.c --- server/core.c 23 Aug 2002 18:05:37 -0000 1.201 +++ server/core.c 15 Oct 2002 14:54:08 -0000 @@ -3564,10 +3564,11 @@ * empty). We do this by returning whatever we have read. This may * or may not be bogus, but is consistent (for now) with EOF logic. */ + /* if (APR_STATUS_IS_EAGAIN(rv)) { rv = APR_SUCCESS; } - + */ return rv; } Index: server/protocol.c =================================================================== RCS file: /home/cvs/httpd-2.0/server/protocol.c,v retrieving revision 1.116 diff -u -r1.116 protocol.c --- server/protocol.c 28 Aug 2002 18:37:48 -0000 1.116 +++ server/protocol.c 15 Oct 2002 14:54:09 -0000 @@ -243,7 +243,8 @@ */ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, apr_size_t *read, request_rec *r, - int fold, apr_bucket_brigade *bb) + int fold, apr_bucket_brigade *bb, + apr_read_type_e block) { apr_status_t rv; apr_bucket *e; @@ -253,7 +254,7 @@ apr_brigade_cleanup(bb); rv = ap_get_brigade(r->input_filters, bb, AP_MODE_GETLINE, - APR_BLOCK_READ, 0); + block, 0); if (rv != APR_SUCCESS) { return rv; @@ -359,7 +360,7 @@ next_size = n - bytes_handled; - rv = ap_rgetline_core(&tmp, next_size, &next_len, r, fold, bb); + rv = ap_rgetline_core(&tmp, next_size, &next_len, r, fold, bb, APR_BLOCK_READ); if (rv != APR_SUCCESS) { return rv; @@ -494,7 +495,7 @@ next_size = n - bytes_handled; - rv = ap_rgetline_core(&tmp, next_size, &next_len, r, fold, bb); + rv = ap_rgetline_core(&tmp, next_size, &next_len, r, fold, bb, APR_BLOCK_READ); if (rv != APR_SUCCESS) { return rv; @@ -531,7 +532,7 @@ #if APR_CHARSET_EBCDIC AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n, apr_size_t *read, request_rec *r, - int fold, apr_bucket_brigade *bb) + int fold, apr_bucket_brigade *bb, apr_read_type_e block) { /* on ASCII boxes, ap_rgetline is a macro which simply invokes * ap_rgetline_core with the same parms @@ -543,7 +544,7 @@ */ apr_status_t rv; - rv = ap_rgetline_core(s, n, read, r, fold, bb); + rv = ap_rgetline_core(s, n, read, r, fold, bb, block); if (rv == APR_SUCCESS) { ap_xlate_proto_from_ascii(*s, *read); } @@ -559,7 +560,7 @@ apr_bucket_brigade *tmp_bb; tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); - rv = ap_rgetline(&tmp_s, n, &len, r, fold, tmp_bb); + rv = ap_rgetline(&tmp_s, n, &len, r, fold, tmp_bb, APR_BLOCK_READ); apr_brigade_destroy(tmp_bb); /* Map the out-of-space condition to the old API. */ @@ -667,11 +668,11 @@ */ r->the_request = NULL; rv = ap_rgetline(&(r->the_request), DEFAULT_LIMIT_REQUEST_LINE + 2, - &len, r, 0, bb); + &len, r, 0, bb, APR_NONBLOCK_READ); if (rv != APR_SUCCESS) { r->request_time = apr_time_now(); - return 0; + return rv; } } while (len <= 0); @@ -741,7 +742,7 @@ return 1; } -AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb) +AP_DECLARE(apr_status_t) ap_get_mime_headers_core(request_rec *r, http_state_t *hs) { char *last_field = NULL; apr_size_t last_len = 0; @@ -750,11 +751,9 @@ char *value; apr_size_t len; int fields_read = 0; - apr_table_t *tmp_headers; - - /* We'll use apr_table_overlap later to merge these into r->headers_in. */ - tmp_headers = apr_table_make(r->pool, 50); - + apr_bucket_brigade *bb = hs->bb; + apr_table_t *tmp_headers = hs->headers; + /* * Read header lines until we get the empty separator line, a read error, * the connection closes (EOF), reach the server limit, or we timeout. @@ -765,7 +764,10 @@ field = NULL; rv = ap_rgetline(&field, DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2, - &len, r, 0, bb); + &len, r, 0, bb, APR_BLOCK_READ); + + if (APR_STATUS_IS_EAGAIN(rv)) + return rv; /* ap_rgetline returns APR_ENOSPC if it fills up the buffer before * finding the end-of-line. This is only going to happen if it @@ -783,12 +785,12 @@ "<pre>\n", ap_escape_html(r->pool, field), "</pre>\n", NULL)); - return; + return rv; } if (rv != APR_SUCCESS) { r->status = HTTP_BAD_REQUEST; - return; + return rv; } if (last_field != NULL) { @@ -821,7 +823,7 @@ apr_table_setn(r->notes, "error-notes", "The number of request header fields " "exceeds this server's limit."); - return; + return rv; } if (!(value = strchr(last_field, ':'))) { /* Find ':' or */ @@ -834,7 +836,7 @@ ap_escape_html(r->pool, last_field), "</pre>\n", NULL)); - return; + return rv; } *value = '\0'; @@ -871,6 +873,7 @@ } apr_table_overlap(r->headers_in, tmp_headers, APR_OVERLAP_TABLES_MERGE); + return APR_SUCCESS; } AP_DECLARE(void) ap_get_mime_headers(request_rec *r) @@ -881,6 +884,53 @@ apr_brigade_destroy(tmp_bb); } +request_rec *ap_create_request(conn_rec *conn) +{ + request_rec *r; + apr_pool_t *p; + const char *expect; + int access_status; + + apr_pool_create(&p, conn->pool); + r = apr_pcalloc(p, sizeof(request_rec)); + + conn->r = r; + + r->pool = p; + r->connection = conn; + r->server = conn->base_server; + + r->user = NULL; + r->ap_auth_type = NULL; + + r->allowed_methods = ap_make_method_list(p, 2); + + r->headers_in = apr_table_make(r->pool, 25); + r->subprocess_env = apr_table_make(r->pool, 25); + r->headers_out = apr_table_make(r->pool, 12); + r->err_headers_out = apr_table_make(r->pool, 5); + r->notes = apr_table_make(r->pool, 5); + + r->request_config = ap_create_request_config(r->pool); + /* Must be set before we run create request hook */ + + r->proto_output_filters = conn->output_filters; + r->output_filters = r->proto_output_filters; + r->proto_input_filters = conn->input_filters; + r->input_filters = r->proto_input_filters; + ap_run_create_request(r); + r->per_dir_config = r->server->lookup_defaults; + + r->sent_bodyct = 0; /* bytect isn't for body */ + + r->read_length = 0; + r->read_body = REQUEST_NO_BODY; + + r->status = HTTP_REQUEST_TIME_OUT; /* Until we get a request */ + r->the_request = NULL; + return r; +} + request_rec *ap_read_request(conn_rec *conn) { request_rec *r; @@ -891,6 +941,9 @@ apr_pool_create(&p, conn->pool); r = apr_pcalloc(p, sizeof(request_rec)); + + conn->r = r; + r->pool = p; r->connection = conn; r->server = conn->base_server; Index: server/mpm/worker/fdqueue.c =================================================================== RCS file: /home/cvs/httpd-2.0/server/mpm/worker/fdqueue.c,v retrieving revision 1.23 diff -u -r1.23 fdqueue.c --- server/mpm/worker/fdqueue.c 2 Aug 2002 17:37:52 -0000 1.23 +++ server/mpm/worker/fdqueue.c 15 Oct 2002 14:54:10 -0000 @@ -253,7 +253,7 @@ * the push operation has completed, it signals other threads waiting * in ap_queue_pop() that they may continue consuming sockets. */ -apr_status_t ap_queue_push(fd_queue_t *queue, apr_socket_t *sd, apr_pool_t *p) +apr_status_t ap_queue_push(fd_queue_t *queue, apr_socket_t *sd, http_state_t *hs, apr_pool_t *p) { fd_queue_elem_t *elem; apr_status_t rv; @@ -267,6 +267,7 @@ elem = &queue->data[queue->nelts]; elem->sd = sd; + elem->hs = hs; elem->p = p; queue->nelts++; @@ -285,7 +286,7 @@ * Once retrieved, the socket is placed into the address specified by * 'sd'. */ -apr_status_t ap_queue_pop(fd_queue_t *queue, apr_socket_t **sd, apr_pool_t **p) +apr_status_t ap_queue_pop(fd_queue_t *queue, apr_socket_t **sd, http_state_t **hs, apr_pool_t **p) { fd_queue_elem_t *elem; apr_status_t rv; @@ -316,6 +317,7 @@ elem = &queue->data[--queue->nelts]; *sd = elem->sd; + *hs = elem->hs; *p = elem->p; #ifdef AP_DEBUG elem->sd = NULL; Index: server/mpm/worker/fdqueue.h =================================================================== RCS file: /home/cvs/httpd-2.0/server/mpm/worker/fdqueue.h,v retrieving revision 1.19 diff -u -r1.19 fdqueue.h --- server/mpm/worker/fdqueue.h 28 Apr 2002 23:12:35 -0000 1.19 +++ server/mpm/worker/fdqueue.h 15 Oct 2002 14:54:10 -0000 @@ -84,6 +84,7 @@ struct fd_queue_elem_t { apr_socket_t *sd; apr_pool_t *p; + http_state_t *hs; }; typedef struct fd_queue_elem_t fd_queue_elem_t; @@ -98,8 +99,8 @@ typedef struct fd_queue_t fd_queue_t; apr_status_t ap_queue_init(fd_queue_t *queue, int queue_capacity, apr_pool_t *a); -apr_status_t ap_queue_push(fd_queue_t *queue, apr_socket_t *sd, apr_pool_t *p); -apr_status_t ap_queue_pop(fd_queue_t *queue, apr_socket_t **sd, apr_pool_t **p); +apr_status_t ap_queue_push(fd_queue_t *queue, apr_socket_t *sd, http_state_t *hs, apr_pool_t *p); +apr_status_t ap_queue_pop(fd_queue_t *queue, apr_socket_t **sd, http_state_t **hs, apr_pool_t **p); apr_status_t ap_queue_interrupt_all(fd_queue_t *queue); apr_status_t ap_queue_term(fd_queue_t *queue); Index: server/mpm/worker/worker.c =================================================================== RCS file: /home/cvs/httpd-2.0/server/mpm/worker/worker.c,v retrieving revision 1.133 diff -u -r1.133 worker.c --- server/mpm/worker/worker.c 28 Aug 2002 18:48:07 -0000 1.133 +++ server/mpm/worker/worker.c 15 Oct 2002 14:54:14 -0000 @@ -111,6 +111,7 @@ #include <signal.h> #include <limits.h> /* for INT_MAX */ +#include "apr_queue.h" /* Limit on the total --- clients will be locked out if more servers than * this are needed. It is intended solely to keep the server from crashing @@ -175,6 +176,12 @@ static int resource_shortage = 0; static fd_queue_t *worker_queue; static fd_queue_info_t *worker_queue_info; +#if 1 +static apr_socket_t *ev_sock; +#else +static apr_queue_t *event_queue; +#endif + /* The structure used to pass unique initialization info to each thread */ typedef struct { @@ -605,7 +612,7 @@ * Child process main loop. */ -static void process_socket(apr_pool_t *p, apr_socket_t *sock, int my_child_num, +static int process_socket(apr_pool_t *p, apr_socket_t *sock, http_state_t *hs, int my_child_num, int my_thread_num, apr_bucket_alloc_t *bucket_alloc) { conn_rec *current_conn; @@ -623,14 +630,49 @@ "(currently %d)", csd, FD_SETSIZE); apr_socket_close(sock); - return; + return 0; } + if (hs == NULL) { /* This is equivalent to HTTP_STATE_NEW_CONNECTION */ - current_conn = ap_run_create_connection(p, ap_server_conf, sock, - conn_id, sbh, bucket_alloc); + current_conn = ap_run_create_connection(p, ap_server_conf, sock, + conn_id, sbh, bucket_alloc); + hs = apr_pcalloc(p, sizeof(http_state_t)); + hs->state = HTTP_STATE_NEW_CONNECTION; + hs->c = current_conn; + hs->p = p; + hs->sock = sock; + current_conn->hs = hs; + + ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, + "process_connection: hs = %x, hs->p = %x", hs, hs->p ); + + } + else { + current_conn = hs->c; + } if (current_conn) { + hs = current_conn->hs; ap_process_connection(current_conn, sock); - ap_lingering_close(current_conn); + switch (hs->state) { + case HTTP_STATE_READ_REQUEST_LINE: + case HTTP_STATE_READ_MIME_HEADERS: + { + apr_status_t rc; + ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, + "Waiting for IO Event: hs = %x, hs->state = %d", hs, hs->state); + do { + rc = apr_queue_push(event_queue, hs); + } while (rc == APR_EINTR); + return 1; + } + default: + { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, + "Closing connection: hs = %x", hs); + ap_lingering_close(current_conn); + return 0; + } + } } } @@ -831,7 +873,7 @@ signal_threads(ST_GRACEFUL); } if (csd != NULL) { - rv = ap_queue_push(worker_queue, csd, ptrans); + rv = ap_queue_push(worker_queue, csd, NULL, ptrans); if (rv) { /* trash the connection; we couldn't queue the connected * socket to a worker @@ -881,6 +923,7 @@ int process_slot = ti->pid; int thread_slot = ti->tid; apr_socket_t *csd = NULL; + http_state_t *hs; apr_bucket_alloc_t *bucket_alloc; apr_pool_t *last_ptrans = NULL; apr_pool_t *ptrans; /* Pool for per-transaction stuff */ @@ -912,7 +955,10 @@ if (workers_may_exit) { break; } - rv = ap_queue_pop(worker_queue, &csd, &ptrans); + rv = ap_queue_pop(worker_queue, &csd, &hs, &ptrans); + + ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, + "ap_queue_pop: hs = %x, ptrans = %x", hs, ptrans); if (rv != APR_SUCCESS) { /* We get APR_EOF during a graceful shutdown once all the connections @@ -944,11 +990,17 @@ } is_idle = 0; worker_sockets[thread_slot] = csd; - process_socket(ptrans, csd, process_slot, thread_slot, bucket_alloc); + rv = process_socket(ptrans, csd, hs, process_slot, thread_slot, bucket_alloc); + if (rv) { + /* io event pending */ + last_ptrans = NULL; + } + else { + requests_this_child--; /* FIXME: should be synchronized - aaron */ + apr_pool_clear(ptrans); + last_ptrans = ptrans; + } worker_sockets[thread_slot] = NULL; - requests_this_child--; /* FIXME: should be synchronized - aaron */ - apr_pool_clear(ptrans); - last_ptrans = ptrans; } ap_update_child_status_from_indexes(process_slot, thread_slot, @@ -969,7 +1021,126 @@ } return 0; } +#if 0 +static void *event_loop(apr_thread_t *thd, void * dummy) +{ + apr_pollset_t *pollset; + apr_status_t rc; + apr_pollfd_t *pollfd; + apr_interval_time_t timeout = 15000000; + apr_int32_t num; + http_state_t *hs; + /* Create the pollset */ + rc = apr_pollset_create(&pollset, 100, pchild, 0); + + while (1) { + void *v; + /* Get work */ + do { + rc = apr_queue_pop(event_queue, &v); + } while (rc == APR_EINTR); + hs = *(http_state_t**)v; + + /* Add work to pollset. These are always read events */ + apr_poll_setup(&pollfd, 1, hs->p); + apr_poll_socket_add(pollfd, hs->sock, APR_POLLIN); + pollfd->client_data = (void*) hs; + apr_pollset_add(pollset, pollfd); + rc = apr_pollset_poll(pollset, timeout, &num, &pollfd); + + while (num) { + rc = apr_pollset_remove(pollset, pollfd); + hs = (http_state_t*) pollfd->client_data; + ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, + "IO Event Received: hs = %x, hs->state = %d", hs, hs->state); + rc = ap_queue_push(worker_queue, + pollfd->desc.s, + hs, + hs->p); + pollfd++; + num--; + } + /* Do timeouts */ + } +} +#else +static void *event_loop(apr_thread_t *thd, void * dummy) +{ + apr_pollset_t *pollset; + apr_status_t rc; + apr_pollfd_t *pollfd; + apr_interval_time_t timeout = 15000000; + apr_int32_t num; + http_state_t *hs; + + /* Create the pollset */ + rc = apr_pollset_create(&pollset, 100, pchild, 0); + + /* Add the event_loop pipe to the pollset */ + apr_poll_setup(&pollfd, 1, pchild); + apr_poll_socket_add(pollfd, ev_sock, APR_POLLIN); + apr_pollset_add(pollset, pollfd); + hs = malloc(sizeof(*hs)); + hs->sock = ev_pipe; + pollfd->client_data = (void*) hs; + + while (1) { + do { + rc = apr_pollset_poll(pollset, timeout, &num, &pollfd); + /* Todo: Timeout old connections */ + } while (rc == APR_TIMEUP); + + while (num) { + hs = (http_state_t*) pollfd->client_data; + if (hs->sock == ev_pipe) { + /* Read the pointer to hs and reset event */ + rc = read(evpipe, &hs, 4); + apr_poll_setup(&pollfd, 1, hs->p); + apr_poll_socket_add(pollfd, hs->sock, APR_POLLIN); + pollfd->client_data = (void*) hs; + apr_pollset_add(pollset, pollfd); + } + else { + rc = apr_pollset_remove(pollset, pollfd); + + ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, + "IO Event Received: hs = %x, hs->state = %d", hs, hs->state); + rc = ap_queue_push(worker_queue, + pollfd->desc.s, + hs, + hs->p); + pollfd++; + num--; + } + } + } +} +#endif +/*static void* create_event_thread(apr_thread_t *thd, void* dummy) */ +static void create_event_thread() +{ + apr_threadattr_t *thread_attr; + apr_thread_t *thread; + proc_info *my_info; + apr_status_t rv; + + apr_threadattr_create(&thread_attr, pchild); + apr_threadattr_detach_set(thread_attr, 0); + + my_info = (proc_info *)malloc(sizeof(proc_info)); + my_info->pid = -1; + my_info->tid = -1; + my_info->sd = 0; + + rv = apr_thread_create(&thread, thread_attr, event_loop, + my_info, pchild); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, + "apr_thread_create: unable to create event thread"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} static void create_listener_thread(thread_starter *ts) { int my_child_num = ts->child_num_arg; @@ -1083,6 +1254,21 @@ create_listener_thread(ts); listener_started = 1; } +#if 1 + /* Create ev_sock, set to non-blocking */ + +#else + /* Init the event queue/thread */ + rv = apr_queue_create(&event_queue, 100, pchild); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, + "ap_queue_init() failed"); + clean_child_exit(APEXIT_CHILDFATAL); + } +#endif + create_event_thread(); + apr_sleep(100); + if (start_thread_may_exit || threads_created == ap_threads_per_child) { break; }
eventio.patch2
Description: Binary data