coar 99/07/29 15:22:09
Modified: . STATUS src .gdbinit CHANGES src/main http_protocol.c Log: Fix the IE-identified problems with Vary (multiple instances, duplicate tokens) for good and all. We can't be sure we're putting in all the right values for all circumstances, but at least now we know the field we send to the client is syntactically sane. (Not that it wasn't before, but it *was* slightly questionable.) PR: 4118 Submitted by: Dean Gaudet, Roy Fielding, Ken Coar Reviewed by: Ken Coar Revision Changes Path 1.730 +1 -16 apache-1.3/STATUS Index: STATUS =================================================================== RCS file: /home/cvs/apache-1.3/STATUS,v retrieving revision 1.729 retrieving revision 1.730 diff -u -r1.729 -r1.730 --- STATUS 1999/07/29 17:55:54 1.729 +++ STATUS 1999/07/29 22:21:49 1.730 @@ -1,5 +1,5 @@ 1.3 STATUS: - Last modified at [$Date: 1999/07/29 17:55:54 $] + Last modified at [$Date: 1999/07/29 22:21:49 $] Release: @@ -66,23 +66,8 @@ RELEASE SHOWSTOPPERS: - * The Vary header field stuff is still broken (multiple - entries occur, etc.). The result is that some browsers (AFAIK at least - MSIE) are horribly confused by the responses. - Status: It should be fixed before 1.3.7 went out. For details - how it should be done, please look at new-httpd mailing list - archive: Ken, Ralf and Roy have already found consensus in the - past there. RELEASE NON-SHOWSTOPPERS BUT WOULD BE REAL NICE TO WRAP THESE UP: - - * The Vary header field stuff is still broken (multiple - entries occur, etc.). The result is that some browsers (AFAIK at least - MSIE) are horribly confused by the responses. - Status: It should be fixed before 1.3.7 went out. For details - how it should be done, please look at new-httpd mailing list - archive: Ken, Ralf and Roy have already found consensus in the - past there. * Graham Legget has found that if he uses the 1.3.7-dev core, and the 1.3.6 proxy code (plus a small patch of his) he doesn't get 1.3 +13 -0 apache-1.3/src/.gdbinit Index: .gdbinit =================================================================== RCS file: /home/cvs/apache-1.3/src/.gdbinit,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- .gdbinit 1998/04/15 18:14:38 1.2 +++ .gdbinit 1999/07/29 22:21:50 1.3 @@ -13,3 +13,16 @@ document dump_table Print the key/value pairs in a table. end + +define dump_string_array + set $a = (char **)((array_header *)$arg0)->elts + set $n = (int)((array_header *)$arg0)->nelts + set $i = 0 + while $i < $n + printf "[%u] '%s'\n", $i, $a[$i] + set $i = $i + 1 + end +end +document dump_string_array + Print all of the elements in an array of strings. +end \ No newline at end of file 1.1403 +8 -7 apache-1.3/src/CHANGES Index: CHANGES =================================================================== RCS file: /home/cvs/apache-1.3/src/CHANGES,v retrieving revision 1.1402 retrieving revision 1.1403 diff -u -r1.1402 -r1.1403 --- CHANGES 1999/07/29 18:13:10 1.1402 +++ CHANGES 1999/07/29 22:21:50 1.1403 @@ -1,13 +1,14 @@ Changes with Apache 1.3.7 - *) Portability changes for BeOS. [David Reid [EMAIL PROTECTED] + *) The "Vary" response header field is now sanitised right before + the header is sent back to the client. Multiple "Vary" fields + are combined, and duplicate tokens (e.g., "Vary: host, host" or + "Vary: host, negotiate, host, accept-language") are reduced to + single instances. This is a better solution than the force-no-vary + one (which is still valid for clients that can't cope with Vary + at all). PR#3118 [Dean Gaudet, Roy Fielding, Ken Coar] - *) Sanitise "Vary" values by not adding duplicate keywords. A - separate routine needs to be used to do this, so any module - that frobs "Vary" needs to be changed. The standard modules - have all been modified. This solution is somewhat inelegant, - but it does the job for now. PR#4118 (better fix than before) - [Ken Coar, Roy Fielding] + *) Portability changes for BeOS. [David Reid [EMAIL PROTECTED] *) Link DSO's with "gcc -shared" instead of "ld -Bshareable" at least on Linux and FreeBSD for now. 1.273 +110 -0 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.272 retrieving revision 1.273 diff -u -r1.272 -r1.273 --- http_protocol.c 1999/07/19 10:15:58 1.272 +++ http_protocol.c 1999/07/29 22:22:04 1.273 @@ -1446,6 +1446,113 @@ && strstr(ua, "MSIE 3"))); } +/* + * qsort comparison routine for fixup_vary(). + */ +static int compare_vary(const void *va, const void *vb) +{ + return strcasecmp(*(const char **)va, *(const char **)vb); +} + +/* + * 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; + + r = (request_rec *)d; + ap_table_merge(r->notes, "Vary-list", val); + return 1; +} + +/* + * Since some clients choke violently on multiple Vary fields, or + * Vary fields with duplicate tokens, combine any multiples and remove + * any duplicates. + */ +static void fixup_vary(request_rec *r) +{ + const char *vary; + array_header *arr; + char *start; + char *e; + char **ecur; + char **eend; + char **ekeep; + + /* 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"); + 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 *)); + + /* XXX: this part could become a new routine which takes a string + * and breaks it up, appending to an array, spliting on /[,\s]+/. + */ + 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; + } + + /* 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); + + /* 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; + } + + /* 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) { int i; @@ -1479,6 +1586,9 @@ ap_table_unset(r->headers_out, "Vary"); r->proto_num = HTTP_VERSION(1,0); ap_table_set(r->subprocess_env, "force-response-1.0", "1"); + } + else { + fixup_vary(r); } ap_hard_timeout("send headers", r);