diff -Naur apache_1.3.6+01/src/include/ap_config.h apache_1.3.6+01-02/src/include/ap_config.h --- apache_1.3.6+01/src/include/ap_config.h Thu Jul 8 12:11:36 1999 +++ apache_1.3.6+01-02/src/include/ap_config.h Wed Jul 14 15:46:15 1999 @@ -210,6 +210,7 @@ #define NO_LONG_DOUBLE #define NO_LINGCLOSE #define HAVE_SYSLOG 1 +#define CACHE_ALIGNMENT 128 /* best value for most SGI servers */ #elif defined(HIUX) #undef HAVE_GMTOFF @@ -1196,6 +1197,22 @@ */ #if defined(__WCOREDUMP) && !defined(WCOREDUMP) #define WCOREDUMP __WCOREDUMP +#endif + +/* + * Aligning the address and length of certain key data structures and + * buffers to exactly match cache lines can dramatically increase + * performance. In the absence of an architecture-specific + * CACHE_ALIGNMENT definition from above, assume this arbitrary default. + * Set to 0 to disable alignment. When adding your own specific version + * above, use the larger of your processor's primary and secondary cache + * line sizes. + */ +#ifndef CACHE_ALIGNMENT +#define CACHE_ALIGNMENT 32 /* in bytes */ +#endif +#if CACHE_ALIGNMENT & (CACHE_ALIGNMENT - 1) +#error "CACHE_ALIGNMENT must be a power of two" #endif #ifdef SUNOS_LIB_PROTOTYPES diff -Naur apache_1.3.6+01/src/include/httpd.h apache_1.3.6+01-02/src/include/httpd.h --- apache_1.3.6+01/src/include/httpd.h Thu Jul 8 12:14:00 1999 +++ apache_1.3.6+01-02/src/include/httpd.h Wed Jul 14 15:47:23 1999 @@ -779,6 +779,10 @@ * record to improve 64bit alignment the next time we need to break * binary compatibility for some other reason. */ + + char *rqbuf; /* quickly_read_request_line()'s buffer */ + char *rqhdr; /* quickly_get_mime_headers()'s headers */ + int rqhdr_len; /* strlen(rqhdr) */ }; diff -Naur apache_1.3.6+01/src/main/http_protocol.c apache_1.3.6+01-02/src/main/http_protocol.c --- apache_1.3.6+01/src/main/http_protocol.c Thu Jul 8 12:28:05 1999 +++ apache_1.3.6+01-02/src/main/http_protocol.c Wed Jul 14 17:19:12 1999 @@ -771,11 +771,117 @@ } } +#ifndef RQBUF_SIZE +#define RQBUF_SIZE 4096 /* should be >= buff.c's DEFAULT_BUFSIZE */ +#endif + +/* + * Read the request and, as quickly as possible with minimal copying, + * parse it or pass it on to the regular parsing function + * read_request_line(). Shares state with quickly_get_mime_headers(), + * which has a similar mission. + */ +static int +quickly_read_request_line(request_rec *r) +{ + BUFF *fb; + int n; + + /* + * If r->rqbuf already exists, there could be pointers into it from + * r->headers_in so instead of reusing it, just replace it. + */ +#if CACHE_ALIGNMENT > 0 + /* align r->rqbuf on a cache line boundary */ + r->rqbuf = ap_palloc(r->pool, RQBUF_SIZE + CACHE_ALIGNMENT - 1); + r->rqbuf = (char *) (((ap_ptr) r->rqbuf + CACHE_ALIGNMENT - 1) & ~(CACHE_ALIGNMENT - 1)); +#else + r->rqbuf = ap_palloc(r->pool, RQBUF_SIZE); +#endif + + /* + * would read directly into fb->inptr but r->the_request needs an + * intact copy, sigh + */ + fb = r->connection->client; + fb->flags |= B_SAFEREAD; + n = ap_bread(fb, r->rqbuf, RQBUF_SIZE); + fb->flags &= ~B_SAFEREAD; + + /* 16 == strlen("GET / HTTP/1.0\n\n"): minimal valid request */ + if (n >= 16 && + r->rqbuf[0] == 'G' && + r->rqbuf[1] == 'E' && + r->rqbuf[2] == 'T' && + r->rqbuf[3] == ' ') { + char *cp, *ep; + + /* find the end of the uri */ + cp = &r->rqbuf[4]; /* 4 == strlen("GET ") */ + ep = &r->rqbuf[n - 11]; /* 11 == strlen(" HTTP/1.0\n\n") */ + while (cp <= ep && !ap_isspace(*cp)) + cp++; + + if (cp <= ep && + cp[0] == ' ' && + cp[1] == 'H' && + cp[2] == 'T' && + cp[3] == 'T' && + cp[4] == 'P' && + cp[5] == '/' && + cp[6] == '1' && + cp[7] == '.' && + (cp[8] == '0' || cp[8] == '1') && + ((cp[9] == '\r' && cp[10] == '\n') || cp[9] == '\n')) { + + r->rqhdr = cp + 10 + (cp[9] == '\r'); + r->rqhdr_len = (int) (&r->rqbuf[n] - r->rqhdr); + cp[9] = 0; /* null-terminate r->the_request */ + + r->request_time = time(NULL); + r->the_request = r->rqbuf; + r->method = "GET"; + r->method_number = M_GET; + ap_assert(!r->assbackwards); + + if (cp[8] == '0') { + r->protocol = "HTTP/1.0"; + r->proto_num = HTTP_VERSION(1,0); + } else { + r->protocol = "HTTP/1.1"; + r->proto_num = HTTP_VERSION(1,1); + } + + *cp = 0; /* avoid copying uri; briefly mangles r->the_request */ + ap_parse_uri(r, &r->rqbuf[4]); + *cp = ' '; + + return 1; + } + } + + /* + * Shortcut parsing failed. No harm done, just copy the + * already-read data into fb and fall back to regular processing. + * If copying bothers you, extend the buffering code to allow an + * alternate read-ahead buffer and then just manipulate pointers. + */ + ap_assert(r->rqhdr_len == 0); + if (n > 0) { + ap_assert(&fb->inptr[n] <= &fb->inbase[fb->bufsiz]); + memcpy(fb->inptr, r->rqbuf, n); + fb->incnt += n; + } + + return 0; +} + static int read_request_line(request_rec *r) { char l[DEFAULT_LIMIT_REQUEST_LINE + 2]; /* getline's two extra for \n\0 */ const char *ll = l; const char *uri; + const char *pro; conn_rec *conn = r->connection; int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol */ int len; @@ -809,7 +915,7 @@ ap_bsetflag(conn->client, B_SAFEREAD, 0); r->request_time = time(NULL); - r->the_request = ap_pstrdup(r->pool, l); + r->the_request = ap_pstrndup(r->pool, l, len); r->method = ap_getword_white(r->pool, &ll); uri = ap_getword_white(r->pool, &ll); @@ -834,9 +940,23 @@ } r->assbackwards = (ll[0] == '\0'); - r->protocol = ap_pstrdup(r->pool, ll[0] ? ll : "HTTP/0.9"); + + if (ll[0]) { + pro = ll; + len = ap_strlen(ll); + } else { + pro = "HTTP/0.9"; + len = 8; + } + r->protocol = ap_pstrndup(r->pool, pro, len); - if (2 == sscanf(r->protocol, "HTTP/%u.%u", &major, &minor) + /* avoid sscanf in the common case */ + if (len == 8 && + pro[0] == 'H' && pro[1] == 'T' && pro[2] == 'T' && pro[3] == 'P' && + pro[4] == '/' && ap_isdigit(pro[5]) && pro[6] == '.' && + ap_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 @@ -845,6 +965,133 @@ return 1; } +/* + * Parse the request options as quickly as possible. Requires state + * from quickly_read_request_line(). Handles only the fast cases, and + * detects and defers the slow ones to the regular parser + * get_mime_headers(). + */ +static int +quickly_get_mime_headers(request_rec *r) +{ + int ok, nh, mh; + table *headers; + char *hp, *ep; + const char *h1name, *h1value; + int remain; + + ok = 0; + + nh = ap_table_elts(r->headers_in)->nelts; + mh = r->server->limit_req_fields; + if (mh <= 0) + mh = 65536; /* essentially infinite */ + headers = NULL; + + hp = r->rqhdr; + ep = hp + r->rqhdr_len; + r->rqhdr_len = 0; /* never re-parse */ + + h1name = NULL; +#ifdef __GNUC__ + h1value = NULL; /* appease stupid compiler */ +#endif + + while (hp < ep && nh < mh) { + char *name, *value; + + name = hp; + + /* find a : (end of name) */ + value = name; + while (value < ep && *value != ':' && !ap_isspace(*value)) + value++; + + /* complete name? */ + if (value > name && value < ep && *value == ':') { + *value = 0; /* null-terminate name */ + + /* skip white space */ + do + value++; + while (value < ep && (*value == ' ' || *value == '\t')); + + /* find end of value */ + hp = value; + while (hp < ep && *hp != '\n') + hp++; + + /* + * complete value? beware leading-space continuation lines, + * or the threat of one if we're right at the end of rqbuf. + * (there must be at least one more byte left and it must + * not be a leading space) + */ + if (hp + 1 < ep && hp[1] != ' ' && hp[1] != '\t') { + hp[-(hp[-1] == '\r')] = 0; /* null-terminate value */ + hp++; + + if (headers) { + /* insert into established table */ + ap_table_addn(headers, name, value); + } else if (h1name) { + /* + * More than one header (this is the second). Make + * a table for them. + */ + headers = ap_make_table(r->pool, 16); + ap_table_addn(headers, h1name, h1value); + ap_table_addn(headers, name, value); + } else { + /* first header, keep it close in case there are no more */ + h1name = name; + h1value = value; + } + + nh++; + } else { + hp = name; + do + name++; + while (*name); + *name = ':'; /* restore : for copy below */ + break; + } + } else { + if (hp < ep && *hp == '\r') + hp++; + if (hp < ep && *hp == '\n') { + hp++; + ok = 1; /* end of header section */ + } + break; + } + } + + /* + * ap_overlap_tables() is expensive so avoid it when possible. + * Right now we skip it only when there are fewer than two headers, + * but we could also skip it if we determine that there are no + * merges required, i.e., there are no duplicate headers. Then we + * could just append headers onto r->headers_in. + */ + if (headers) + ap_overlap_tables(r->headers_in, headers, AP_OVERLAP_TABLES_MERGE); + else if (h1name) + ap_table_mergen(r->headers_in, h1name, h1value); + + remain = (int) (ep - hp); + if (remain > 0) { + BUFF *fb = r->connection->client; + + ap_assert(&fb->inptr[remain] <= &fb->inbase[fb->bufsiz]); + memcpy(fb->inptr, hp, remain); + fb->incnt += remain; + } + + return ok; +} + static void get_mime_headers(request_rec *r) { char field[DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2]; /* getline's two extra */ @@ -852,7 +1099,7 @@ char *value; char *copy; int len; - unsigned int fields_read = 0; + unsigned int fields_read = ap_table_elts(r->headers_in)->nelts; table *tmp_headers; /* We'll use ap_overlap_tables later to merge these into r->headers_in. */ @@ -934,13 +1181,9 @@ r->request_config = ap_create_request_config(r->pool); 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; #ifdef CHARSET_EBCDIC ap_bsetflag(r->connection->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1); @@ -949,7 +1192,7 @@ /* Get the request... */ ap_keepalive_timeout("read request line", r); - if (!read_request_line(r)) { + if (!quickly_read_request_line(r) && !read_request_line(r)) { ap_kill_timeout(r); if (r->status == HTTP_REQUEST_URI_TOO_LARGE) { @@ -964,7 +1207,8 @@ } if (!r->assbackwards) { ap_hard_timeout("read request headers", r); - get_mime_headers(r); + if (!quickly_get_mime_headers(r)) + get_mime_headers(r); ap_kill_timeout(r); if (r->status != HTTP_REQUEST_TIME_OUT) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,