fielding 99/07/30 20:30:19
Modified: src CHANGES src/main http_protocol.c Log: Replace the Vary fixup code with a single-pass, single-copy implementation that only adds the cost of a single ap_make_array when there is no Vary field. Revision Changes Path 1.1406 +1 -1 apache-1.3/src/CHANGES Index: CHANGES =================================================================== RCS file: /home/cvs/apache-1.3/src/CHANGES,v retrieving revision 1.1405 retrieving revision 1.1406 diff -u -r1.1405 -r1.1406 --- CHANGES 1999/07/31 00:37:22 1.1405 +++ CHANGES 1999/07/31 03:30:16 1.1406 @@ -10,7 +10,7 @@ *) Fix SIGSEGV on some systems because the Vary fix below included a call to table_do with a variable argument list that was not - NULL terminated. [Roy Fielding] + NULL terminated. Replaced with better implementation. [Roy Fielding] Changes with Apache 1.3.7 [not released] 1.275 +56 -85 apache-1.3/src/main/http_protocol.c Index: http_protocol.c =================================================================== RCS file: /home/cvs/apache-1.3/src/main/http_protocol.c,v retrieving revision 1.274 retrieving revision 1.275 diff -u -r1.274 -r1.275 --- http_protocol.c 1999/07/31 00:37:25 1.274 +++ http_protocol.c 1999/07/31 03:30:18 1.275 @@ -1446,27 +1446,55 @@ && strstr(ua, "MSIE 3"))); } -/* - * qsort comparison routine for fixup_vary(). +/* This routine is called by ap_table_do and merges all instances of + * the passed field values into a single array that will be further + * processed by some later routine. Originally intended to help split + * and recombine multiple Vary fields, though it is generic to any field + * consisting of comma/space-separated tokens. */ -static int compare_vary(const void *va, const void *vb) +static int uniq_field_values(void *d, const char *key, const char *val) { - return strcasecmp(*(const char **)va, *(const char **)vb); -} + array_header *values; + char *start; + char *e; + char **strpp; + int i; -/* - * ap_table_get() only picks up the first occurrence of a key, - * which means that if there's a Vary in r->headers_out and another - * in r->err_headers_out, one might get ignored. This routine - * is called by ap_table_do and merges all instances of Vary into - * a temporary list in r->notes. - */ -static int merge_vary_fields(void *d, const char *key, const char *val) -{ - request_rec *r; + values = (array_header *)d; + + e = ap_pstrdup(values->pool, val); - r = (request_rec *)d; - ap_table_merge(r->notes, "Vary-list", val); + do { + /* Find a non-empty fieldname */ + + while (*e == ',' || ap_isspace(*e)) { + ++e; + } + if (*e == '\0') { + break; + } + start = e; + while (*e != '\0' && *e != ',' && !ap_isspace(*e)) { + ++e; + } + if (*e != '\0') { + *e++ = '\0'; + } + + /* Now add it to values if it isn't already represented. + * Could be replaced by a ap_array_strcasecmp() if we had one. + */ + for (i = 0, strpp = (char **) values->elts; i < values->nelts; + ++i, ++strpp) { + if (*strpp && strcasecmp(*strpp, start) == 0) { + break; + } + } + if (i == values->nelts) { /* if not found */ + *(char **)ap_push_array(values) = start; + } + } while (*e != '\0'); + return 1; } @@ -1477,80 +1505,23 @@ */ static void fixup_vary(request_rec *r) { - const char *vary; - array_header *arr; - char *start; - char *e; - char **ecur; - char **eend; - char **ekeep; + array_header *varies; - /* Don't do any unnecessary manipulations.. - */ - if (ap_table_get(r->headers_out, "Vary") == NULL) { - return; - } - ap_table_do((int (*)(void *, const char *, const char *))merge_vary_fields, - (void *) r, r->headers_out, "Vary", NULL); - vary = ap_table_get(r->notes, "Vary-list"); - - /* XXX: we could make things a lot better, by having r->vary, - * which is an array of char * -- which modules append to as they - * find things which the request varies on. This is probably - * better than a table, because a table would require O(n^2) - * string comparisons... another option would be to use a table - * but indicate that folks should use ap_table_add... - * at any rate, if we had such an array, we would just set - * arr = r->vary here (or arr = ap_table_elts(r->vary)). - */ - arr = ap_make_array(r->pool, 5, sizeof(char *)); + varies = ap_make_array(r->pool, 5, sizeof(char *)); - /* XXX: this part could become a new routine which takes a string - * and breaks it up, appending to an array, spliting on /[,\s]+/. + /* Extract all Vary fields from the headers_out, separate each into + * its comma-separated fieldname values, and then add them to varies + * if not already present in the array. */ - e = ap_pstrdup(r->pool, vary); - start = e; - while (*e) { - while (ap_isspace(*e)) { - ++e; - } - start = e; - e = strchr(e, ','); - if (!e) { - break; - } - *e = '\0'; - *(char **)ap_push_array(arr) = start; - ++e; - } - if (*start) { - *(char **)ap_push_array(arr) = start; - } + ap_table_do((int (*)(void *, const char *, const char *))uniq_field_values, + (void *) varies, r->headers_out, "Vary", NULL); - /* XXX: this part could become a new routine which modifies an - * array of char * in place, eliminating duplicate entries - */ - if (arr->nelts > 1) { - qsort(arr->elts, arr->nelts, sizeof(char *), compare_vary); + /* If we found any, replace old Vary fields with unique-ified value */ - /* now pluck out the non-duplicates */ - ekeep = (char **)arr->elts; - ecur = ekeep + 1; - eend = ekeep + arr->nelts - 1; - while (ecur <= eend) { - if (strcasecmp(*ecur, *ekeep)) { - *++ekeep = *ecur; - } - ++ecur; - } - arr->nelts = ekeep - (char **)arr->elts + 1; + if (varies->nelts > 0) { + ap_table_setn(r->headers_out, "Vary", + ap_array_pstrcat(r->pool, varies, ',')); } - - /* and finally we're done, we can just merge the array adding , - * between entries - */ - ap_table_setn(r->headers_out, "Vary", - ap_array_pstrcat(r->pool, arr, ',')); } API_EXPORT(void) ap_send_http_header(request_rec *r)