--------------------- PatchSet 5298 Date: 2002/10/08 11:11:33 Author: rbcollins Branch: rbcollins_cxxtest Tag: (none) Log: and more Members: include/Array.h:1.6.44.2->1.6.44.3 include/util.h:1.11.20.5->1.11.20.6 src/HttpHdrContRange.c:1.5->1.5.44.1(DEAD) src/HttpHdrContRange.cc:1.1->1.1.2.1 src/HttpHdrExtField.c:1.4->1.4.96.1(DEAD) src/HttpHdrExtField.cc:1.1->1.1.2.1 src/HttpHdrRange.c:1.7.44.1->1.7.44.2(DEAD) src/HttpHdrRange.cc:1.1->1.1.2.1 src/HttpHeader.c:1.16.10.3->1.16.10.4(DEAD) src/HttpHeader.cc:1.1->1.1.2.1 src/HttpMsg.c:1.6->1.6.96.1(DEAD) src/HttpMsg.cc:1.1->1.1.2.1 src/HttpReply.c:1.10->1.10.44.1(DEAD) src/HttpReply.cc:1.1->1.1.2.1 src/HttpStatusLine.c:1.9->1.9.4.1(DEAD) src/HttpStatusLine.cc:1.1->1.1.2.1 src/Makefile.am:1.29.2.12->1.29.2.13 src/enums.h:1.40.2.5->1.40.2.6 Index: squid/include/Array.h =================================================================== RCS file: /cvsroot/squid-sf//squid/include/Array.h,v retrieving revision 1.6.44.2 retrieving revision 1.6.44.3 diff -u -r1.6.44.2 -r1.6.44.3 --- squid/include/Array.h 4 Oct 2002 07:07:18 -0000 1.6.44.2 +++ squid/include/Array.h 8 Oct 2002 11:11:33 -0000 1.6.44.3 @@ -1,5 +1,5 @@ /* - * $Id: Array.h,v 1.6.44.2 2002/10/04 07:07:18 rbcollins Exp $ + * $Id: Array.h,v 1.6.44.3 2002/10/08 11:11:33 rbcollins Exp $ * * AUTHOR: Alex Rousskov * @@ -45,10 +45,10 @@ extern Array *arrayCreate(void); SQUIDCEXTERN void arrayInit(Array * s); -extern void arrayClean(Array * s); +SQUIDCEXTERN void arrayClean(Array * s); extern void arrayDestroy(Array * s); SQUIDCEXTERN void arrayAppend(Array * s, void *obj); -extern void arrayPreAppend(Array * s, int app_count); +SQUIDCEXTERN void arrayPreAppend(Array * s, int app_count); #endif /* SQUID_ARRAY_H */ Index: squid/include/util.h =================================================================== RCS file: /cvsroot/squid-sf//squid/include/util.h,v retrieving revision 1.11.20.5 retrieving revision 1.11.20.6 diff -u -r1.11.20.5 -r1.11.20.6 --- squid/include/util.h 7 Oct 2002 09:21:04 -0000 1.11.20.5 +++ squid/include/util.h 8 Oct 2002 11:11:34 -0000 1.11.20.6 @@ -1,5 +1,5 @@ /* - * $Id: util.h,v 1.11.20.5 2002/10/07 09:21:04 rbcollins Exp $ + * $Id: util.h,v 1.11.20.6 2002/10/08 11:11:34 rbcollins Exp $ * * AUTHOR: Harvest Derived * @@ -73,8 +73,8 @@ SQUIDCEXTERN int tvSubUsec(struct timeval, struct timeval); SQUIDCEXTERN double tvSubDsec(struct timeval, struct timeval); SQUIDCEXTERN char *xstrncpy(char *, const char *, size_t); -extern size_t xcountws(const char *str); -extern time_t parse_rfc1123(const char *str); +SQUIDCEXTERN size_t xcountws(const char *str); +SQUIDCEXTERN time_t parse_rfc1123(const char *str); SQUIDCEXTERN void *xcalloc(size_t, size_t); SQUIDCEXTERN void *xmalloc(size_t); SQUIDCEXTERN void *xrealloc(void *, size_t); @@ -121,7 +121,7 @@ SQUIDCEXTERN int xpercentInt(double part, double whole); SQUIDCEXTERN double xdiv(double nom, double denom); -extern const char *xitoa(int num); +SQUIDCEXTERN const char *xitoa(int num); #if !HAVE_DRAND48 double drand48(void); --- squid/src/HttpHdrContRange.c Wed Feb 14 01:07:36 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,195 +0,0 @@ - -/* - * $Id: HttpHdrContRange.c,v 1.5 2001/10/24 09:42:11 squidadm Exp $ - * - * DEBUG: section 68 HTTP Content-Range Header - * AUTHOR: Alex Rousskov - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid.h" - -/* - * Currently only byte ranges are supported - * - * Content-Range = "Content-Range" ":" content-range-spec - * content-range-spec = byte-content-range-spec - * byte-content-range-spec = bytes-unit SP - * ( byte-range-resp-spec | "*") "/" - * ( entity-length | "*" ) - * byte-range-resp-spec = first-byte-pos "-" last-byte-pos - * entity-length = 1*DIGIT - */ - - -/* local constants */ -#define range_spec_unknown ((size_t)-1) - -/* local routines */ -#define known_spec(s) ((s) != range_spec_unknown) -#define size_min(a,b) ((a) <= (b) ? (a) : (b)) -#define size_diff(a,b) ((a) >= (b) ? ((a)-(b)) : 0) - -/* globals */ - -/* parses range-resp-spec and inits spec, returns true on success */ -static int -httpHdrRangeRespSpecParseInit(HttpHdrRangeSpec * spec, const char *field, int flen) -{ - const char *p; - assert(spec); - spec->offset = spec->length = range_spec_unknown; - if (flen < 2) - return 0; - /* is spec given ? */ - if (*field == '*') - return 1; - /* check format, must be %d-%d */ - if (!((p = strchr(field, '-')) && (p - field < flen))) { - debug(68, 2) ("invalid (no '-') resp-range-spec near: '%s'\n", field); - return 0; - } - /* parse offset */ - if (!httpHeaderParseSize(field, &spec->offset)) - return 0; - p++; - /* do we have last-pos ? */ - if (p - field < flen) { - ssize_t last_pos; - if (!httpHeaderParseSize(p, &last_pos)) - return 0; - spec->length = size_diff(last_pos + 1, spec->offset); - } - /* we managed to parse, check if the result makes sence */ - if (known_spec(spec->length) && !spec->length) { - debug(68, 2) ("invalid range (%ld += %ld) in resp-range-spec near: '%s'\n", - (long int) spec->offset, (long int) spec->length, field); - return 0; - } - return 1; -} - -static void -httpHdrRangeRespSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p) -{ - if (!known_spec(spec->offset) || !known_spec(spec->length)) - packerPrintf(p, "*"); - else - packerPrintf(p, "bytes %ld-%ld", - (long int) spec->offset, (long int) spec->offset + spec->length - 1); -} - -/* - * Content Range - */ - -HttpHdrContRange * -httpHdrContRangeCreate(void) -{ - HttpHdrContRange *r = memAllocate(MEM_HTTP_HDR_CONTENT_RANGE); - r->spec.offset = r->spec.length = range_spec_unknown; - r->elength = range_spec_unknown; - return r; -} - -HttpHdrContRange * -httpHdrContRangeParseCreate(const char *str) -{ - HttpHdrContRange *r = httpHdrContRangeCreate(); - if (!httpHdrContRangeParseInit(r, str)) { - httpHdrContRangeDestroy(r); - r = NULL; - } - return r; -} - -/* returns true if ranges are valid; inits HttpHdrContRange */ -int -httpHdrContRangeParseInit(HttpHdrContRange * range, const char *str) -{ - const char *p; - assert(range && str); - debug(68, 8) ("parsing content-range field: '%s'\n", str); - /* check range type */ - if (strncasecmp(str, "bytes ", 6)) - return 0; - str += 6; - /* split */ - if (!(p = strchr(str, '/'))) - return 0; - if (*str == '*') - range->spec.offset = range->spec.length = range_spec_unknown; - else if (!httpHdrRangeRespSpecParseInit(&range->spec, str, p - str)) - return 0; - p++; - if (*p == '*') - range->elength = range_spec_unknown; - else if (!httpHeaderParseSize(p, &range->elength)) - return 0; - debug(68, 8) ("parsed content-range field: %ld-%ld / %ld\n", - (long int) range->spec.offset, (long int) range->spec.offset + range->spec.length - 1, - (long int) range->elength); - return 1; -} - -void -httpHdrContRangeDestroy(HttpHdrContRange * range) -{ - assert(range); - memFree(range, MEM_HTTP_HDR_CONTENT_RANGE); -} - -HttpHdrContRange * -httpHdrContRangeDup(const HttpHdrContRange * range) -{ - HttpHdrContRange *dup; - assert(range); - dup = httpHdrContRangeCreate(); - *dup = *range; - return dup; -} - -void -httpHdrContRangePackInto(const HttpHdrContRange * range, Packer * p) -{ - assert(range && p); - httpHdrRangeRespSpecPackInto(&range->spec, p); - if (!known_spec(range->elength)) - packerPrintf(p, "/*"); - else - packerPrintf(p, "/%ld", (long int) range->elength); -} - -void -httpHdrContRangeSet(HttpHdrContRange * cr, HttpHdrRangeSpec spec, ssize_t ent_len) -{ - assert(cr && ent_len >= 0); - cr->spec = spec; - cr->elength = ent_len; -} --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/HttpHdrContRange.cc Wed Feb 14 01:07:36 2007 @@ -0,0 +1,204 @@ + +/* + * $Id: HttpHdrContRange.cc,v 1.1.2.1 2002/10/08 11:11:34 rbcollins Exp $ + * + * DEBUG: section 68 HTTP Content-Range Header + * AUTHOR: Alex Rousskov + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + +/* + * Currently only byte ranges are supported + * + * Content-Range = "Content-Range" ":" content-range-spec + * content-range-spec = byte-content-range-spec + * byte-content-range-spec = bytes-unit SP + * ( byte-range-resp-spec | "*") "/" + * ( entity-length | "*" ) + * byte-range-resp-spec = first-byte-pos "-" last-byte-pos + * entity-length = 1*DIGIT + */ + + +/* local constants */ +#define range_spec_unknown ((size_t)-1) + +/* local routines */ +#define known_spec(s) ((s) != range_spec_unknown) +#define size_min(a,b) ((a) <= (b) ? (a) : (b)) +#define size_diff(a,b) ((a) >= (b) ? ((a)-(b)) : 0) + +/* globals */ + +/* parses range-resp-spec and inits spec, returns true on success */ +static int +httpHdrRangeRespSpecParseInit(HttpHdrRangeSpec * spec, const char *field, int flen) +{ + const char *p; + assert(spec); + spec->offset = spec->length = range_spec_unknown; + if (flen < 2) + return 0; + /* is spec given ? */ + if (*field == '*') + return 1; + /* check format, must be %d-%d */ + if (!((p = strchr(field, '-')) && (p - field < flen))) { + debug(68, 2) ("invalid (no '-') resp-range-spec near: '%s'\n", field); + return 0; + } + /* parse offset */ + if (!httpHeaderParseSize(field, &spec->offset)) + return 0; + p++; + /* do we have last-pos ? */ + if (p - field < flen) { + ssize_t last_pos; + if (!httpHeaderParseSize(p, &last_pos)) + return 0; + spec->length = size_diff(last_pos + 1, spec->offset); + } + /* Ensure typecast is safe */ + assert (spec->length >= 0); + + /* we managed to parse, check if the result makes sence */ + if (known_spec((size_t)spec->length) && spec->length != 0) { + debug(68, 2) ("invalid range (%ld += %ld) in resp-range-spec near: '%s'\n", + (long int) spec->offset, (long int) spec->length, field); + return 0; + } + return 1; +} + +static void +httpHdrRangeRespSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p) +{ + /* Ensure typecast is safe */ + assert (spec->length >= 0); + assert (spec->length >= 0); + + if (!known_spec((size_t)spec->offset) || !known_spec((size_t)spec->length)) + packerPrintf(p, "*"); + else + packerPrintf(p, "bytes %ld-%ld", + (long int) spec->offset, (long int) spec->offset + spec->length - 1); +} + +/* + * Content Range + */ + +HttpHdrContRange * +httpHdrContRangeCreate(void) +{ + HttpHdrContRange *r = (HttpHdrContRange *)memAllocate(MEM_HTTP_HDR_CONTENT_RANGE); + r->spec.offset = r->spec.length = range_spec_unknown; + r->elength = range_spec_unknown; + return r; +} + +HttpHdrContRange * +httpHdrContRangeParseCreate(const char *str) +{ + HttpHdrContRange *r = httpHdrContRangeCreate(); + if (!httpHdrContRangeParseInit(r, str)) { + httpHdrContRangeDestroy(r); + r = NULL; + } + return r; +} + +/* returns true if ranges are valid; inits HttpHdrContRange */ +int +httpHdrContRangeParseInit(HttpHdrContRange * range, const char *str) +{ + const char *p; + assert(range && str); + debug(68, 8) ("parsing content-range field: '%s'\n", str); + /* check range type */ + if (strncasecmp(str, "bytes ", 6)) + return 0; + str += 6; + /* split */ + if (!(p = strchr(str, '/'))) + return 0; + if (*str == '*') + range->spec.offset = range->spec.length = range_spec_unknown; + else if (!httpHdrRangeRespSpecParseInit(&range->spec, str, p - str)) + return 0; + p++; + if (*p == '*') + range->elength = range_spec_unknown; + else if (!httpHeaderParseSize(p, &range->elength)) + return 0; + debug(68, 8) ("parsed content-range field: %ld-%ld / %ld\n", + (long int) range->spec.offset, (long int) range->spec.offset + range->spec.length - 1, + (long int) range->elength); + return 1; +} + +void +httpHdrContRangeDestroy(HttpHdrContRange * range) +{ + assert(range); + memFree(range, MEM_HTTP_HDR_CONTENT_RANGE); +} + +HttpHdrContRange * +httpHdrContRangeDup(const HttpHdrContRange * range) +{ + HttpHdrContRange *dup; + assert(range); + dup = httpHdrContRangeCreate(); + *dup = *range; + return dup; +} + +void +httpHdrContRangePackInto(const HttpHdrContRange * range, Packer * p) +{ + assert(range && p); + httpHdrRangeRespSpecPackInto(&range->spec, p); + /* Ensure typecast is safe */ + assert (range->elength >= 0); + if (!known_spec((size_t)range->elength)) + packerPrintf(p, "/*"); + else + packerPrintf(p, "/%ld", (long int) range->elength); +} + +void +httpHdrContRangeSet(HttpHdrContRange * cr, HttpHdrRangeSpec spec, ssize_t ent_len) +{ + assert(cr && ent_len >= 0); + cr->spec = spec; + cr->elength = ent_len; +} --- squid/src/HttpHdrExtField.c Wed Feb 14 01:07:36 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,100 +0,0 @@ - -/* - * $Id: HttpHdrExtField.c,v 1.4 2001/01/12 08:20:32 hno Exp $ - * - * DEBUG: section 69 HTTP Header: Extension Field - * AUTHOR: Alex Rousskov - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid.h" - -/* local prototypes */ -static HttpHdrExtField *httpHdrExtFieldDoCreate(const char *name, int name_len, const char *value, int val_len); - - -/* implementation */ - -static HttpHdrExtField * -httpHdrExtFieldDoCreate(const char *name, int name_len, - const char *value, int value_len) -{ - HttpHdrExtField *f = xcalloc(1, sizeof(HttpHdrExtField)); - stringLimitInit(&f->name, name, name_len); - stringLimitInit(&f->value, value, value_len); - return f; -} - -HttpHdrExtField * -httpHdrExtFieldCreate(const char *name, const char *value) -{ - return httpHdrExtFieldDoCreate( - name, strlen(name), - value, strlen(value)); -} - -/* parses ext field; returns fresh ext field on success and NULL on failure */ -HttpHdrExtField * -httpHdrExtFieldParseCreate(const char *field_start, const char *field_end) -{ - /* note: name_start == field_start */ - const char *name_end = strchr(field_start, ':'); - const char *value_start; - /* note: value_end == field_end */ - - if (!name_end || name_end <= field_start || name_end > field_end) - return NULL; - - value_start = name_end + 1; /* skip ':' */ - /* skip white space */ - while (value_start < field_end && xisspace(*value_start)) - value_start++; - - return httpHdrExtFieldDoCreate( - field_start, name_end - field_start, - value_start, field_end - value_start); -} - -void -httpHdrExtFieldDestroy(HttpHdrExtField * f) -{ - assert(f); - stringClean(&f->name); - stringClean(&f->value); - xfree(f); -} - -HttpHdrExtField * -httpHdrExtFieldDup(HttpHdrExtField * f) -{ - assert(f); - return httpHdrExtFieldDoCreate( - strBuf(f->name), strLen(f->name), - strBuf(f->value), strLen(f->value)); -} --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/HttpHdrExtField.cc Wed Feb 14 01:07:36 2007 @@ -0,0 +1,100 @@ + +/* + * $Id: HttpHdrExtField.cc,v 1.1.2.1 2002/10/08 11:11:34 rbcollins Exp $ + * + * DEBUG: section 69 HTTP Header: Extension Field + * AUTHOR: Alex Rousskov + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + +/* local prototypes */ +static HttpHdrExtField *httpHdrExtFieldDoCreate(const char *name, int name_len, const char *value, int val_len); + + +/* implementation */ + +static HttpHdrExtField * +httpHdrExtFieldDoCreate(const char *name, int name_len, + const char *value, int value_len) +{ + HttpHdrExtField *f = xcalloc(1, sizeof(HttpHdrExtField)); + stringLimitInit(&f->name, name, name_len); + stringLimitInit(&f->value, value, value_len); + return f; +} + +HttpHdrExtField * +httpHdrExtFieldCreate(const char *name, const char *value) +{ + return httpHdrExtFieldDoCreate( + name, strlen(name), + value, strlen(value)); +} + +/* parses ext field; returns fresh ext field on success and NULL on failure */ +HttpHdrExtField * +httpHdrExtFieldParseCreate(const char *field_start, const char *field_end) +{ + /* note: name_start == field_start */ + const char *name_end = strchr(field_start, ':'); + const char *value_start; + /* note: value_end == field_end */ + + if (!name_end || name_end <= field_start || name_end > field_end) + return NULL; + + value_start = name_end + 1; /* skip ':' */ + /* skip white space */ + while (value_start < field_end && xisspace(*value_start)) + value_start++; + + return httpHdrExtFieldDoCreate( + field_start, name_end - field_start, + value_start, field_end - value_start); +} + +void +httpHdrExtFieldDestroy(HttpHdrExtField * f) +{ + assert(f); + stringClean(&f->name); + stringClean(&f->value); + xfree(f); +} + +HttpHdrExtField * +httpHdrExtFieldDup(HttpHdrExtField * f) +{ + assert(f); + return httpHdrExtFieldDoCreate( + strBuf(f->name), strLen(f->name), + strBuf(f->value), strLen(f->value)); +} --- squid/src/HttpHdrRange.c Wed Feb 14 01:07:36 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,493 +0,0 @@ - -/* - * $Id: HttpHdrRange.c,v 1.7.44.1 2002/10/04 07:07:19 rbcollins Exp $ - * - * DEBUG: section 64 HTTP Range Header - * AUTHOR: Alex Rousskov - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid.h" -#include "Store.h" - -/* - * Currently only byte ranges are supported - * - * Essentially, there are three types of byte ranges: - * - * 1) first-byte-pos "-" last-byte-pos // range - * 2) first-byte-pos "-" // trailer - * 3) "-" suffix-length // suffix (last length bytes) - * - * - * When Range field is parsed, we have no clue about the content - * length of the document. Thus, we simply code an "absent" part - * using range_spec_unknown constant. - * - * Note: when response length becomes known, we convert any range - * spec into type one above. (Canonization process). - */ - - -/* local constants */ -#define range_spec_unknown ((ssize_t)-1) - -/* local routines */ -#define known_spec(s) ((s) != range_spec_unknown) -#define size_min(a,b) ((a) <= (b) ? (a) : (b)) -#define size_diff(a,b) ((a) >= (b) ? ((a)-(b)) : 0) -static HttpHdrRangeSpec *httpHdrRangeSpecDup(const HttpHdrRangeSpec * spec); -static int httpHdrRangeSpecCanonize(HttpHdrRangeSpec * spec, size_t clen); -static void httpHdrRangeSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p); - -/* globals */ -static int RangeParsedCount = 0; - -/* - * Range-Spec - */ - -static HttpHdrRangeSpec * -httpHdrRangeSpecCreate(void) -{ - return memAllocate(MEM_HTTP_HDR_RANGE_SPEC); -} - -/* parses range-spec and returns new object on success */ -static HttpHdrRangeSpec * -httpHdrRangeSpecParseCreate(const char *field, int flen) -{ - HttpHdrRangeSpec spec = - {range_spec_unknown, range_spec_unknown}; - const char *p; - if (flen < 2) - return NULL; - /* is it a suffix-byte-range-spec ? */ - if (*field == '-') { - if (!httpHeaderParseSize(field + 1, &spec.length)) - return NULL; - } else - /* must have a '-' somewhere in _this_ field */ - if (!((p = strchr(field, '-')) || (p - field >= flen))) { - debug(64, 2) ("ignoring invalid (missing '-') range-spec near: '%s'\n", field); - return NULL; - } else { - if (!httpHeaderParseSize(field, &spec.offset)) - return NULL; - p++; - /* do we have last-pos ? */ - if (p - field < flen) { - ssize_t last_pos; - if (!httpHeaderParseSize(p, &last_pos)) - return NULL; - spec.length = size_diff(last_pos + 1, spec.offset); - } - } - /* we managed to parse, check if the result makes sence */ - if (known_spec(spec.length) && !spec.length) { - debug(64, 2) ("ignoring invalid (zero length) range-spec near: '%s'\n", field); - return NULL; - } - return httpHdrRangeSpecDup(&spec); -} - -static void -httpHdrRangeSpecDestroy(HttpHdrRangeSpec * spec) -{ - memFree(spec, MEM_HTTP_HDR_RANGE_SPEC); -} - - -static HttpHdrRangeSpec * -httpHdrRangeSpecDup(const HttpHdrRangeSpec * spec) -{ - HttpHdrRangeSpec *dup = httpHdrRangeSpecCreate(); - dup->offset = spec->offset; - dup->length = spec->length; - return dup; -} - -static void -httpHdrRangeSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p) -{ - if (!known_spec(spec->offset)) /* suffix */ - packerPrintf(p, "-%ld", (long int) spec->length); - else if (!known_spec(spec->length)) /* trailer */ - packerPrintf(p, "%ld-", (long int) spec->offset); - else /* range */ - packerPrintf(p, "%ld-%ld", - (long int) spec->offset, (long int) spec->offset + spec->length - 1); -} - -/* fills "absent" positions in range specification based on response body size - * returns true if the range is still valid - * range is valid if its intersection with [0,length-1] is not empty - */ -static int -httpHdrRangeSpecCanonize(HttpHdrRangeSpec * spec, size_t clen) -{ - debug(64, 5) ("httpHdrRangeSpecCanonize: have: [%ld, %ld) len: %ld\n", - (long int) spec->offset, (long int) spec->offset + spec->length, (long int) spec->length); - if (!known_spec(spec->offset)) /* suffix */ - spec->offset = size_diff(clen, spec->length); - else if (!known_spec(spec->length)) /* trailer */ - spec->length = size_diff(clen, spec->offset); - /* we have a "range" now, adjust length if needed */ - assert(known_spec(spec->length)); - assert(known_spec(spec->offset)); - spec->length = size_min(size_diff(clen, spec->offset), spec->length); - /* check range validity */ - debug(64, 5) ("httpHdrRangeSpecCanonize: done: [%ld, %ld) len: %ld\n", - (long int) spec->offset, (long int) spec->offset + (long int) spec->length, (long int) spec->length); - return spec->length > 0; -} - -/* merges recepient with donor if possible; returns true on success - * both specs must be canonized prior to merger, of course */ -static int -httpHdrRangeSpecMergeWith(HttpHdrRangeSpec * recep, const HttpHdrRangeSpec * donor) -{ - int merged = 0; -#if MERGING_BREAKS_NOTHING - /* Note: this code works, but some clients may not like its effects */ - size_t rhs = recep->offset + recep->length; /* no -1 ! */ - const size_t donor_rhs = donor->offset + donor->length; /* no -1 ! */ - assert(known_spec(recep->offset)); - assert(known_spec(donor->offset)); - assert(recep->length > 0); - assert(donor->length > 0); - /* do we have a left hand side overlap? */ - if (donor->offset < recep->offset && recep->offset <= donor_rhs) { - recep->offset = donor->offset; /* decrease left offset */ - merged = 1; - } - /* do we have a right hand side overlap? */ - if (donor->offset <= rhs && rhs < donor_rhs) { - rhs = donor_rhs; /* increase right offset */ - merged = 1; - } - /* adjust length if offsets have been changed */ - if (merged) { - assert(rhs > recep->offset); - recep->length = rhs - recep->offset; - } else { - /* does recepient contain donor? */ - merged = - recep->offset <= donor->offset && donor->offset < rhs; - } -#endif - return merged; -} - -/* - * Range - */ - -static HttpHdrRange * -httpHdrRangeCreate(void) -{ - HttpHdrRange *r = memAllocate(MEM_HTTP_HDR_RANGE); - stackInit(&r->specs); - return r; -} - -HttpHdrRange * -httpHdrRangeParseCreate(const String * str) -{ - HttpHdrRange *r = httpHdrRangeCreate(); - if (!httpHdrRangeParseInit(r, str)) { - httpHdrRangeDestroy(r); - r = NULL; - } - return r; -} - -/* returns true if ranges are valid; inits HttpHdrRange */ -int -httpHdrRangeParseInit(HttpHdrRange * range, const String * str) -{ - const char *item; - const char *pos = NULL; - int ilen; - int count = 0; - assert(range && str); - RangeParsedCount++; - debug(64, 8) ("parsing range field: '%s'\n", strBuf(*str)); - /* check range type */ - if (strNCaseCmp(*str, "bytes=", 6)) - return 0; - /* skip "bytes="; hack! */ - pos = strBuf(*str) + 5; - /* iterate through comma separated list */ - while (strListGetItem(str, ',', &item, &ilen, &pos)) { - HttpHdrRangeSpec *spec = httpHdrRangeSpecParseCreate(item, ilen); - /* - * HTTP/1.1 draft says we must ignore the whole header field if one spec - * is invalid. However, RFC 2068 just says that we must ignore that spec. - */ - if (spec) - stackPush(&range->specs, spec); - count++; - } - debug(64, 8) ("parsed range range count: %d\n", range->specs.count); - return range->specs.count; -} - -void -httpHdrRangeDestroy(HttpHdrRange * range) -{ - assert(range); - while (range->specs.count) - httpHdrRangeSpecDestroy(stackPop(&range->specs)); - stackClean(&range->specs); - memFree(range, MEM_HTTP_HDR_RANGE); -} - -HttpHdrRange * -httpHdrRangeDup(const HttpHdrRange * range) -{ - HttpHdrRange *dup; - int i; - assert(range); - dup = httpHdrRangeCreate(); - stackPrePush(&dup->specs, range->specs.count); - for (i = 0; i < range->specs.count; i++) - stackPush(&dup->specs, httpHdrRangeSpecDup(range->specs.items[i])); - assert(range->specs.count == dup->specs.count); - return dup; -} - -void -httpHdrRangePackInto(const HttpHdrRange * range, Packer * p) -{ - HttpHdrRangePos pos = HttpHdrRangeInitPos; - const HttpHdrRangeSpec *spec; - assert(range); - while ((spec = httpHdrRangeGetSpec(range, &pos))) { - if (pos != HttpHdrRangeInitPos) - packerAppend(p, ",", 1); - httpHdrRangeSpecPackInto(spec, p); - } -} - -/* - * canonizes all range specs within a set preserving the order - * returns true if the set is valid after canonization; - * the set is valid if - * - all range specs are valid and - * - there is at least one range spec - */ -int -httpHdrRangeCanonize(HttpHdrRange * range, ssize_t clen) -{ - int i; - HttpHdrRangeSpec *spec; - HttpHdrRangePos pos = HttpHdrRangeInitPos; - Stack goods; - assert(range); - assert(clen >= 0); - stackInit(&goods); - debug(64, 3) ("httpHdrRangeCanonize: started with %d specs, clen: %ld\n", range->specs.count, (long int) clen); - - /* canonize each entry and destroy bad ones if any */ - while ((spec = httpHdrRangeGetSpec(range, &pos))) { - if (httpHdrRangeSpecCanonize(spec, clen)) - stackPush(&goods, spec); - else - httpHdrRangeSpecDestroy(spec); - } - debug(64, 3) ("httpHdrRangeCanonize: found %d bad specs\n", - range->specs.count - goods.count); - /* reset old array */ - stackClean(&range->specs); - stackInit(&range->specs); - spec = NULL; - /* merge specs: - * take one spec from "goods" and merge it with specs from - * "range->specs" (if any) until there is no overlap */ - for (i = 0; i < goods.count;) { - HttpHdrRangeSpec *prev_spec = stackTop(&range->specs); - spec = goods.items[i]; - if (prev_spec) { - if (httpHdrRangeSpecMergeWith(spec, prev_spec)) { - /* merged with current so get rid of the prev one */ - assert(prev_spec == stackPop(&range->specs)); - httpHdrRangeSpecDestroy(prev_spec); - continue; /* re-iterate */ - } - } - stackPush(&range->specs, spec); - spec = NULL; - i++; /* progress */ - } - if (spec) /* last "merge" may not be pushed yet */ - stackPush(&range->specs, spec); - debug(64, 3) ("httpHdrRangeCanonize: had %d specs, merged %d specs\n", - goods.count, goods.count - range->specs.count); - debug(64, 3) ("httpHdrRangeCanonize: finished with %d specs\n", - range->specs.count); - stackClean(&goods); - return range->specs.count > 0; -} - -/* searches for next range, returns true if found */ -HttpHdrRangeSpec * -httpHdrRangeGetSpec(const HttpHdrRange * range, HttpHdrRangePos * pos) -{ - assert(range); - assert(pos && *pos >= -1 && *pos < range->specs.count); - (*pos)++; - if (*pos < range->specs.count) - return (HttpHdrRangeSpec *) range->specs.items[*pos]; - else - return NULL; -} - -/* hack: returns true if range specs are too "complex" for Squid to handle */ -/* requires that specs are "canonized" first! */ -int -httpHdrRangeIsComplex(const HttpHdrRange * range) -{ - HttpHdrRangePos pos = HttpHdrRangeInitPos; - const HttpHdrRangeSpec *spec; - size_t offset = 0; - assert(range); - /* check that all rangers are in "strong" order */ - while ((spec = httpHdrRangeGetSpec(range, &pos))) { - if (spec->offset < offset) - return 1; - offset = spec->offset + spec->length; - } - return 0; -} - -/* - * hack: returns true if range specs may be too "complex" when "canonized". - * see also: httpHdrRangeIsComplex. - */ -int -httpHdrRangeWillBeComplex(const HttpHdrRange * range) -{ - HttpHdrRangePos pos = HttpHdrRangeInitPos; - const HttpHdrRangeSpec *spec; - size_t offset = 0; - assert(range); - /* check that all rangers are in "strong" order, */ - /* as far as we can tell without the content length */ - while ((spec = httpHdrRangeGetSpec(range, &pos))) { - if (!known_spec(spec->offset)) /* ignore unknowns */ - continue; - if (spec->offset < offset) - return 1; - offset = spec->offset; - if (known_spec(spec->length)) /* avoid unknowns */ - offset += spec->length; - } - return 0; -} - -/* - * Returns lowest known offset in range spec(s), or range_spec_unknown - * this is used for size limiting - */ -ssize_t -httpHdrRangeFirstOffset(const HttpHdrRange * range) -{ - ssize_t offset = range_spec_unknown; - HttpHdrRangePos pos = HttpHdrRangeInitPos; - const HttpHdrRangeSpec *spec; - assert(range); - while ((spec = httpHdrRangeGetSpec(range, &pos))) { - if (spec->offset < offset || !known_spec(offset)) - offset = spec->offset; - } - return offset; -} - -/* - * Returns lowest offset in range spec(s), 0 if unknown. - * This is used for finding out where we need to start if all - * ranges are combined into one, for example FTP REST. - * Use 0 for size if unknown - */ -ssize_t -httpHdrRangeLowestOffset(const HttpHdrRange * range, ssize_t size) -{ - ssize_t offset = range_spec_unknown; - ssize_t current; - HttpHdrRangePos pos = HttpHdrRangeInitPos; - const HttpHdrRangeSpec *spec; - assert(range); - while ((spec = httpHdrRangeGetSpec(range, &pos))) { - current = spec->offset; - if (!known_spec(current)) { - if (spec->length > size || !known_spec(spec->length)) - return 0; /* Unknown. Assume start of file */ - current = size - spec->length; - } - if (current < offset || !known_spec(offset)) - offset = current; - } - return known_spec(offset) ? offset : 0; -} - - -/* generates a "unique" boundary string for multipart responses - * the caller is responsible for cleaning the string */ -String -httpHdrRangeBoundaryStr(clientHttpRequest * http) -{ - const char *key; - String b = StringNull; - assert(http); - stringAppend(&b, full_appname_string, strlen(full_appname_string)); - stringAppend(&b, ":", 1); - key = storeKeyText(http->entry->hash.key); - stringAppend(&b, key, strlen(key)); - return b; -} - -/* - * Return true if the first range offset is larger than the configured - * limit. - */ -int -httpHdrRangeOffsetLimit(HttpHdrRange * range) -{ - if (NULL == range) - /* not a range request */ - return 0; - if (-1 == Config.rangeOffsetLimit) - /* disabled */ - return 0; - if (Config.rangeOffsetLimit >= httpHdrRangeFirstOffset(range)) - /* below the limit */ - return 0; - return 1; -} --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/HttpHdrRange.cc Wed Feb 14 01:07:36 2007 @@ -0,0 +1,499 @@ + +/* + * $Id: HttpHdrRange.cc,v 1.1.2.1 2002/10/08 11:11:34 rbcollins Exp $ + * + * DEBUG: section 64 HTTP Range Header + * AUTHOR: Alex Rousskov + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "Store.h" + +/* + * Currently only byte ranges are supported + * + * Essentially, there are three types of byte ranges: + * + * 1) first-byte-pos "-" last-byte-pos // range + * 2) first-byte-pos "-" // trailer + * 3) "-" suffix-length // suffix (last length bytes) + * + * + * When Range field is parsed, we have no clue about the content + * length of the document. Thus, we simply code an "absent" part + * using range_spec_unknown constant. + * + * Note: when response length becomes known, we convert any range + * spec into type one above. (Canonization process). + */ + + +/* local constants */ +#define range_spec_unknown ((ssize_t)-1) + +/* local routines */ +#define known_spec(s) ((s) != range_spec_unknown) +#define size_min(a,b) ((a) <= (b) ? (a) : (b)) +#define size_diff(a,b) ((a) >= (b) ? ((a)-(b)) : 0) +static HttpHdrRangeSpec *httpHdrRangeSpecDup(const HttpHdrRangeSpec * spec); +static int httpHdrRangeSpecCanonize(HttpHdrRangeSpec * spec, size_t clen); +static void httpHdrRangeSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p); + +/* globals */ +static int RangeParsedCount = 0; + +/* + * Range-Spec + */ + +static HttpHdrRangeSpec * +httpHdrRangeSpecCreate(void) +{ + return (HttpHdrRangeSpec *)memAllocate(MEM_HTTP_HDR_RANGE_SPEC); +} + +/* parses range-spec and returns new object on success */ +static HttpHdrRangeSpec * +httpHdrRangeSpecParseCreate(const char *field, int flen) +{ + HttpHdrRangeSpec spec = + {range_spec_unknown, range_spec_unknown}; + const char *p; + if (flen < 2) + return NULL; + /* is it a suffix-byte-range-spec ? */ + if (*field == '-') { + if (!httpHeaderParseSize(field + 1, &spec.length)) + return NULL; + } else + /* must have a '-' somewhere in _this_ field */ + if (!((p = strchr(field, '-')) || (p - field >= flen))) { + debug(64, 2) ("ignoring invalid (missing '-') range-spec near: '%s'\n", field); + return NULL; + } else { + if (!httpHeaderParseSize(field, &spec.offset)) + return NULL; + p++; + /* do we have last-pos ? */ + if (p - field < flen) { + ssize_t last_pos; + if (!httpHeaderParseSize(p, &last_pos)) + return NULL; + spec.length = size_diff(last_pos + 1, spec.offset); + } + } + /* we managed to parse, check if the result makes sence */ + if (known_spec(spec.length) && !spec.length) { + debug(64, 2) ("ignoring invalid (zero length) range-spec near: '%s'\n", field); + return NULL; + } + return httpHdrRangeSpecDup(&spec); +} + +static void +httpHdrRangeSpecDestroy(HttpHdrRangeSpec * spec) +{ + memFree(spec, MEM_HTTP_HDR_RANGE_SPEC); +} + + +static HttpHdrRangeSpec * +httpHdrRangeSpecDup(const HttpHdrRangeSpec * spec) +{ + HttpHdrRangeSpec *dup = httpHdrRangeSpecCreate(); + dup->offset = spec->offset; + dup->length = spec->length; + return dup; +} + +static void +httpHdrRangeSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p) +{ + if (!known_spec(spec->offset)) /* suffix */ + packerPrintf(p, "-%ld", (long int) spec->length); + else if (!known_spec(spec->length)) /* trailer */ + packerPrintf(p, "%ld-", (long int) spec->offset); + else /* range */ + packerPrintf(p, "%ld-%ld", + (long int) spec->offset, (long int) spec->offset + spec->length - 1); +} + +/* fills "absent" positions in range specification based on response body size + * returns true if the range is still valid + * range is valid if its intersection with [0,length-1] is not empty + */ +static int +httpHdrRangeSpecCanonize(HttpHdrRangeSpec * spec, size_t clen) +{ + debug(64, 5) ("httpHdrRangeSpecCanonize: have: [%ld, %ld) len: %ld\n", + (long int) spec->offset, (long int) spec->offset + spec->length, (long int) spec->length); + /* ensure the type casts are safe */ + assert (spec->length >= 0); + assert (spec->offset >= 0); + if (!known_spec(spec->offset)) /* suffix */ + spec->offset = size_diff(clen, (size_t)spec->length); + else if (!known_spec(spec->length)) /* trailer */ + spec->length = size_diff(clen, (size_t)spec->offset); + /* we have a "range" now, adjust length if needed */ + assert(known_spec(spec->length)); + assert(known_spec(spec->offset)); + spec->length = size_min(size_diff(clen, (size_t)spec->offset), (size_t)spec->length); + /* check range validity */ + debug(64, 5) ("httpHdrRangeSpecCanonize: done: [%ld, %ld) len: %ld\n", + (long int) spec->offset, (long int) spec->offset + (long int) spec->length, (long int) spec->length); + return spec->length > 0; +} + +/* merges recepient with donor if possible; returns true on success + * both specs must be canonized prior to merger, of course */ +static int +httpHdrRangeSpecMergeWith(HttpHdrRangeSpec * recep, const HttpHdrRangeSpec * donor) +{ + int merged = 0; +#if MERGING_BREAKS_NOTHING + /* Note: this code works, but some clients may not like its effects */ + size_t rhs = recep->offset + recep->length; /* no -1 ! */ + const size_t donor_rhs = donor->offset + donor->length; /* no -1 ! */ + assert(known_spec(recep->offset)); + assert(known_spec(donor->offset)); + assert(recep->length > 0); + assert(donor->length > 0); + /* do we have a left hand side overlap? */ + if (donor->offset < recep->offset && recep->offset <= donor_rhs) { + recep->offset = donor->offset; /* decrease left offset */ + merged = 1; + } + /* do we have a right hand side overlap? */ + if (donor->offset <= rhs && rhs < donor_rhs) { + rhs = donor_rhs; /* increase right offset */ + merged = 1; + } + /* adjust length if offsets have been changed */ + if (merged) { + assert(rhs > recep->offset); + recep->length = rhs - recep->offset; + } else { + /* does recepient contain donor? */ + merged = + recep->offset <= donor->offset && donor->offset < rhs; + } +#endif + return merged; +} + +/* + * Range + */ + +static HttpHdrRange * +httpHdrRangeCreate(void) +{ + HttpHdrRange *r = (HttpHdrRange *)memAllocate(MEM_HTTP_HDR_RANGE); + stackInit(&r->specs); + return r; +} + +HttpHdrRange * +httpHdrRangeParseCreate(const String * str) +{ + HttpHdrRange *r = httpHdrRangeCreate(); + if (!httpHdrRangeParseInit(r, str)) { + httpHdrRangeDestroy(r); + r = NULL; + } + return r; +} + +/* returns true if ranges are valid; inits HttpHdrRange */ +int +httpHdrRangeParseInit(HttpHdrRange * range, const String * str) +{ + const char *item; + const char *pos = NULL; + int ilen; + int count = 0; + assert(range && str); + RangeParsedCount++; + debug(64, 8) ("parsing range field: '%s'\n", strBuf(*str)); + /* check range type */ + if (strNCaseCmp(*str, "bytes=", 6)) + return 0; + /* skip "bytes="; hack! */ + pos = strBuf(*str) + 5; + /* iterate through comma separated list */ + while (strListGetItem(str, ',', &item, &ilen, &pos)) { + HttpHdrRangeSpec *spec = httpHdrRangeSpecParseCreate(item, ilen); + /* + * HTTP/1.1 draft says we must ignore the whole header field if one spec + * is invalid. However, RFC 2068 just says that we must ignore that spec. + */ + if (spec) + stackPush(&range->specs, spec); + count++; + } + debug(64, 8) ("parsed range range count: %d\n", range->specs.count); + return range->specs.count; +} + +void +httpHdrRangeDestroy(HttpHdrRange * range) +{ + assert(range); + while (range->specs.count) + httpHdrRangeSpecDestroy((HttpHdrRangeSpec *)stackPop(&range->specs)); + stackClean(&range->specs); + memFree(range, MEM_HTTP_HDR_RANGE); +} + +HttpHdrRange * +httpHdrRangeDup(const HttpHdrRange * range) +{ + HttpHdrRange *dup; + int i; + assert(range); + dup = httpHdrRangeCreate(); + stackPrePush(&dup->specs, range->specs.count); + for (i = 0; i < range->specs.count; i++) + stackPush(&dup->specs, httpHdrRangeSpecDup((HttpHdrRangeSpec *)range->specs.items[i])); + assert(range->specs.count == dup->specs.count); + return dup; +} + +void +httpHdrRangePackInto(const HttpHdrRange * range, Packer * p) +{ + HttpHdrRangePos pos = HttpHdrRangeInitPos; + const HttpHdrRangeSpec *spec; + assert(range); + while ((spec = httpHdrRangeGetSpec(range, &pos))) { + if (pos != HttpHdrRangeInitPos) + packerAppend(p, ",", 1); + httpHdrRangeSpecPackInto(spec, p); + } +} + +/* + * canonizes all range specs within a set preserving the order + * returns true if the set is valid after canonization; + * the set is valid if + * - all range specs are valid and + * - there is at least one range spec + */ +int +httpHdrRangeCanonize(HttpHdrRange * range, ssize_t clen) +{ + int i; + HttpHdrRangeSpec *spec; + HttpHdrRangePos pos = HttpHdrRangeInitPos; + Stack goods; + assert(range); + assert(clen >= 0); + stackInit(&goods); + debug(64, 3) ("httpHdrRangeCanonize: started with %d specs, clen: %ld\n", range->specs.count, (long int) clen); + + /* canonize each entry and destroy bad ones if any */ + while ((spec = httpHdrRangeGetSpec(range, &pos))) { + if (httpHdrRangeSpecCanonize(spec, clen)) + stackPush(&goods, spec); + else + httpHdrRangeSpecDestroy(spec); + } + debug(64, 3) ("httpHdrRangeCanonize: found %d bad specs\n", + range->specs.count - goods.count); + /* reset old array */ + stackClean(&range->specs); + stackInit(&range->specs); + spec = NULL; + /* merge specs: + * take one spec from "goods" and merge it with specs from + * "range->specs" (if any) until there is no overlap */ + for (i = 0; i < goods.count;) { + HttpHdrRangeSpec *prev_spec = (HttpHdrRangeSpec *)stackTop(&range->specs); + spec = (HttpHdrRangeSpec *)goods.items[i]; + if (prev_spec) { + if (httpHdrRangeSpecMergeWith(spec, prev_spec)) { + /* merged with current so get rid of the prev one */ + assert(prev_spec == stackPop(&range->specs)); + httpHdrRangeSpecDestroy(prev_spec); + continue; /* re-iterate */ + } + } + stackPush(&range->specs, spec); + spec = NULL; + i++; /* progress */ + } + if (spec) /* last "merge" may not be pushed yet */ + stackPush(&range->specs, spec); + debug(64, 3) ("httpHdrRangeCanonize: had %d specs, merged %d specs\n", + goods.count, goods.count - range->specs.count); + debug(64, 3) ("httpHdrRangeCanonize: finished with %d specs\n", + range->specs.count); + stackClean(&goods); + return range->specs.count > 0; +} + +/* searches for next range, returns true if found */ +HttpHdrRangeSpec * +httpHdrRangeGetSpec(const HttpHdrRange * range, HttpHdrRangePos * pos) +{ + assert(range); + assert(pos && *pos >= -1 && *pos < range->specs.count); + (*pos)++; + if (*pos < range->specs.count) + return (HttpHdrRangeSpec *) range->specs.items[*pos]; + else + return NULL; +} + +/* hack: returns true if range specs are too "complex" for Squid to handle */ +/* requires that specs are "canonized" first! */ +int +httpHdrRangeIsComplex(const HttpHdrRange * range) +{ + HttpHdrRangePos pos = HttpHdrRangeInitPos; + const HttpHdrRangeSpec *spec; + size_t offset = 0; + assert(range); + /* check that all rangers are in "strong" order */ + while ((spec = httpHdrRangeGetSpec(range, &pos))) { + /* Ensure typecasts is safe */ + assert (spec->offset >= 0); + if ((unsigned int)spec->offset < offset) + return 1; + offset = spec->offset + spec->length; + } + return 0; +} + +/* + * hack: returns true if range specs may be too "complex" when "canonized". + * see also: httpHdrRangeIsComplex. + */ +int +httpHdrRangeWillBeComplex(const HttpHdrRange * range) +{ + HttpHdrRangePos pos = HttpHdrRangeInitPos; + const HttpHdrRangeSpec *spec; + size_t offset = 0; + assert(range); + /* check that all rangers are in "strong" order, */ + /* as far as we can tell without the content length */ + while ((spec = httpHdrRangeGetSpec(range, &pos))) { + if (!known_spec(spec->offset)) /* ignore unknowns */ + continue; + /* Ensure typecasts is safe */ + assert (spec->offset >= 0); + if ((size_t) spec->offset < offset) + return 1; + offset = spec->offset; + if (known_spec(spec->length)) /* avoid unknowns */ + offset += spec->length; + } + return 0; +} + +/* + * Returns lowest known offset in range spec(s), or range_spec_unknown + * this is used for size limiting + */ +ssize_t +httpHdrRangeFirstOffset(const HttpHdrRange * range) +{ + ssize_t offset = range_spec_unknown; + HttpHdrRangePos pos = HttpHdrRangeInitPos; + const HttpHdrRangeSpec *spec; + assert(range); + while ((spec = httpHdrRangeGetSpec(range, &pos))) { + if (spec->offset < offset || !known_spec(offset)) + offset = spec->offset; + } + return offset; +} + +/* + * Returns lowest offset in range spec(s), 0 if unknown. + * This is used for finding out where we need to start if all + * ranges are combined into one, for example FTP REST. + * Use 0 for size if unknown + */ +ssize_t +httpHdrRangeLowestOffset(const HttpHdrRange * range, ssize_t size) +{ + ssize_t offset = range_spec_unknown; + ssize_t current; + HttpHdrRangePos pos = HttpHdrRangeInitPos; + const HttpHdrRangeSpec *spec; + assert(range); + while ((spec = httpHdrRangeGetSpec(range, &pos))) { + current = spec->offset; + if (!known_spec(current)) { + if (spec->length > size || !known_spec(spec->length)) + return 0; /* Unknown. Assume start of file */ + current = size - spec->length; + } + if (current < offset || !known_spec(offset)) + offset = current; + } + return known_spec(offset) ? offset : 0; +} + +/* generates a "unique" boundary string for multipart responses + * the caller is responsible for cleaning the string */ +String +httpHdrRangeBoundaryStr(clientHttpRequest * http) +{ + const char *key; + String b = StringNull; + assert(http); + stringAppend(&b, full_appname_string, strlen(full_appname_string)); + stringAppend(&b, ":", 1); + key = storeKeyText((unsigned char *)http->entry->hash.key); + stringAppend(&b, key, strlen(key)); + return b; +} + +/* + * Return true if the first range offset is larger than the configured + * limit. + */ +int +httpHdrRangeOffsetLimit(HttpHdrRange * range) +{ + if (NULL == range) + /* not a range request */ + return 0; + if (-1 == (ssize_t)Config.rangeOffsetLimit) + /* disabled */ + return 0; + if ((ssize_t)Config.rangeOffsetLimit >= httpHdrRangeFirstOffset(range)) + /* below the limit */ + return 0; + return 1; +} --- squid/src/HttpHeader.c Wed Feb 14 01:07:36 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,1236 +0,0 @@ - -/* - * $Id: HttpHeader.c,v 1.16.10.3 2002/10/08 04:06:34 rbcollins Exp $ - * - * DEBUG: section 55 HTTP Header - * AUTHOR: Alex Rousskov - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid.h" -#include "Store.h" -#include "HttpHeader.h" - -/* - * On naming conventions: - * - * HTTP/1.1 defines message-header as - * - * message-header = field-name ":" [ field-value ] CRLF - * field-name = token - * field-value = *( field-content | LWS ) - * - * HTTP/1.1 does not give a name name a group of all message-headers in a message. - * Squid 1.1 seems to refer to that group _plus_ start-line as "headers". - * - * HttpHeader is an object that represents all message-headers in a message. - * HttpHeader does not manage start-line. - * - * HttpHeader is implemented as a collection of header "entries". - * An entry is a (field_id, field_name, field_value) triplet. - */ - - -/* - * local constants and vars - */ - -/* - * A table with major attributes for every known field. - * We calculate name lengths and reorganize this array on start up. - * After reorganization, field id can be used as an index to the table. - */ -static const HttpHeaderFieldAttrs HeadersAttrs[] = -{ - {"Accept", HDR_ACCEPT, ftStr}, - {"Accept-Charset", HDR_ACCEPT_CHARSET, ftStr}, - {"Accept-Encoding", HDR_ACCEPT_ENCODING, ftStr}, - {"Accept-Language", HDR_ACCEPT_LANGUAGE, ftStr}, - {"Accept-Ranges", HDR_ACCEPT_RANGES, ftStr}, - {"Age", HDR_AGE, ftInt}, - {"Allow", HDR_ALLOW, ftStr}, - {"Authorization", HDR_AUTHORIZATION, ftStr}, /* for now */ - {"Cache-Control", HDR_CACHE_CONTROL, ftPCc}, - {"Connection", HDR_CONNECTION, ftStr}, - {"Content-Base", HDR_CONTENT_BASE, ftStr}, - {"Content-Encoding", HDR_CONTENT_ENCODING, ftStr}, - {"Content-Language", HDR_CONTENT_LANGUAGE, ftStr}, - {"Content-Length", HDR_CONTENT_LENGTH, ftInt}, - {"Content-Location", HDR_CONTENT_LOCATION, ftStr}, - {"Content-MD5", HDR_CONTENT_MD5, ftStr}, /* for now */ - {"Content-Range", HDR_CONTENT_RANGE, ftPContRange}, - {"Content-Type", HDR_CONTENT_TYPE, ftStr}, - {"Cookie", HDR_COOKIE, ftStr}, - {"Date", HDR_DATE, ftDate_1123}, - {"ETag", HDR_ETAG, ftETag}, - {"Expires", HDR_EXPIRES, ftDate_1123}, - {"From", HDR_FROM, ftStr}, - {"Host", HDR_HOST, ftStr}, - {"If-Match", HDR_IF_MATCH, ftStr}, /* for now */ - {"If-Modified-Since", HDR_IF_MODIFIED_SINCE, ftDate_1123}, - {"If-None-Match", HDR_IF_NONE_MATCH, ftStr}, /* for now */ - {"If-Range", HDR_IF_RANGE, ftDate_1123_or_ETag}, - {"Last-Modified", HDR_LAST_MODIFIED, ftDate_1123}, - {"Link", HDR_LINK, ftStr}, - {"Location", HDR_LOCATION, ftStr}, - {"Max-Forwards", HDR_MAX_FORWARDS, ftInt}, - {"Mime-Version", HDR_MIME_VERSION, ftStr}, /* for now */ - {"Pragma", HDR_PRAGMA, ftStr}, - {"Proxy-Authenticate", HDR_PROXY_AUTHENTICATE, ftStr}, - {"Proxy-Authentication-Info", HDR_PROXY_AUTHENTICATION_INFO, ftStr}, - {"Proxy-Authorization", HDR_PROXY_AUTHORIZATION, ftStr}, - {"Proxy-Connection", HDR_PROXY_CONNECTION, ftStr}, - {"Public", HDR_PUBLIC, ftStr}, - {"Range", HDR_RANGE, ftPRange}, - {"Referer", HDR_REFERER, ftStr}, - {"Request-Range", HDR_REQUEST_RANGE, ftPRange}, /* usually matches HDR_RANGE */ - {"Retry-After", HDR_RETRY_AFTER, ftStr}, /* for now (ftDate_1123 or ftInt!) */ - {"Server", HDR_SERVER, ftStr}, - {"Set-Cookie", HDR_SET_COOKIE, ftStr}, - {"Title", HDR_TITLE, ftStr}, - {"Transfer-Encoding", HDR_TRANSFER_ENCODING, ftStr}, - {"Upgrade", HDR_UPGRADE, ftStr}, /* for now */ - {"User-Agent", HDR_USER_AGENT, ftStr}, - {"Vary", HDR_VARY, ftStr}, /* for now */ - {"Via", HDR_VIA, ftStr}, /* for now */ - {"Warning", HDR_WARNING, ftStr}, /* for now */ - {"WWW-Authenticate", HDR_WWW_AUTHENTICATE, ftStr}, - {"Authentication-Info", HDR_AUTHENTICATION_INFO, ftStr}, - {"X-Cache", HDR_X_CACHE, ftStr}, - {"X-Cache-Lookup", HDR_X_CACHE_LOOKUP, ftStr}, - {"X-Forwarded-For", HDR_X_FORWARDED_FOR, ftStr}, - {"X-Request-URI", HDR_X_REQUEST_URI, ftStr}, - {"X-Squid-Error", HDR_X_SQUID_ERROR, ftStr}, - {"Negotiate", HDR_NEGOTIATE, ftStr}, -#if X_ACCELERATOR_VARY - {"X-Accelerator-Vary", HDR_X_ACCELERATOR_VARY, ftStr}, -#endif - {"Other:", HDR_OTHER, ftStr} /* ':' will not allow matches */ -}; -static HttpHeaderFieldInfo *Headers = NULL; - -/* - * headers with field values defined as #(values) in HTTP/1.1 - * Headers that are currently not recognized, are commented out. - */ -static HttpHeaderMask ListHeadersMask; /* set run-time using ListHeadersArr */ -static http_hdr_type ListHeadersArr[] = -{ - HDR_ACCEPT, - HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE, - HDR_ACCEPT_RANGES, HDR_ALLOW, - HDR_CACHE_CONTROL, - HDR_CONTENT_ENCODING, - HDR_CONTENT_LANGUAGE, - HDR_CONNECTION, - HDR_IF_MATCH, HDR_IF_NONE_MATCH, - HDR_LINK, HDR_PRAGMA, - HDR_PROXY_CONNECTION, - HDR_TRANSFER_ENCODING, - HDR_UPGRADE, - HDR_VARY, - HDR_VIA, - /* HDR_WARNING, */ - HDR_WWW_AUTHENTICATE, - HDR_AUTHENTICATION_INFO, - HDR_PROXY_AUTHENTICATION_INFO, - /* HDR_EXPECT, HDR_TE, HDR_TRAILER */ -#if X_ACCELERATOR_VARY - HDR_X_ACCELERATOR_VARY, -#endif - HDR_X_FORWARDED_FOR -}; - -/* general-headers */ -static http_hdr_type GeneralHeadersArr[] = -{ - HDR_CACHE_CONTROL, HDR_CONNECTION, HDR_DATE, HDR_PRAGMA, - HDR_TRANSFER_ENCODING, - HDR_UPGRADE, - /* HDR_TRAILER, */ - HDR_VIA -}; - -/* entity-headers */ -static http_hdr_type EntityHeadersArr[] = -{ - HDR_ALLOW, HDR_CONTENT_BASE, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE, - HDR_CONTENT_LENGTH, HDR_CONTENT_LOCATION, HDR_CONTENT_MD5, - HDR_CONTENT_RANGE, HDR_CONTENT_TYPE, HDR_ETAG, HDR_EXPIRES, HDR_LAST_MODIFIED, HDR_LINK, - HDR_OTHER -}; - -static HttpHeaderMask ReplyHeadersMask; /* set run-time using ReplyHeaders */ -static http_hdr_type ReplyHeadersArr[] = -{ - HDR_ACCEPT, HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE, - HDR_ACCEPT_RANGES, HDR_AGE, - HDR_LOCATION, HDR_MAX_FORWARDS, - HDR_MIME_VERSION, HDR_PUBLIC, HDR_RETRY_AFTER, HDR_SERVER, HDR_SET_COOKIE, - HDR_VARY, - HDR_WARNING, HDR_PROXY_CONNECTION, HDR_X_CACHE, - HDR_X_CACHE_LOOKUP, - HDR_X_REQUEST_URI, -#if X_ACCELERATOR_VARY - HDR_X_ACCELERATOR_VARY, -#endif - HDR_X_SQUID_ERROR -}; - -static HttpHeaderMask RequestHeadersMask; /* set run-time using RequestHeaders */ -static http_hdr_type RequestHeadersArr[] = -{ - HDR_AUTHORIZATION, HDR_FROM, HDR_HOST, - HDR_IF_MATCH, HDR_IF_MODIFIED_SINCE, HDR_IF_NONE_MATCH, - HDR_IF_RANGE, HDR_MAX_FORWARDS, HDR_PROXY_CONNECTION, - HDR_PROXY_AUTHORIZATION, HDR_RANGE, HDR_REFERER, HDR_REQUEST_RANGE, - HDR_USER_AGENT, HDR_X_FORWARDED_FOR -}; - -/* header accounting */ -static HttpHeaderStat HttpHeaderStats[] = -{ - {"all"}, -#if USE_HTCP - {"HTCP reply"}, -#endif - {"request"}, - {"reply"} -}; -static int HttpHeaderStatCount = countof(HttpHeaderStats); - -static int HeaderEntryParsedCount = 0; - -/* - * local routines - */ - -#define assert_eid(id) assert((id) >= 0 && (id) < HDR_ENUM_END) - -static HttpHeaderEntry *httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value); -static void httpHeaderEntryDestroy(HttpHeaderEntry * e); -static HttpHeaderEntry *httpHeaderEntryParseCreate(const char *field_start, const char *field_end); -static void httpHeaderNoteParsedEntry(http_hdr_type id, String value, int error); - -static void httpHeaderStatInit(HttpHeaderStat * hs, const char *label); -static void httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e); - -/* - * Module initialization routines - */ - -void -httpHeaderInitModule(void) -{ - int i; - /* check that we have enough space for masks */ - assert(8 * sizeof(HttpHeaderMask) >= HDR_ENUM_END); - /* all headers must be described */ - assert(countof(HeadersAttrs) == HDR_ENUM_END); - if (!Headers) - Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END); - /* create masks */ - httpHeaderMaskInit(&ListHeadersMask, 0); - httpHeaderCalcMask(&ListHeadersMask, (const int *) ListHeadersArr, countof(ListHeadersArr)); - httpHeaderMaskInit(&ReplyHeadersMask, 0); - httpHeaderCalcMask(&ReplyHeadersMask, (const int *) ReplyHeadersArr, countof(ReplyHeadersArr)); - httpHeaderCalcMask(&ReplyHeadersMask, (const int *) GeneralHeadersArr, countof(GeneralHeadersArr)); - httpHeaderCalcMask(&ReplyHeadersMask, (const int *) EntityHeadersArr, countof(EntityHeadersArr)); - httpHeaderMaskInit(&RequestHeadersMask, 0); - httpHeaderCalcMask(&RequestHeadersMask, (const int *) RequestHeadersArr, countof(RequestHeadersArr)); - httpHeaderCalcMask(&RequestHeadersMask, (const int *) GeneralHeadersArr, countof(GeneralHeadersArr)); - httpHeaderCalcMask(&RequestHeadersMask, (const int *) EntityHeadersArr, countof(EntityHeadersArr)); - /* init header stats */ - assert(HttpHeaderStatCount == hoReply + 1); - for (i = 0; i < HttpHeaderStatCount; i++) - httpHeaderStatInit(HttpHeaderStats + i, HttpHeaderStats[i].label); - HttpHeaderStats[hoRequest].owner_mask = &RequestHeadersMask; - HttpHeaderStats[hoReply].owner_mask = &ReplyHeadersMask; -#if USE_HTCP - HttpHeaderStats[hoHtcpReply].owner_mask = &ReplyHeadersMask; -#endif - /* init dependent modules */ - httpHdrCcInitModule(); - /* register with cache manager */ - cachemgrRegister("http_headers", - "HTTP Header Statistics", httpHeaderStoreReport, 0, 1); -} - -void -httpHeaderCleanModule(void) -{ - httpHeaderDestroyFieldsInfo(Headers, HDR_ENUM_END); - Headers = NULL; - httpHdrCcCleanModule(); -} - -static void -httpHeaderStatInit(HttpHeaderStat * hs, const char *label) -{ - assert(hs); - assert(label); - memset(hs, 0, sizeof(HttpHeaderStat)); - hs->label = label; - statHistEnumInit(&hs->hdrUCountDistr, 32); /* not a real enum */ - statHistEnumInit(&hs->fieldTypeDistr, HDR_ENUM_END); - statHistEnumInit(&hs->ccTypeDistr, CC_ENUM_END); -} - -/* - * HttpHeader Implementation - */ - -void -httpHeaderInit(HttpHeader * hdr, http_hdr_owner_type owner) -{ - assert(hdr); - assert(owner > hoNone && owner <= hoReply); - debug(55, 7) ("init-ing hdr: %p owner: %d\n", hdr, owner); - memset(hdr, 0, sizeof(*hdr)); - hdr->owner = owner; - arrayInit(&hdr->entries); -} - -void -httpHeaderClean(HttpHeader * hdr) -{ - HttpHeaderPos pos = HttpHeaderInitPos; - HttpHeaderEntry *e; - - assert(hdr); - assert(hdr->owner > hoNone && hdr->owner <= hoReply); - debug(55, 7) ("cleaning hdr: %p owner: %d\n", hdr, hdr->owner); - - statHistCount(&HttpHeaderStats[hdr->owner].hdrUCountDistr, hdr->entries.count); - HttpHeaderStats[hdr->owner].destroyedCount++; - HttpHeaderStats[hdr->owner].busyDestroyedCount += hdr->entries.count > 0; - while ((e = httpHeaderGetEntry(hdr, &pos))) { - /* tmp hack to try to avoid coredumps */ - if (e->id < 0 || e->id >= HDR_ENUM_END) { - debug(55, 0) ("httpHeaderClean BUG: entry[%d] is invalid (%d). Ignored.\n", - (int) pos, e->id); - } else { - statHistCount(&HttpHeaderStats[hdr->owner].fieldTypeDistr, e->id); - /* yes, this destroy() leaves us in an incosistent state */ - httpHeaderEntryDestroy(e); - } - } - arrayClean(&hdr->entries); -} - -/* append entries (also see httpHeaderUpdate) */ -void -httpHeaderAppend(HttpHeader * dest, const HttpHeader * src) -{ - const HttpHeaderEntry *e; - HttpHeaderPos pos = HttpHeaderInitPos; - assert(src && dest); - assert(src != dest); - debug(55, 7) ("appending hdr: %p += %p\n", dest, src); - - while ((e = httpHeaderGetEntry(src, &pos))) { - httpHeaderAddEntry(dest, httpHeaderEntryClone(e)); - } -} - -/* use fresh entries to replace old ones */ -void -httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask) -{ - const HttpHeaderEntry *e; - HttpHeaderPos pos = HttpHeaderInitPos; - assert(old && fresh); - assert(old != fresh); - debug(55, 7) ("updating hdr: %p <- %p\n", old, fresh); - - while ((e = httpHeaderGetEntry(fresh, &pos))) { - /* deny bad guys (ok to check for HDR_OTHER) here */ - if (denied_mask && CBIT_TEST(*denied_mask, e->id)) - continue; - httpHeaderDelByName(old, strBuf(e->name)); - httpHeaderAddEntry(old, httpHeaderEntryClone(e)); - } -} - -/* just handy in parsing: resets and returns false */ -int -httpHeaderReset(HttpHeader * hdr) -{ - http_hdr_owner_type ho; - assert(hdr); - ho = hdr->owner; - httpHeaderClean(hdr); - httpHeaderInit(hdr, ho); - return 0; -} - -int -httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_end) -{ - const char *field_start = header_start; - HttpHeaderEntry *e; - - assert(hdr); - assert(header_start && header_end); - debug(55, 7) ("parsing hdr: (%p)\n%s\n", hdr, getStringPrefix(header_start, header_end)); - HttpHeaderStats[hdr->owner].parsedCount++; - /* commonn format headers are ":[ws]" lines delimited by */ - while (field_start < header_end) { - const char *field_end; - const char *field_ptr = field_start; - do { - field_end = field_ptr = field_ptr + strcspn(field_ptr, "\r\n"); - /* skip CRLF */ - if (*field_ptr == '\r') - field_ptr++; - if (*field_ptr == '\n') - field_ptr++; - } - while (*field_ptr == ' ' || *field_ptr == '\t'); - if (!*field_end || field_end > header_end) - return httpHeaderReset(hdr); /* missing */ - e = httpHeaderEntryParseCreate(field_start, field_end); - if (e != NULL) - httpHeaderAddEntry(hdr, e); - else - debug(55, 2) ("warning: ignoring unparseable http header field near '%s'\n", - getStringPrefix(field_start, field_end)); - field_start = field_end; - /* skip CRLF */ - if (*field_start == '\r') - field_start++; - if (*field_start == '\n') - field_start++; - } - return 1; /* even if no fields where found, it is a valid header */ -} - -/* packs all the entries using supplied packer */ -void -httpHeaderPackInto(const HttpHeader * hdr, Packer * p) -{ - HttpHeaderPos pos = HttpHeaderInitPos; - const HttpHeaderEntry *e; - assert(hdr && p); - debug(55, 7) ("packing hdr: (%p)\n", hdr); - /* pack all entries one by one */ - while ((e = httpHeaderGetEntry(hdr, &pos))) - httpHeaderEntryPackInto(e, p); -} - -/* returns next valid entry */ -HttpHeaderEntry * -httpHeaderGetEntry(const HttpHeader * hdr, HttpHeaderPos * pos) -{ - assert(hdr && pos); - assert(*pos >= HttpHeaderInitPos && *pos < hdr->entries.count); - for ((*pos)++; *pos < hdr->entries.count; (*pos)++) { - if (hdr->entries.items[*pos]) - return hdr->entries.items[*pos]; - } - return NULL; -} - -/* - * returns a pointer to a specified entry if any - * note that we return one entry so it does not make much sense to ask for - * "list" headers - */ -HttpHeaderEntry * -httpHeaderFindEntry(const HttpHeader * hdr, http_hdr_type id) -{ - HttpHeaderPos pos = HttpHeaderInitPos; - HttpHeaderEntry *e; - assert(hdr); - assert_eid(id); - assert(!CBIT_TEST(ListHeadersMask, id)); - - /* check mask first */ - if (!CBIT_TEST(hdr->mask, id)) - return NULL; - /* looks like we must have it, do linear search */ - while ((e = httpHeaderGetEntry(hdr, &pos))) { - if (e->id == id) - return e; - } - /* hm.. we thought it was there, but it was not found */ - assert(0); - return NULL; /* not reached */ -} - -/* - * same as httpHeaderFindEntry - */ -static HttpHeaderEntry * -httpHeaderFindLastEntry(const HttpHeader * hdr, http_hdr_type id) -{ - HttpHeaderPos pos = HttpHeaderInitPos; - HttpHeaderEntry *e; - HttpHeaderEntry *result = NULL; - assert(hdr); - assert_eid(id); - assert(!CBIT_TEST(ListHeadersMask, id)); - - /* check mask first */ - if (!CBIT_TEST(hdr->mask, id)) - return NULL; - /* looks like we must have it, do linear search */ - while ((e = httpHeaderGetEntry(hdr, &pos))) { - if (e->id == id) - result = e; - } - assert(result); /* must be there! */ - return result; -} - -/* - * deletes all fields with a given name if any, returns #fields deleted; - */ -int -httpHeaderDelByName(HttpHeader * hdr, const char *name) -{ - int count = 0; - HttpHeaderPos pos = HttpHeaderInitPos; - HttpHeaderEntry *e; - httpHeaderMaskInit(&hdr->mask, 0); /* temporal inconsistency */ - debug(55, 7) ("deleting '%s' fields in hdr %p\n", name, hdr); - while ((e = httpHeaderGetEntry(hdr, &pos))) { - if (!strCaseCmp(e->name, name)) { - httpHeaderDelAt(hdr, pos); - count++; - } else - CBIT_SET(hdr->mask, e->id); - } - return count; -} - -/* deletes all entries with a given id, returns the #entries deleted */ -int -httpHeaderDelById(HttpHeader * hdr, http_hdr_type id) -{ - int count = 0; - HttpHeaderPos pos = HttpHeaderInitPos; - HttpHeaderEntry *e; - debug(55, 8) ("%p del-by-id %d\n", hdr, id); - assert(hdr); - assert_eid(id); - assert_eid(id != HDR_OTHER); /* does not make sense */ - if (!CBIT_TEST(hdr->mask, id)) - return 0; - while ((e = httpHeaderGetEntry(hdr, &pos))) { - if (e->id == id) { - httpHeaderDelAt(hdr, pos); - count++; - } - } - CBIT_CLR(hdr->mask, id); - assert(count); - return count; -} - -/* - * deletes an entry at pos and leaves a gap; leaving a gap makes it - * possible to iterate(search) and delete fields at the same time - */ -void -httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos) -{ - HttpHeaderEntry *e; - assert(pos >= HttpHeaderInitPos && pos < hdr->entries.count); - e = hdr->entries.items[pos]; - hdr->entries.items[pos] = NULL; - /* decrement header length, allow for ": " and crlf */ - hdr->len -= strLen(e->name) + 2 + strLen(e->value) + 2; - assert(hdr->len >= 0); - httpHeaderEntryDestroy(e); -} - - -/* appends an entry; - * does not call httpHeaderEntryClone() so one should not reuse "*e" - */ -void -httpHeaderAddEntry(HttpHeader * hdr, HttpHeaderEntry * e) -{ - assert(hdr && e); - assert_eid(e->id); - - debug(55, 7) ("%p adding entry: %d at %d\n", - hdr, e->id, hdr->entries.count); - if (CBIT_TEST(hdr->mask, e->id)) - Headers[e->id].stat.repCount++; - else - CBIT_SET(hdr->mask, e->id); - arrayAppend(&hdr->entries, e); - /* increment header length, allow for ": " and crlf */ - hdr->len += strLen(e->name) + 2 + strLen(e->value) + 2; -} - -/* return a list of entries with the same id separated by ',' and ws */ -String -httpHeaderGetList(const HttpHeader * hdr, http_hdr_type id) -{ - String s = StringNull; - HttpHeaderEntry *e; - HttpHeaderPos pos = HttpHeaderInitPos; - debug(55, 6) ("%p: joining for id %d\n", hdr, id); - /* only fields from ListHeaders array can be "listed" */ - assert(CBIT_TEST(ListHeadersMask, id)); - if (!CBIT_TEST(hdr->mask, id)) - return s; - while ((e = httpHeaderGetEntry(hdr, &pos))) { - if (e->id == id) - strListAdd(&s, strBuf(e->value), ','); - } - /* - * note: we might get an empty (len==0) string if there was an "empty" - * header; we must not get a NULL string though. - */ - assert(strBuf(s)); - /* temporary warning: remove it! @?@ @?@ @?@ */ - if (!strLen(s)) - debug(55, 3) ("empty list header: %s (%d)\n", strBuf(Headers[id].name), id); - debug(55, 6) ("%p: joined for id %d: %s\n", hdr, id, strBuf(s)); - return s; -} - -/* return a string or list of entries with the same id separated by ',' and ws */ -String -httpHeaderGetStrOrList(const HttpHeader * hdr, http_hdr_type id) -{ - HttpHeaderEntry *e; - - if (CBIT_TEST(ListHeadersMask, id)) - return httpHeaderGetList(hdr, id); - if ((e = httpHeaderFindEntry(hdr, id))) { - String s; - stringInit(&s, strBuf(e->value)); - return s; - } - return StringNull; -} - -/* - * Returns the value of the specified header. - */ -String -httpHeaderGetByName(const HttpHeader * hdr, const char *name) -{ - http_hdr_type id; - HttpHeaderPos pos = HttpHeaderInitPos; - HttpHeaderEntry *e; - String result = StringNull; - - assert(hdr); - assert(name); - - /* First try the quick path */ - id = httpHeaderIdByNameDef(name, strlen(name)); - if (id != -1) - return httpHeaderGetStrOrList(hdr, id); - - /* Sorry, an unknown header name. Do linear search */ - while ((e = httpHeaderGetEntry(hdr, &pos))) { - if (e->id == HDR_OTHER && strCaseCmp(e->name, name) == 0) { - strListAdd(&result, strBuf(e->value), ','); - } - } - return result; -} - -/* - * Returns a the value of the specified list member, if any. - */ -String -httpHeaderGetByNameListMember(const HttpHeader * hdr, const char *name, const char *member, const char separator) -{ - String result = StringNull; - String header; - const char *pos = NULL; - const char *item; - int ilen; - int mlen = strlen(member); - - assert(hdr); - assert(name); - - header = httpHeaderGetByName(hdr, name); - - while (strListGetItem(&header, separator, &item, &ilen, &pos)) { - if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') { - stringAppend(&result, item + mlen + 1, ilen - mlen - 1); - break; - } - } - return result; -} - -/* - * returns a the value of the specified list member, if any. - */ -String -httpHeaderGetListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator) -{ - String result = StringNull; - String header; - const char *pos = NULL; - const char *item; - int ilen; - int mlen = strlen(member); - - assert(hdr); - assert(id >= 0); - - header = httpHeaderGetStrOrList(hdr, id); - - while (strListGetItem(&header, separator, &item, &ilen, &pos)) { - if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') { - stringAppend(&result, item + mlen + 1, ilen - mlen - 1); - break; - } - } - stringClean(&header); - return result; -} - -/* test if a field is present */ -int -httpHeaderHas(const HttpHeader * hdr, http_hdr_type id) -{ - assert(hdr); - assert_eid(id); - assert(id != HDR_OTHER); - debug(55, 7) ("%p lookup for %d\n", hdr, id); - return CBIT_TEST(hdr->mask, id); -} - -void -httpHeaderPutInt(HttpHeader * hdr, http_hdr_type id, int number) -{ - assert_eid(id); - assert(Headers[id].type == ftInt); /* must be of an appropriate type */ - assert(number >= 0); - httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, xitoa(number))); -} - -void -httpHeaderPutTime(HttpHeader * hdr, http_hdr_type id, time_t htime) -{ - assert_eid(id); - assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */ - assert(htime >= 0); - httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, mkrfc1123(htime))); -} - -void -httpHeaderPutStr(HttpHeader * hdr, http_hdr_type id, const char *str) -{ - assert_eid(id); - assert(Headers[id].type == ftStr); /* must be of an appropriate type */ - assert(str); - httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, str)); -} - -void -httpHeaderPutAuth(HttpHeader * hdr, const char *auth_scheme, const char *realm) -{ - assert(hdr && auth_scheme && realm); - httpHeaderPutStrf(hdr, HDR_WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm); -} - -void -httpHeaderPutCc(HttpHeader * hdr, const HttpHdrCc * cc) -{ - MemBuf mb; - Packer p; - assert(hdr && cc); - /* remove old directives if any */ - httpHeaderDelById(hdr, HDR_CACHE_CONTROL); - /* pack into mb */ - memBufDefInit(&mb); - packerToMemInit(&p, &mb); - httpHdrCcPackInto(cc, &p); - /* put */ - httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CACHE_CONTROL, NULL, mb.buf)); - /* cleanup */ - packerClean(&p); - memBufClean(&mb); -} - -void -httpHeaderPutContRange(HttpHeader * hdr, const HttpHdrContRange * cr) -{ - MemBuf mb; - Packer p; - assert(hdr && cr); - /* remove old directives if any */ - httpHeaderDelById(hdr, HDR_CONTENT_RANGE); - /* pack into mb */ - memBufDefInit(&mb); - packerToMemInit(&p, &mb); - httpHdrContRangePackInto(cr, &p); - /* put */ - httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CONTENT_RANGE, NULL, mb.buf)); - /* cleanup */ - packerClean(&p); - memBufClean(&mb); -} - -void -httpHeaderPutRange(HttpHeader * hdr, const HttpHdrRange * range) -{ - MemBuf mb; - Packer p; - assert(hdr && range); - /* remove old directives if any */ - httpHeaderDelById(hdr, HDR_RANGE); - /* pack into mb */ - memBufDefInit(&mb); - packerToMemInit(&p, &mb); - httpHdrRangePackInto(range, &p); - /* put */ - httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_RANGE, NULL, mb.buf)); - /* cleanup */ - packerClean(&p); - memBufClean(&mb); -} - -/* add extension header (these fields are not parsed/analyzed/joined, etc.) */ -void -httpHeaderPutExt(HttpHeader * hdr, const char *name, const char *value) -{ - assert(name && value); - debug(55, 8) ("%p adds ext entry '%s: %s'\n", hdr, name, value); - httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_OTHER, name, value)); -} - -int -httpHeaderGetInt(const HttpHeader * hdr, http_hdr_type id) -{ - HttpHeaderEntry *e; - int value = -1; - int ok; - assert_eid(id); - assert(Headers[id].type == ftInt); /* must be of an appropriate type */ - if ((e = httpHeaderFindEntry(hdr, id))) { - ok = httpHeaderParseInt(strBuf(e->value), &value); - httpHeaderNoteParsedEntry(e->id, e->value, !ok); - } - return value; -} - -time_t -httpHeaderGetTime(const HttpHeader * hdr, http_hdr_type id) -{ - HttpHeaderEntry *e; - time_t value = -1; - assert_eid(id); - assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */ - if ((e = httpHeaderFindEntry(hdr, id))) { - value = parse_rfc1123(strBuf(e->value)); - httpHeaderNoteParsedEntry(e->id, e->value, value < 0); - } - return value; -} - -/* sync with httpHeaderGetLastStr */ -const char * -httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id) -{ - HttpHeaderEntry *e; - assert_eid(id); - assert(Headers[id].type == ftStr); /* must be of an appropriate type */ - if ((e = httpHeaderFindEntry(hdr, id))) { - httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */ - return strBuf(e->value); - } - return NULL; -} - -/* unusual */ -const char * -httpHeaderGetLastStr(const HttpHeader * hdr, http_hdr_type id) -{ - HttpHeaderEntry *e; - assert_eid(id); - assert(Headers[id].type == ftStr); /* must be of an appropriate type */ - if ((e = httpHeaderFindLastEntry(hdr, id))) { - httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */ - return strBuf(e->value); - } - return NULL; -} - -HttpHdrCc * -httpHeaderGetCc(const HttpHeader * hdr) -{ - HttpHdrCc *cc; - String s; - if (!CBIT_TEST(hdr->mask, HDR_CACHE_CONTROL)) - return NULL; - s = httpHeaderGetList(hdr, HDR_CACHE_CONTROL); - cc = httpHdrCcParseCreate(&s); - HttpHeaderStats[hdr->owner].ccParsedCount++; - if (cc) - httpHdrCcUpdateStats(cc, &HttpHeaderStats[hdr->owner].ccTypeDistr); - httpHeaderNoteParsedEntry(HDR_CACHE_CONTROL, s, !cc); - stringClean(&s); - return cc; -} - -HttpHdrRange * -httpHeaderGetRange(const HttpHeader * hdr) -{ - HttpHdrRange *r = NULL; - HttpHeaderEntry *e; - /* some clients will send "Request-Range" _and_ *matching* "Range" - * who knows, some clients might send Request-Range only; - * this "if" should work correctly in both cases; - * hopefully no clients send mismatched headers! */ - if ((e = httpHeaderFindEntry(hdr, HDR_RANGE)) || - (e = httpHeaderFindEntry(hdr, HDR_REQUEST_RANGE))) { - r = httpHdrRangeParseCreate(&e->value); - httpHeaderNoteParsedEntry(e->id, e->value, !r); - } - return r; -} - -HttpHdrContRange * -httpHeaderGetContRange(const HttpHeader * hdr) -{ - HttpHdrContRange *cr = NULL; - HttpHeaderEntry *e; - if ((e = httpHeaderFindEntry(hdr, HDR_CONTENT_RANGE))) { - cr = httpHdrContRangeParseCreate(strBuf(e->value)); - httpHeaderNoteParsedEntry(e->id, e->value, !cr); - } - return cr; -} - -const char * -httpHeaderGetAuth(const HttpHeader * hdr, http_hdr_type id, const char *auth_scheme) -{ - const char *field; - int l; - assert(hdr && auth_scheme); - field = httpHeaderGetStr(hdr, id); - if (!field) /* no authorization field */ - return NULL; - l = strlen(auth_scheme); - if (!l || strncasecmp(field, auth_scheme, l)) /* wrong scheme */ - return NULL; - field += l; - if (!xisspace(*field)) /* wrong scheme */ - return NULL; - /* skip white space */ - field += xcountws(field); - if (!*field) /* no authorization cookie */ - return NULL; - return base64_decode(field); -} - -ETag -httpHeaderGetETag(const HttpHeader * hdr, http_hdr_type id) -{ - ETag etag = - {NULL, -1}; - HttpHeaderEntry *e; - assert(Headers[id].type == ftETag); /* must be of an appropriate type */ - if ((e = httpHeaderFindEntry(hdr, id))) - etagParseInit(&etag, strBuf(e->value)); - return etag; -} - -TimeOrTag -httpHeaderGetTimeOrTag(const HttpHeader * hdr, http_hdr_type id) -{ - TimeOrTag tot; - HttpHeaderEntry *e; - assert(Headers[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */ - memset(&tot, 0, sizeof(tot)); - if ((e = httpHeaderFindEntry(hdr, id))) { - const char *str = strBuf(e->value); - /* try as an ETag */ - if (etagParseInit(&tot.tag, str)) { - tot.valid = tot.tag.str != NULL; - tot.time = -1; - } else { - /* or maybe it is time? */ - tot.time = parse_rfc1123(str); - tot.valid = tot.time >= 0; - tot.tag.str = NULL; - } - } - assert(tot.time < 0 || !tot.tag.str); /* paranoid */ - return tot; -} - -/* - * HttpHeaderEntry - */ - -static HttpHeaderEntry * -httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value) -{ - HttpHeaderEntry *e; - assert_eid(id); - e = memAllocate(MEM_HTTP_HDR_ENTRY); - e->id = id; - if (id != HDR_OTHER) - e->name = Headers[id].name; - else - stringInit(&e->name, name); - stringInit(&e->value, value); - Headers[id].stat.aliveCount++; - debug(55, 9) ("created entry %p: '%s: %s'\n", e, strBuf(e->name), strBuf(e->value)); - return e; -} - -static void -httpHeaderEntryDestroy(HttpHeaderEntry * e) -{ - assert(e); - assert_eid(e->id); - debug(55, 9) ("destroying entry %p: '%s: %s'\n", e, strBuf(e->name), strBuf(e->value)); - /* clean name if needed */ - if (e->id == HDR_OTHER) - stringClean(&e->name); - stringClean(&e->value); - assert(Headers[e->id].stat.aliveCount); - Headers[e->id].stat.aliveCount--; - e->id = -1; - memFree(e, MEM_HTTP_HDR_ENTRY); -} - -/* parses and inits header entry, returns new entry on success */ -static HttpHeaderEntry * -httpHeaderEntryParseCreate(const char *field_start, const char *field_end) -{ - HttpHeaderEntry *e; - int id; - /* note: name_start == field_start */ - const char *name_end = strchr(field_start, ':'); - const int name_len = name_end ? name_end - field_start : 0; - const char *value_start = field_start + name_len + 1; /* skip ':' */ - /* note: value_end == field_end */ - - HeaderEntryParsedCount++; - - /* do we have a valid field name within this field? */ - if (!name_len || name_end > field_end) - return NULL; - if (name_len > 65536) { - /* String has a 64K limit */ - debug(55, 1) ("WARNING: ignoring header name of %d bytes\n", name_len); - return NULL; - } - /* now we know we can parse it */ - e = memAllocate(MEM_HTTP_HDR_ENTRY); - debug(55, 9) ("creating entry %p: near '%s'\n", e, getStringPrefix(field_start, field_end)); - /* is it a "known" field? */ - id = httpHeaderIdByName(field_start, name_len, Headers, HDR_ENUM_END); - if (id < 0) - id = HDR_OTHER; - assert_eid(id); - e->id = id; - /* set field name */ - if (id == HDR_OTHER) - stringLimitInit(&e->name, field_start, name_len); - else - e->name = Headers[id].name; - /* trim field value */ - while (value_start < field_end && xisspace(*value_start)) - value_start++; - if (field_end - value_start > 65536) { - /* String has a 64K limit */ - debug(55, 1) ("WARNING: ignoring '%s' header of %d bytes\n", - strBuf(e->name), (int) (field_end - value_start)); - if (e->id == HDR_OTHER) - stringClean(&e->name); - memFree(e, MEM_HTTP_HDR_ENTRY); - return NULL; - } - /* set field value */ - stringLimitInit(&e->value, value_start, field_end - value_start); - Headers[id].stat.seenCount++; - Headers[id].stat.aliveCount++; - debug(55, 9) ("created entry %p: '%s: %s'\n", e, strBuf(e->name), strBuf(e->value)); - return e; -} - -HttpHeaderEntry * -httpHeaderEntryClone(const HttpHeaderEntry * e) -{ - return httpHeaderEntryCreate(e->id, strBuf(e->name), strBuf(e->value)); -} - -void -httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p) -{ - assert(e && p); - packerAppend(p, strBuf(e->name), strLen(e->name)); - packerAppend(p, ": ", 2); - packerAppend(p, strBuf(e->value), strLen(e->value)); - packerAppend(p, "\r\n", 2); -} - -static void -httpHeaderNoteParsedEntry(http_hdr_type id, String context, int error) -{ - Headers[id].stat.parsCount++; - if (error) { - Headers[id].stat.errCount++; - debug(55, 2) ("cannot parse hdr field: '%s: %s'\n", - strBuf(Headers[id].name), strBuf(context)); - } -} - -/* - * Reports - */ - -/* tmp variable used to pass stat info to dumpers */ -extern const HttpHeaderStat *dump_stat; /* argh! */ -const HttpHeaderStat *dump_stat = NULL; - -static void -httpHeaderFieldStatDumper(StoreEntry * sentry, int idx, double val, double size, int count) -{ - const int id = (int) val; - const int valid_id = id >= 0 && id < HDR_ENUM_END; - const char *name = valid_id ? strBuf(Headers[id].name) : "INVALID"; - int visible = count > 0; - /* for entries with zero count, list only those that belong to current type of message */ - if (!visible && valid_id && dump_stat->owner_mask) - visible = CBIT_TEST(*dump_stat->owner_mask, id); - if (visible) - storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n", - id, name, count, xdiv(count, dump_stat->busyDestroyedCount)); -} - -static void -httpHeaderFldsPerHdrDumper(StoreEntry * sentry, int idx, double val, double size, int count) -{ - if (count) - storeAppendPrintf(sentry, "%2d\t %5d\t %5d\t %6.2f\n", - idx, (int) val, count, - xpercent(count, dump_stat->destroyedCount)); -} - - -static void -httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e) -{ - assert(hs && e); - - dump_stat = hs; - storeAppendPrintf(e, "\nHeader Stats: %s\n", hs->label); - storeAppendPrintf(e, "\nField type distribution\n"); - storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n", - "id", "name", "count", "#/header"); - statHistDump(&hs->fieldTypeDistr, e, httpHeaderFieldStatDumper); - storeAppendPrintf(e, "\nCache-control directives distribution\n"); - storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n", - "id", "name", "count", "#/cc_field"); - statHistDump(&hs->ccTypeDistr, e, httpHdrCcStatDumper); - storeAppendPrintf(e, "\nNumber of fields per header distribution\n"); - storeAppendPrintf(e, "%2s\t %-5s\t %5s\t %6s\n", - "id", "#flds", "count", "%total"); - statHistDump(&hs->hdrUCountDistr, e, httpHeaderFldsPerHdrDumper); - dump_stat = NULL; -} - -void -httpHeaderStoreReport(StoreEntry * e) -{ - int i; - http_hdr_type ht; - assert(e); - - HttpHeaderStats[0].parsedCount = - HttpHeaderStats[hoRequest].parsedCount + HttpHeaderStats[hoReply].parsedCount; - HttpHeaderStats[0].ccParsedCount = - HttpHeaderStats[hoRequest].ccParsedCount + HttpHeaderStats[hoReply].ccParsedCount; - HttpHeaderStats[0].destroyedCount = - HttpHeaderStats[hoRequest].destroyedCount + HttpHeaderStats[hoReply].destroyedCount; - HttpHeaderStats[0].busyDestroyedCount = - HttpHeaderStats[hoRequest].busyDestroyedCount + HttpHeaderStats[hoReply].busyDestroyedCount; - - for (i = 1; i < HttpHeaderStatCount; i++) { - httpHeaderStatDump(HttpHeaderStats + i, e); - storeAppendPrintf(e, "%s\n", "
"); - } - /* field stats for all messages */ - storeAppendPrintf(e, "\nHttp Fields Stats (replies and requests)\n"); - storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\t %6s\n", - "id", "name", "#alive", "%err", "%repeat"); - for (ht = 0; ht < HDR_ENUM_END; ht++) { - HttpHeaderFieldInfo *f = Headers + ht; - storeAppendPrintf(e, "%2d\t %-20s\t %5d\t %6.3f\t %6.3f\n", - f->id, strBuf(f->name), f->stat.aliveCount, - xpercent(f->stat.errCount, f->stat.parsCount), - xpercent(f->stat.repCount, f->stat.seenCount)); - } - storeAppendPrintf(e, "Headers Parsed: %d + %d = %d\n", - HttpHeaderStats[hoRequest].parsedCount, - HttpHeaderStats[hoReply].parsedCount, - HttpHeaderStats[0].parsedCount); - storeAppendPrintf(e, "Hdr Fields Parsed: %d\n", HeaderEntryParsedCount); -} - -http_hdr_type -httpHeaderIdByName(const char *name, int name_len, const HttpHeaderFieldInfo * info, int end) -{ - int i; - for (i = 0; i < end; ++i) { - if (name_len >= 0 && name_len != strLen(info[i].name)) - continue; - if (!strncasecmp(name, strBuf(info[i].name), - name_len < 0 ? strLen(info[i].name) + 1 : name_len)) - return i; - } - return -1; -} - -http_hdr_type -httpHeaderIdByNameDef(const char *name, int name_len) -{ - if (!Headers) - Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END); - return httpHeaderIdByName(name, name_len, Headers, HDR_ENUM_END); -} - -const char * -httpHeaderNameById(int id) -{ - if (!Headers) - Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END); - assert(id >= 0 && id < HDR_ENUM_END); - return HeadersAttrs[id].name; -} --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/HttpHeader.cc Wed Feb 14 01:07:36 2007 @@ -0,0 +1,1243 @@ + +/* + * $Id: HttpHeader.cc,v 1.1.2.1 2002/10/08 11:11:34 rbcollins Exp $ + * + * DEBUG: section 55 HTTP Header + * AUTHOR: Alex Rousskov + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "Store.h" +#include "HttpHeader.h" + +/* + * On naming conventions: + * + * HTTP/1.1 defines message-header as + * + * message-header = field-name ":" [ field-value ] CRLF + * field-name = token + * field-value = *( field-content | LWS ) + * + * HTTP/1.1 does not give a name name a group of all message-headers in a message. + * Squid 1.1 seems to refer to that group _plus_ start-line as "headers". + * + * HttpHeader is an object that represents all message-headers in a message. + * HttpHeader does not manage start-line. + * + * HttpHeader is implemented as a collection of header "entries". + * An entry is a (field_id, field_name, field_value) triplet. + */ + + +/* + * local constants and vars + */ + +/* + * A table with major attributes for every known field. + * We calculate name lengths and reorganize this array on start up. + * After reorganization, field id can be used as an index to the table. + */ +static const HttpHeaderFieldAttrs HeadersAttrs[] = +{ + {"Accept", HDR_ACCEPT, ftStr}, + {"Accept-Charset", HDR_ACCEPT_CHARSET, ftStr}, + {"Accept-Encoding", HDR_ACCEPT_ENCODING, ftStr}, + {"Accept-Language", HDR_ACCEPT_LANGUAGE, ftStr}, + {"Accept-Ranges", HDR_ACCEPT_RANGES, ftStr}, + {"Age", HDR_AGE, ftInt}, + {"Allow", HDR_ALLOW, ftStr}, + {"Authorization", HDR_AUTHORIZATION, ftStr}, /* for now */ + {"Cache-Control", HDR_CACHE_CONTROL, ftPCc}, + {"Connection", HDR_CONNECTION, ftStr}, + {"Content-Base", HDR_CONTENT_BASE, ftStr}, + {"Content-Encoding", HDR_CONTENT_ENCODING, ftStr}, + {"Content-Language", HDR_CONTENT_LANGUAGE, ftStr}, + {"Content-Length", HDR_CONTENT_LENGTH, ftInt}, + {"Content-Location", HDR_CONTENT_LOCATION, ftStr}, + {"Content-MD5", HDR_CONTENT_MD5, ftStr}, /* for now */ + {"Content-Range", HDR_CONTENT_RANGE, ftPContRange}, + {"Content-Type", HDR_CONTENT_TYPE, ftStr}, + {"Cookie", HDR_COOKIE, ftStr}, + {"Date", HDR_DATE, ftDate_1123}, + {"ETag", HDR_ETAG, ftETag}, + {"Expires", HDR_EXPIRES, ftDate_1123}, + {"From", HDR_FROM, ftStr}, + {"Host", HDR_HOST, ftStr}, + {"If-Match", HDR_IF_MATCH, ftStr}, /* for now */ + {"If-Modified-Since", HDR_IF_MODIFIED_SINCE, ftDate_1123}, + {"If-None-Match", HDR_IF_NONE_MATCH, ftStr}, /* for now */ + {"If-Range", HDR_IF_RANGE, ftDate_1123_or_ETag}, + {"Last-Modified", HDR_LAST_MODIFIED, ftDate_1123}, + {"Link", HDR_LINK, ftStr}, + {"Location", HDR_LOCATION, ftStr}, + {"Max-Forwards", HDR_MAX_FORWARDS, ftInt}, + {"Mime-Version", HDR_MIME_VERSION, ftStr}, /* for now */ + {"Pragma", HDR_PRAGMA, ftStr}, + {"Proxy-Authenticate", HDR_PROXY_AUTHENTICATE, ftStr}, + {"Proxy-Authentication-Info", HDR_PROXY_AUTHENTICATION_INFO, ftStr}, + {"Proxy-Authorization", HDR_PROXY_AUTHORIZATION, ftStr}, + {"Proxy-Connection", HDR_PROXY_CONNECTION, ftStr}, + {"Public", HDR_PUBLIC, ftStr}, + {"Range", HDR_RANGE, ftPRange}, + {"Referer", HDR_REFERER, ftStr}, + {"Request-Range", HDR_REQUEST_RANGE, ftPRange}, /* usually matches HDR_RANGE */ + {"Retry-After", HDR_RETRY_AFTER, ftStr}, /* for now (ftDate_1123 or ftInt!) */ + {"Server", HDR_SERVER, ftStr}, + {"Set-Cookie", HDR_SET_COOKIE, ftStr}, + {"Title", HDR_TITLE, ftStr}, + {"Transfer-Encoding", HDR_TRANSFER_ENCODING, ftStr}, + {"Upgrade", HDR_UPGRADE, ftStr}, /* for now */ + {"User-Agent", HDR_USER_AGENT, ftStr}, + {"Vary", HDR_VARY, ftStr}, /* for now */ + {"Via", HDR_VIA, ftStr}, /* for now */ + {"Warning", HDR_WARNING, ftStr}, /* for now */ + {"WWW-Authenticate", HDR_WWW_AUTHENTICATE, ftStr}, + {"Authentication-Info", HDR_AUTHENTICATION_INFO, ftStr}, + {"X-Cache", HDR_X_CACHE, ftStr}, + {"X-Cache-Lookup", HDR_X_CACHE_LOOKUP, ftStr}, + {"X-Forwarded-For", HDR_X_FORWARDED_FOR, ftStr}, + {"X-Request-URI", HDR_X_REQUEST_URI, ftStr}, + {"X-Squid-Error", HDR_X_SQUID_ERROR, ftStr}, + {"Negotiate", HDR_NEGOTIATE, ftStr}, +#if X_ACCELERATOR_VARY + {"X-Accelerator-Vary", HDR_X_ACCELERATOR_VARY, ftStr}, +#endif + {"Other:", HDR_OTHER, ftStr} /* ':' will not allow matches */ +}; +static HttpHeaderFieldInfo *Headers = NULL; + +http_hdr_type &operator++ (http_hdr_type &aHeader) +{ + aHeader = (http_hdr_type)(++(int)aHeader); + return aHeader; +} + + +/* + * headers with field values defined as #(values) in HTTP/1.1 + * Headers that are currently not recognized, are commented out. + */ +static HttpHeaderMask ListHeadersMask; /* set run-time using ListHeadersArr */ +static http_hdr_type ListHeadersArr[] = +{ + HDR_ACCEPT, + HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE, + HDR_ACCEPT_RANGES, HDR_ALLOW, + HDR_CACHE_CONTROL, + HDR_CONTENT_ENCODING, + HDR_CONTENT_LANGUAGE, + HDR_CONNECTION, + HDR_IF_MATCH, HDR_IF_NONE_MATCH, + HDR_LINK, HDR_PRAGMA, + HDR_PROXY_CONNECTION, + HDR_TRANSFER_ENCODING, + HDR_UPGRADE, + HDR_VARY, + HDR_VIA, + /* HDR_WARNING, */ + HDR_WWW_AUTHENTICATE, + HDR_AUTHENTICATION_INFO, + HDR_PROXY_AUTHENTICATION_INFO, + /* HDR_EXPECT, HDR_TE, HDR_TRAILER */ +#if X_ACCELERATOR_VARY + HDR_X_ACCELERATOR_VARY, +#endif + HDR_X_FORWARDED_FOR +}; + +/* general-headers */ +static http_hdr_type GeneralHeadersArr[] = +{ + HDR_CACHE_CONTROL, HDR_CONNECTION, HDR_DATE, HDR_PRAGMA, + HDR_TRANSFER_ENCODING, + HDR_UPGRADE, + /* HDR_TRAILER, */ + HDR_VIA +}; + +/* entity-headers */ +static http_hdr_type EntityHeadersArr[] = +{ + HDR_ALLOW, HDR_CONTENT_BASE, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE, + HDR_CONTENT_LENGTH, HDR_CONTENT_LOCATION, HDR_CONTENT_MD5, + HDR_CONTENT_RANGE, HDR_CONTENT_TYPE, HDR_ETAG, HDR_EXPIRES, HDR_LAST_MODIFIED, HDR_LINK, + HDR_OTHER +}; + +static HttpHeaderMask ReplyHeadersMask; /* set run-time using ReplyHeaders */ +static http_hdr_type ReplyHeadersArr[] = +{ + HDR_ACCEPT, HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE, + HDR_ACCEPT_RANGES, HDR_AGE, + HDR_LOCATION, HDR_MAX_FORWARDS, + HDR_MIME_VERSION, HDR_PUBLIC, HDR_RETRY_AFTER, HDR_SERVER, HDR_SET_COOKIE, + HDR_VARY, + HDR_WARNING, HDR_PROXY_CONNECTION, HDR_X_CACHE, + HDR_X_CACHE_LOOKUP, + HDR_X_REQUEST_URI, +#if X_ACCELERATOR_VARY + HDR_X_ACCELERATOR_VARY, +#endif + HDR_X_SQUID_ERROR +}; + +static HttpHeaderMask RequestHeadersMask; /* set run-time using RequestHeaders */ +static http_hdr_type RequestHeadersArr[] = +{ + HDR_AUTHORIZATION, HDR_FROM, HDR_HOST, + HDR_IF_MATCH, HDR_IF_MODIFIED_SINCE, HDR_IF_NONE_MATCH, + HDR_IF_RANGE, HDR_MAX_FORWARDS, HDR_PROXY_CONNECTION, + HDR_PROXY_AUTHORIZATION, HDR_RANGE, HDR_REFERER, HDR_REQUEST_RANGE, + HDR_USER_AGENT, HDR_X_FORWARDED_FOR +}; + +/* header accounting */ +static HttpHeaderStat HttpHeaderStats[] = +{ + {"all"}, +#if USE_HTCP + {"HTCP reply"}, +#endif + {"request"}, + {"reply"} +}; +static int HttpHeaderStatCount = countof(HttpHeaderStats); + +static int HeaderEntryParsedCount = 0; + +/* + * local routines + */ + +#define assert_eid(id) assert((id) >= 0 && (id) < HDR_ENUM_END) + +static HttpHeaderEntry *httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value); +static void httpHeaderEntryDestroy(HttpHeaderEntry * e); +static HttpHeaderEntry *httpHeaderEntryParseCreate(const char *field_start, const char *field_end); +static void httpHeaderNoteParsedEntry(http_hdr_type id, String value, int error); + +static void httpHeaderStatInit(HttpHeaderStat * hs, const char *label); +static void httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e); + +/* + * Module initialization routines + */ + +void +httpHeaderInitModule(void) +{ + int i; + /* check that we have enough space for masks */ + assert(8 * sizeof(HttpHeaderMask) >= HDR_ENUM_END); + /* all headers must be described */ + assert(countof(HeadersAttrs) == HDR_ENUM_END); + if (!Headers) + Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END); + /* create masks */ + httpHeaderMaskInit(&ListHeadersMask, 0); + httpHeaderCalcMask(&ListHeadersMask, (const int *) ListHeadersArr, countof(ListHeadersArr)); + httpHeaderMaskInit(&ReplyHeadersMask, 0); + httpHeaderCalcMask(&ReplyHeadersMask, (const int *) ReplyHeadersArr, countof(ReplyHeadersArr)); + httpHeaderCalcMask(&ReplyHeadersMask, (const int *) GeneralHeadersArr, countof(GeneralHeadersArr)); + httpHeaderCalcMask(&ReplyHeadersMask, (const int *) EntityHeadersArr, countof(EntityHeadersArr)); + httpHeaderMaskInit(&RequestHeadersMask, 0); + httpHeaderCalcMask(&RequestHeadersMask, (const int *) RequestHeadersArr, countof(RequestHeadersArr)); + httpHeaderCalcMask(&RequestHeadersMask, (const int *) GeneralHeadersArr, countof(GeneralHeadersArr)); + httpHeaderCalcMask(&RequestHeadersMask, (const int *) EntityHeadersArr, countof(EntityHeadersArr)); + /* init header stats */ + assert(HttpHeaderStatCount == hoReply + 1); + for (i = 0; i < HttpHeaderStatCount; i++) + httpHeaderStatInit(HttpHeaderStats + i, HttpHeaderStats[i].label); + HttpHeaderStats[hoRequest].owner_mask = &RequestHeadersMask; + HttpHeaderStats[hoReply].owner_mask = &ReplyHeadersMask; +#if USE_HTCP + HttpHeaderStats[hoHtcpReply].owner_mask = &ReplyHeadersMask; +#endif + /* init dependent modules */ + httpHdrCcInitModule(); + /* register with cache manager */ + cachemgrRegister("http_headers", + "HTTP Header Statistics", httpHeaderStoreReport, 0, 1); +} + +void +httpHeaderCleanModule(void) +{ + httpHeaderDestroyFieldsInfo(Headers, HDR_ENUM_END); + Headers = NULL; + httpHdrCcCleanModule(); +} + +static void +httpHeaderStatInit(HttpHeaderStat * hs, const char *label) +{ + assert(hs); + assert(label); + memset(hs, 0, sizeof(HttpHeaderStat)); + hs->label = label; + statHistEnumInit(&hs->hdrUCountDistr, 32); /* not a real enum */ + statHistEnumInit(&hs->fieldTypeDistr, HDR_ENUM_END); + statHistEnumInit(&hs->ccTypeDistr, CC_ENUM_END); +} + +/* + * HttpHeader Implementation + */ + +void +httpHeaderInit(HttpHeader * hdr, http_hdr_owner_type owner) +{ + assert(hdr); + assert(owner > hoNone && owner <= hoReply); + debug(55, 7) ("init-ing hdr: %p owner: %d\n", hdr, owner); + memset(hdr, 0, sizeof(*hdr)); + hdr->owner = owner; + arrayInit(&hdr->entries); +} + +void +httpHeaderClean(HttpHeader * hdr) +{ + HttpHeaderPos pos = HttpHeaderInitPos; + HttpHeaderEntry *e; + + assert(hdr); + assert(hdr->owner > hoNone && hdr->owner <= hoReply); + debug(55, 7) ("cleaning hdr: %p owner: %d\n", hdr, hdr->owner); + + statHistCount(&HttpHeaderStats[hdr->owner].hdrUCountDistr, hdr->entries.count); + HttpHeaderStats[hdr->owner].destroyedCount++; + HttpHeaderStats[hdr->owner].busyDestroyedCount += hdr->entries.count > 0; + while ((e = httpHeaderGetEntry(hdr, &pos))) { + /* tmp hack to try to avoid coredumps */ + if (e->id < 0 || e->id >= HDR_ENUM_END) { + debug(55, 0) ("httpHeaderClean BUG: entry[%d] is invalid (%d). Ignored.\n", + (int) pos, e->id); + } else { + statHistCount(&HttpHeaderStats[hdr->owner].fieldTypeDistr, e->id); + /* yes, this destroy() leaves us in an incosistent state */ + httpHeaderEntryDestroy(e); + } + } + arrayClean(&hdr->entries); +} + +/* append entries (also see httpHeaderUpdate) */ +void +httpHeaderAppend(HttpHeader * dest, const HttpHeader * src) +{ + const HttpHeaderEntry *e; + HttpHeaderPos pos = HttpHeaderInitPos; + assert(src && dest); + assert(src != dest); + debug(55, 7) ("appending hdr: %p += %p\n", dest, src); + + while ((e = httpHeaderGetEntry(src, &pos))) { + httpHeaderAddEntry(dest, httpHeaderEntryClone(e)); + } +} + +/* use fresh entries to replace old ones */ +void +httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask) +{ + const HttpHeaderEntry *e; + HttpHeaderPos pos = HttpHeaderInitPos; + assert(old && fresh); + assert(old != fresh); + debug(55, 7) ("updating hdr: %p <- %p\n", old, fresh); + + while ((e = httpHeaderGetEntry(fresh, &pos))) { + /* deny bad guys (ok to check for HDR_OTHER) here */ + if (denied_mask && CBIT_TEST(*denied_mask, e->id)) + continue; + httpHeaderDelByName(old, strBuf(e->name)); + httpHeaderAddEntry(old, httpHeaderEntryClone(e)); + } +} + +/* just handy in parsing: resets and returns false */ +int +httpHeaderReset(HttpHeader * hdr) +{ + http_hdr_owner_type ho; + assert(hdr); + ho = hdr->owner; + httpHeaderClean(hdr); + httpHeaderInit(hdr, ho); + return 0; +} + +int +httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_end) +{ + const char *field_start = header_start; + HttpHeaderEntry *e; + + assert(hdr); + assert(header_start && header_end); + debug(55, 7) ("parsing hdr: (%p)\n%s\n", hdr, getStringPrefix(header_start, header_end)); + HttpHeaderStats[hdr->owner].parsedCount++; + /* commonn format headers are ":[ws]" lines delimited by */ + while (field_start < header_end) { + const char *field_end; + const char *field_ptr = field_start; + do { + field_end = field_ptr = field_ptr + strcspn(field_ptr, "\r\n"); + /* skip CRLF */ + if (*field_ptr == '\r') + field_ptr++; + if (*field_ptr == '\n') + field_ptr++; + } + while (*field_ptr == ' ' || *field_ptr == '\t'); + if (!*field_end || field_end > header_end) + return httpHeaderReset(hdr); /* missing */ + e = httpHeaderEntryParseCreate(field_start, field_end); + if (e != NULL) + httpHeaderAddEntry(hdr, e); + else + debug(55, 2) ("warning: ignoring unparseable http header field near '%s'\n", + getStringPrefix(field_start, field_end)); + field_start = field_end; + /* skip CRLF */ + if (*field_start == '\r') + field_start++; + if (*field_start == '\n') + field_start++; + } + return 1; /* even if no fields where found, it is a valid header */ +} + +/* packs all the entries using supplied packer */ +void +httpHeaderPackInto(const HttpHeader * hdr, Packer * p) +{ + HttpHeaderPos pos = HttpHeaderInitPos; + const HttpHeaderEntry *e; + assert(hdr && p); + debug(55, 7) ("packing hdr: (%p)\n", hdr); + /* pack all entries one by one */ + while ((e = httpHeaderGetEntry(hdr, &pos))) + httpHeaderEntryPackInto(e, p); +} + +/* returns next valid entry */ +HttpHeaderEntry * +httpHeaderGetEntry(const HttpHeader * hdr, HttpHeaderPos * pos) +{ + assert(hdr && pos); + assert(*pos >= HttpHeaderInitPos && *pos < hdr->entries.count); + for ((*pos)++; *pos < hdr->entries.count; (*pos)++) { + if (hdr->entries.items[*pos]) + return (HttpHeaderEntry*)hdr->entries.items[*pos]; + } + return NULL; +} + +/* + * returns a pointer to a specified entry if any + * note that we return one entry so it does not make much sense to ask for + * "list" headers + */ +HttpHeaderEntry * +httpHeaderFindEntry(const HttpHeader * hdr, http_hdr_type id) +{ + HttpHeaderPos pos = HttpHeaderInitPos; + HttpHeaderEntry *e; + assert(hdr); + assert_eid(id); + assert(!CBIT_TEST(ListHeadersMask, id)); + + /* check mask first */ + if (!CBIT_TEST(hdr->mask, id)) + return NULL; + /* looks like we must have it, do linear search */ + while ((e = httpHeaderGetEntry(hdr, &pos))) { + if (e->id == id) + return e; + } + /* hm.. we thought it was there, but it was not found */ + assert(0); + return NULL; /* not reached */ +} + +/* + * same as httpHeaderFindEntry + */ +static HttpHeaderEntry * +httpHeaderFindLastEntry(const HttpHeader * hdr, http_hdr_type id) +{ + HttpHeaderPos pos = HttpHeaderInitPos; + HttpHeaderEntry *e; + HttpHeaderEntry *result = NULL; + assert(hdr); + assert_eid(id); + assert(!CBIT_TEST(ListHeadersMask, id)); + + /* check mask first */ + if (!CBIT_TEST(hdr->mask, id)) + return NULL; + /* looks like we must have it, do linear search */ + while ((e = httpHeaderGetEntry(hdr, &pos))) { + if (e->id == id) + result = e; + } + assert(result); /* must be there! */ + return result; +} + +/* + * deletes all fields with a given name if any, returns #fields deleted; + */ +int +httpHeaderDelByName(HttpHeader * hdr, const char *name) +{ + int count = 0; + HttpHeaderPos pos = HttpHeaderInitPos; + HttpHeaderEntry *e; + httpHeaderMaskInit(&hdr->mask, 0); /* temporal inconsistency */ + debug(55, 7) ("deleting '%s' fields in hdr %p\n", name, hdr); + while ((e = httpHeaderGetEntry(hdr, &pos))) { + if (!strCaseCmp(e->name, name)) { + httpHeaderDelAt(hdr, pos); + count++; + } else + CBIT_SET(hdr->mask, e->id); + } + return count; +} + +/* deletes all entries with a given id, returns the #entries deleted */ +int +httpHeaderDelById(HttpHeader * hdr, http_hdr_type id) +{ + int count = 0; + HttpHeaderPos pos = HttpHeaderInitPos; + HttpHeaderEntry *e; + debug(55, 8) ("%p del-by-id %d\n", hdr, id); + assert(hdr); + assert_eid(id); + assert_eid(id != HDR_OTHER); /* does not make sense */ + if (!CBIT_TEST(hdr->mask, id)) + return 0; + while ((e = httpHeaderGetEntry(hdr, &pos))) { + if (e->id == id) { + httpHeaderDelAt(hdr, pos); + count++; + } + } + CBIT_CLR(hdr->mask, id); + assert(count); + return count; +} + +/* + * deletes an entry at pos and leaves a gap; leaving a gap makes it + * possible to iterate(search) and delete fields at the same time + */ +void +httpHeaderDelAt(HttpHeader * hdr, HttpHeaderPos pos) +{ + HttpHeaderEntry *e; + assert(pos >= HttpHeaderInitPos && pos < hdr->entries.count); + e = (HttpHeaderEntry*)hdr->entries.items[pos]; + hdr->entries.items[pos] = NULL; + /* decrement header length, allow for ": " and crlf */ + hdr->len -= strLen(e->name) + 2 + strLen(e->value) + 2; + assert(hdr->len >= 0); + httpHeaderEntryDestroy(e); +} + + +/* appends an entry; + * does not call httpHeaderEntryClone() so one should not reuse "*e" + */ +void +httpHeaderAddEntry(HttpHeader * hdr, HttpHeaderEntry * e) +{ + assert(hdr && e); + assert_eid(e->id); + + debug(55, 7) ("%p adding entry: %d at %d\n", + hdr, e->id, hdr->entries.count); + if (CBIT_TEST(hdr->mask, e->id)) + Headers[e->id].stat.repCount++; + else + CBIT_SET(hdr->mask, e->id); + arrayAppend(&hdr->entries, e); + /* increment header length, allow for ": " and crlf */ + hdr->len += strLen(e->name) + 2 + strLen(e->value) + 2; +} + +/* return a list of entries with the same id separated by ',' and ws */ +String +httpHeaderGetList(const HttpHeader * hdr, http_hdr_type id) +{ + String s = StringNull; + HttpHeaderEntry *e; + HttpHeaderPos pos = HttpHeaderInitPos; + debug(55, 6) ("%p: joining for id %d\n", hdr, id); + /* only fields from ListHeaders array can be "listed" */ + assert(CBIT_TEST(ListHeadersMask, id)); + if (!CBIT_TEST(hdr->mask, id)) + return s; + while ((e = httpHeaderGetEntry(hdr, &pos))) { + if (e->id == id) + strListAdd(&s, strBuf(e->value), ','); + } + /* + * note: we might get an empty (len==0) string if there was an "empty" + * header; we must not get a NULL string though. + */ + assert(strBuf(s)); + /* temporary warning: remove it! @?@ @?@ @?@ */ + if (!strLen(s)) + debug(55, 3) ("empty list header: %s (%d)\n", strBuf(Headers[id].name), id); + debug(55, 6) ("%p: joined for id %d: %s\n", hdr, id, strBuf(s)); + return s; +} + +/* return a string or list of entries with the same id separated by ',' and ws */ +String +httpHeaderGetStrOrList(const HttpHeader * hdr, http_hdr_type id) +{ + HttpHeaderEntry *e; + + if (CBIT_TEST(ListHeadersMask, id)) + return httpHeaderGetList(hdr, id); + if ((e = httpHeaderFindEntry(hdr, id))) { + String s; + stringInit(&s, strBuf(e->value)); + return s; + } + return StringNull; +} + +/* + * Returns the value of the specified header. + */ +String +httpHeaderGetByName(const HttpHeader * hdr, const char *name) +{ + http_hdr_type id; + HttpHeaderPos pos = HttpHeaderInitPos; + HttpHeaderEntry *e; + String result = StringNull; + + assert(hdr); + assert(name); + + /* First try the quick path */ + id = httpHeaderIdByNameDef(name, strlen(name)); + if (id != -1) + return httpHeaderGetStrOrList(hdr, id); + + /* Sorry, an unknown header name. Do linear search */ + while ((e = httpHeaderGetEntry(hdr, &pos))) { + if (e->id == HDR_OTHER && strCaseCmp(e->name, name) == 0) { + strListAdd(&result, strBuf(e->value), ','); + } + } + return result; +} + +/* + * Returns a the value of the specified list member, if any. + */ +String +httpHeaderGetByNameListMember(const HttpHeader * hdr, const char *name, const char *member, const char separator) +{ + String result = StringNull; + String header; + const char *pos = NULL; + const char *item; + int ilen; + int mlen = strlen(member); + + assert(hdr); + assert(name); + + header = httpHeaderGetByName(hdr, name); + + while (strListGetItem(&header, separator, &item, &ilen, &pos)) { + if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') { + stringAppend(&result, item + mlen + 1, ilen - mlen - 1); + break; + } + } + return result; +} + +/* + * returns a the value of the specified list member, if any. + */ +String +httpHeaderGetListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator) +{ + String result = StringNull; + String header; + const char *pos = NULL; + const char *item; + int ilen; + int mlen = strlen(member); + + assert(hdr); + assert(id >= 0); + + header = httpHeaderGetStrOrList(hdr, id); + + while (strListGetItem(&header, separator, &item, &ilen, &pos)) { + if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') { + stringAppend(&result, item + mlen + 1, ilen - mlen - 1); + break; + } + } + stringClean(&header); + return result; +} + +/* test if a field is present */ +int +httpHeaderHas(const HttpHeader * hdr, http_hdr_type id) +{ + assert(hdr); + assert_eid(id); + assert(id != HDR_OTHER); + debug(55, 7) ("%p lookup for %d\n", hdr, id); + return CBIT_TEST(hdr->mask, id); +} + +void +httpHeaderPutInt(HttpHeader * hdr, http_hdr_type id, int number) +{ + assert_eid(id); + assert(Headers[id].type == ftInt); /* must be of an appropriate type */ + assert(number >= 0); + httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, xitoa(number))); +} + +void +httpHeaderPutTime(HttpHeader * hdr, http_hdr_type id, time_t htime) +{ + assert_eid(id); + assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */ + assert(htime >= 0); + httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, mkrfc1123(htime))); +} + +void +httpHeaderPutStr(HttpHeader * hdr, http_hdr_type id, const char *str) +{ + assert_eid(id); + assert(Headers[id].type == ftStr); /* must be of an appropriate type */ + assert(str); + httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, str)); +} + +void +httpHeaderPutAuth(HttpHeader * hdr, const char *auth_scheme, const char *realm) +{ + assert(hdr && auth_scheme && realm); + httpHeaderPutStrf(hdr, HDR_WWW_AUTHENTICATE, "%s realm=\"%s\"", auth_scheme, realm); +} + +void +httpHeaderPutCc(HttpHeader * hdr, const HttpHdrCc * cc) +{ + MemBuf mb; + Packer p; + assert(hdr && cc); + /* remove old directives if any */ + httpHeaderDelById(hdr, HDR_CACHE_CONTROL); + /* pack into mb */ + memBufDefInit(&mb); + packerToMemInit(&p, &mb); + httpHdrCcPackInto(cc, &p); + /* put */ + httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CACHE_CONTROL, NULL, mb.buf)); + /* cleanup */ + packerClean(&p); + memBufClean(&mb); +} + +void +httpHeaderPutContRange(HttpHeader * hdr, const HttpHdrContRange * cr) +{ + MemBuf mb; + Packer p; + assert(hdr && cr); + /* remove old directives if any */ + httpHeaderDelById(hdr, HDR_CONTENT_RANGE); + /* pack into mb */ + memBufDefInit(&mb); + packerToMemInit(&p, &mb); + httpHdrContRangePackInto(cr, &p); + /* put */ + httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_CONTENT_RANGE, NULL, mb.buf)); + /* cleanup */ + packerClean(&p); + memBufClean(&mb); +} + +void +httpHeaderPutRange(HttpHeader * hdr, const HttpHdrRange * range) +{ + MemBuf mb; + Packer p; + assert(hdr && range); + /* remove old directives if any */ + httpHeaderDelById(hdr, HDR_RANGE); + /* pack into mb */ + memBufDefInit(&mb); + packerToMemInit(&p, &mb); + httpHdrRangePackInto(range, &p); + /* put */ + httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_RANGE, NULL, mb.buf)); + /* cleanup */ + packerClean(&p); + memBufClean(&mb); +} + +/* add extension header (these fields are not parsed/analyzed/joined, etc.) */ +void +httpHeaderPutExt(HttpHeader * hdr, const char *name, const char *value) +{ + assert(name && value); + debug(55, 8) ("%p adds ext entry '%s: %s'\n", hdr, name, value); + httpHeaderAddEntry(hdr, httpHeaderEntryCreate(HDR_OTHER, name, value)); +} + +int +httpHeaderGetInt(const HttpHeader * hdr, http_hdr_type id) +{ + HttpHeaderEntry *e; + int value = -1; + int ok; + assert_eid(id); + assert(Headers[id].type == ftInt); /* must be of an appropriate type */ + if ((e = httpHeaderFindEntry(hdr, id))) { + ok = httpHeaderParseInt(strBuf(e->value), &value); + httpHeaderNoteParsedEntry(e->id, e->value, !ok); + } + return value; +} + +time_t +httpHeaderGetTime(const HttpHeader * hdr, http_hdr_type id) +{ + HttpHeaderEntry *e; + time_t value = -1; + assert_eid(id); + assert(Headers[id].type == ftDate_1123); /* must be of an appropriate type */ + if ((e = httpHeaderFindEntry(hdr, id))) { + value = parse_rfc1123(strBuf(e->value)); + httpHeaderNoteParsedEntry(e->id, e->value, value < 0); + } + return value; +} + +/* sync with httpHeaderGetLastStr */ +const char * +httpHeaderGetStr(const HttpHeader * hdr, http_hdr_type id) +{ + HttpHeaderEntry *e; + assert_eid(id); + assert(Headers[id].type == ftStr); /* must be of an appropriate type */ + if ((e = httpHeaderFindEntry(hdr, id))) { + httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */ + return strBuf(e->value); + } + return NULL; +} + +/* unusual */ +const char * +httpHeaderGetLastStr(const HttpHeader * hdr, http_hdr_type id) +{ + HttpHeaderEntry *e; + assert_eid(id); + assert(Headers[id].type == ftStr); /* must be of an appropriate type */ + if ((e = httpHeaderFindLastEntry(hdr, id))) { + httpHeaderNoteParsedEntry(e->id, e->value, 0); /* no errors are possible */ + return strBuf(e->value); + } + return NULL; +} + +HttpHdrCc * +httpHeaderGetCc(const HttpHeader * hdr) +{ + HttpHdrCc *cc; + String s; + if (!CBIT_TEST(hdr->mask, HDR_CACHE_CONTROL)) + return NULL; + s = httpHeaderGetList(hdr, HDR_CACHE_CONTROL); + cc = httpHdrCcParseCreate(&s); + HttpHeaderStats[hdr->owner].ccParsedCount++; + if (cc) + httpHdrCcUpdateStats(cc, &HttpHeaderStats[hdr->owner].ccTypeDistr); + httpHeaderNoteParsedEntry(HDR_CACHE_CONTROL, s, !cc); + stringClean(&s); + return cc; +} + +HttpHdrRange * +httpHeaderGetRange(const HttpHeader * hdr) +{ + HttpHdrRange *r = NULL; + HttpHeaderEntry *e; + /* some clients will send "Request-Range" _and_ *matching* "Range" + * who knows, some clients might send Request-Range only; + * this "if" should work correctly in both cases; + * hopefully no clients send mismatched headers! */ + if ((e = httpHeaderFindEntry(hdr, HDR_RANGE)) || + (e = httpHeaderFindEntry(hdr, HDR_REQUEST_RANGE))) { + r = httpHdrRangeParseCreate(&e->value); + httpHeaderNoteParsedEntry(e->id, e->value, !r); + } + return r; +} + +HttpHdrContRange * +httpHeaderGetContRange(const HttpHeader * hdr) +{ + HttpHdrContRange *cr = NULL; + HttpHeaderEntry *e; + if ((e = httpHeaderFindEntry(hdr, HDR_CONTENT_RANGE))) { + cr = httpHdrContRangeParseCreate(strBuf(e->value)); + httpHeaderNoteParsedEntry(e->id, e->value, !cr); + } + return cr; +} + +const char * +httpHeaderGetAuth(const HttpHeader * hdr, http_hdr_type id, const char *auth_scheme) +{ + const char *field; + int l; + assert(hdr && auth_scheme); + field = httpHeaderGetStr(hdr, id); + if (!field) /* no authorization field */ + return NULL; + l = strlen(auth_scheme); + if (!l || strncasecmp(field, auth_scheme, l)) /* wrong scheme */ + return NULL; + field += l; + if (!xisspace(*field)) /* wrong scheme */ + return NULL; + /* skip white space */ + field += xcountws(field); + if (!*field) /* no authorization cookie */ + return NULL; + return base64_decode(field); +} + +ETag +httpHeaderGetETag(const HttpHeader * hdr, http_hdr_type id) +{ + ETag etag = + {NULL, -1}; + HttpHeaderEntry *e; + assert(Headers[id].type == ftETag); /* must be of an appropriate type */ + if ((e = httpHeaderFindEntry(hdr, id))) + etagParseInit(&etag, strBuf(e->value)); + return etag; +} + +TimeOrTag +httpHeaderGetTimeOrTag(const HttpHeader * hdr, http_hdr_type id) +{ + TimeOrTag tot; + HttpHeaderEntry *e; + assert(Headers[id].type == ftDate_1123_or_ETag); /* must be of an appropriate type */ + memset(&tot, 0, sizeof(tot)); + if ((e = httpHeaderFindEntry(hdr, id))) { + const char *str = strBuf(e->value); + /* try as an ETag */ + if (etagParseInit(&tot.tag, str)) { + tot.valid = tot.tag.str != NULL; + tot.time = -1; + } else { + /* or maybe it is time? */ + tot.time = parse_rfc1123(str); + tot.valid = tot.time >= 0; + tot.tag.str = NULL; + } + } + assert(tot.time < 0 || !tot.tag.str); /* paranoid */ + return tot; +} + +/* + * HttpHeaderEntry + */ + +static HttpHeaderEntry * +httpHeaderEntryCreate(http_hdr_type id, const char *name, const char *value) +{ + HttpHeaderEntry *e; + assert_eid(id); + e = (HttpHeaderEntry *)memAllocate(MEM_HTTP_HDR_ENTRY); + e->id = id; + if (id != HDR_OTHER) + e->name = Headers[id].name; + else + stringInit(&e->name, name); + stringInit(&e->value, value); + Headers[id].stat.aliveCount++; + debug(55, 9) ("created entry %p: '%s: %s'\n", e, strBuf(e->name), strBuf(e->value)); + return e; +} + +static void +httpHeaderEntryDestroy(HttpHeaderEntry * e) +{ + assert(e); + assert_eid(e->id); + debug(55, 9) ("destroying entry %p: '%s: %s'\n", e, strBuf(e->name), strBuf(e->value)); + /* clean name if needed */ + if (e->id == HDR_OTHER) + stringClean(&e->name); + stringClean(&e->value); + assert(Headers[e->id].stat.aliveCount); + Headers[e->id].stat.aliveCount--; + e->id = HDR_BAD_HDR; + memFree(e, MEM_HTTP_HDR_ENTRY); +} + +/* parses and inits header entry, returns new entry on success */ +static HttpHeaderEntry * +httpHeaderEntryParseCreate(const char *field_start, const char *field_end) +{ + HttpHeaderEntry *e; + http_hdr_type id; + /* note: name_start == field_start */ + const char *name_end = strchr(field_start, ':'); + const int name_len = name_end ? name_end - field_start : 0; + const char *value_start = field_start + name_len + 1; /* skip ':' */ + /* note: value_end == field_end */ + + HeaderEntryParsedCount++; + + /* do we have a valid field name within this field? */ + if (!name_len || name_end > field_end) + return NULL; + if (name_len > 65536) { + /* String has a 64K limit */ + debug(55, 1) ("WARNING: ignoring header name of %d bytes\n", name_len); + return NULL; + } + /* now we know we can parse it */ + e = (HttpHeaderEntry *)memAllocate(MEM_HTTP_HDR_ENTRY); + debug(55, 9) ("creating entry %p: near '%s'\n", e, getStringPrefix(field_start, field_end)); + /* is it a "known" field? */ + id = httpHeaderIdByName(field_start, name_len, Headers, HDR_ENUM_END); + if (id < 0) + id = HDR_OTHER; + assert_eid(id); + e->id = id; + /* set field name */ + if (id == HDR_OTHER) + stringLimitInit(&e->name, field_start, name_len); + else + e->name = Headers[id].name; + /* trim field value */ + while (value_start < field_end && xisspace(*value_start)) + value_start++; + if (field_end - value_start > 65536) { + /* String has a 64K limit */ + debug(55, 1) ("WARNING: ignoring '%s' header of %d bytes\n", + strBuf(e->name), (int) (field_end - value_start)); + if (e->id == HDR_OTHER) + stringClean(&e->name); + memFree(e, MEM_HTTP_HDR_ENTRY); + return NULL; + } + /* set field value */ + stringLimitInit(&e->value, value_start, field_end - value_start); + Headers[id].stat.seenCount++; + Headers[id].stat.aliveCount++; + debug(55, 9) ("created entry %p: '%s: %s'\n", e, strBuf(e->name), strBuf(e->value)); + return e; +} + +HttpHeaderEntry * +httpHeaderEntryClone(const HttpHeaderEntry * e) +{ + return httpHeaderEntryCreate(e->id, strBuf(e->name), strBuf(e->value)); +} + +void +httpHeaderEntryPackInto(const HttpHeaderEntry * e, Packer * p) +{ + assert(e && p); + packerAppend(p, strBuf(e->name), strLen(e->name)); + packerAppend(p, ": ", 2); + packerAppend(p, strBuf(e->value), strLen(e->value)); + packerAppend(p, "\r\n", 2); +} + +static void +httpHeaderNoteParsedEntry(http_hdr_type id, String context, int error) +{ + Headers[id].stat.parsCount++; + if (error) { + Headers[id].stat.errCount++; + debug(55, 2) ("cannot parse hdr field: '%s: %s'\n", + strBuf(Headers[id].name), strBuf(context)); + } +} + +/* + * Reports + */ + +/* tmp variable used to pass stat info to dumpers */ +extern const HttpHeaderStat *dump_stat; /* argh! */ +const HttpHeaderStat *dump_stat = NULL; + +static void +httpHeaderFieldStatDumper(StoreEntry * sentry, int idx, double val, double size, int count) +{ + const int id = (int) val; + const int valid_id = id >= 0 && id < HDR_ENUM_END; + const char *name = valid_id ? strBuf(Headers[id].name) : "INVALID"; + int visible = count > 0; + /* for entries with zero count, list only those that belong to current type of message */ + if (!visible && valid_id && dump_stat->owner_mask) + visible = CBIT_TEST(*dump_stat->owner_mask, id); + if (visible) + storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n", + id, name, count, xdiv(count, dump_stat->busyDestroyedCount)); +} + +static void +httpHeaderFldsPerHdrDumper(StoreEntry * sentry, int idx, double val, double size, int count) +{ + if (count) + storeAppendPrintf(sentry, "%2d\t %5d\t %5d\t %6.2f\n", + idx, (int) val, count, + xpercent(count, dump_stat->destroyedCount)); +} + + +static void +httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e) +{ + assert(hs && e); + + dump_stat = hs; + storeAppendPrintf(e, "\nHeader Stats: %s\n", hs->label); + storeAppendPrintf(e, "\nField type distribution\n"); + storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n", + "id", "name", "count", "#/header"); + statHistDump(&hs->fieldTypeDistr, e, httpHeaderFieldStatDumper); + storeAppendPrintf(e, "\nCache-control directives distribution\n"); + storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\n", + "id", "name", "count", "#/cc_field"); + statHistDump(&hs->ccTypeDistr, e, httpHdrCcStatDumper); + storeAppendPrintf(e, "\nNumber of fields per header distribution\n"); + storeAppendPrintf(e, "%2s\t %-5s\t %5s\t %6s\n", + "id", "#flds", "count", "%total"); + statHistDump(&hs->hdrUCountDistr, e, httpHeaderFldsPerHdrDumper); + dump_stat = NULL; +} + +void +httpHeaderStoreReport(StoreEntry * e) +{ + int i; + http_hdr_type ht; + assert(e); + + HttpHeaderStats[0].parsedCount = + HttpHeaderStats[hoRequest].parsedCount + HttpHeaderStats[hoReply].parsedCount; + HttpHeaderStats[0].ccParsedCount = + HttpHeaderStats[hoRequest].ccParsedCount + HttpHeaderStats[hoReply].ccParsedCount; + HttpHeaderStats[0].destroyedCount = + HttpHeaderStats[hoRequest].destroyedCount + HttpHeaderStats[hoReply].destroyedCount; + HttpHeaderStats[0].busyDestroyedCount = + HttpHeaderStats[hoRequest].busyDestroyedCount + HttpHeaderStats[hoReply].busyDestroyedCount; + + for (i = 1; i < HttpHeaderStatCount; i++) { + httpHeaderStatDump(HttpHeaderStats + i, e); + storeAppendPrintf(e, "%s\n", "
"); + } + /* field stats for all messages */ + storeAppendPrintf(e, "\nHttp Fields Stats (replies and requests)\n"); + storeAppendPrintf(e, "%2s\t %-20s\t %5s\t %6s\t %6s\n", + "id", "name", "#alive", "%err", "%repeat"); + for (ht = (http_hdr_type)0; ht < HDR_ENUM_END; ++ht) { + HttpHeaderFieldInfo *f = Headers + ht; + storeAppendPrintf(e, "%2d\t %-20s\t %5d\t %6.3f\t %6.3f\n", + f->id, strBuf(f->name), f->stat.aliveCount, + xpercent(f->stat.errCount, f->stat.parsCount), + xpercent(f->stat.repCount, f->stat.seenCount)); + } + storeAppendPrintf(e, "Headers Parsed: %d + %d = %d\n", + HttpHeaderStats[hoRequest].parsedCount, + HttpHeaderStats[hoReply].parsedCount, + HttpHeaderStats[0].parsedCount); + storeAppendPrintf(e, "Hdr Fields Parsed: %d\n", HeaderEntryParsedCount); +} + +http_hdr_type +httpHeaderIdByName(const char *name, int name_len, const HttpHeaderFieldInfo * info, int end) +{ + int i; + for (i = 0; i < end; ++i) { + if (name_len >= 0 && name_len != strLen(info[i].name)) + continue; + if (!strncasecmp(name, strBuf(info[i].name), + name_len < 0 ? strLen(info[i].name) + 1 : name_len)) + return info[i].id; + } + return HDR_BAD_HDR; +} + +http_hdr_type +httpHeaderIdByNameDef(const char *name, int name_len) +{ + if (!Headers) + Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END); + return httpHeaderIdByName(name, name_len, Headers, HDR_ENUM_END); +} + +const char * +httpHeaderNameById(int id) +{ + if (!Headers) + Headers = httpHeaderBuildFieldsInfo(HeadersAttrs, HDR_ENUM_END); + assert(id >= 0 && id < HDR_ENUM_END); + return HeadersAttrs[id].name; +} --- squid/src/HttpMsg.c Wed Feb 14 01:07:36 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,116 +0,0 @@ - -/* - * $Id: HttpMsg.c,v 1.6 2001/01/12 08:20:32 hno Exp $ - * - * DEBUG: section 74 HTTP Message - * AUTHOR: Alex Rousskov - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid.h" - - -/* find end of headers */ -int -httpMsgIsolateHeaders(const char **parse_start, const char **blk_start, const char **blk_end) -{ - /* - * parse_start points to the first line of HTTP message *headers*, - * not including the request or status lines - */ - size_t l = strlen(*parse_start); - size_t end = headersEnd(*parse_start, l); - int nnl; - if (end) { - *blk_start = *parse_start; - *blk_end = *parse_start + end - 1; - /* - * leave blk_end pointing to the first character after the - * first newline which terminates the headers - */ - assert(**blk_end == '\n'); - while (*(*blk_end - 1) == '\r') - (*blk_end)--; - assert(*(*blk_end - 1) == '\n'); - *parse_start += end; - return 1; - } - /* - * If we didn't find the end of headers, and parse_start does - * NOT point to a CR or NL character, then return failure - */ - if (**parse_start != '\r' && **parse_start != '\n') - return 0; /* failure */ - /* - * If we didn't find the end of headers, and parse_start does point - * to an empty line, then we have empty headers. Skip all CR and - * NL characters up to the first NL. Leave parse_start pointing at - * the first character after the first NL. - */ - *blk_start = *parse_start; - *blk_end = *blk_start; - for (nnl = 0; nnl == 0; (*parse_start)++) { - if (**parse_start == '\r') - (void) 0; - else if (**parse_start == '\n') - nnl++; - else - break; - } - return 1; -} - -/* returns true if connection should be "persistent" - * after processing this message */ -int -httpMsgIsPersistent(http_version_t http_ver, const HttpHeader * hdr) -{ - if ((http_ver.major >= 1) && (http_ver.minor >= 1)) { - /* - * for modern versions of HTTP: persistent unless there is - * a "Connection: close" header. - */ - return !httpHeaderHasConnDir(hdr, "close"); - } else { - /* - * Persistent connections in Netscape 3.x are allegedly broken, - * return false if it is a browser connection. If there is a - * VIA header, then we assume this is NOT a browser connection. - */ - const char *agent = httpHeaderGetStr(hdr, HDR_USER_AGENT); - if (agent && !httpHeaderHas(hdr, HDR_VIA)) { - if (!strncasecmp(agent, "Mozilla/3.", 10)) - return 0; - if (!strncasecmp(agent, "Netscape/3.", 11)) - return 0; - } - /* for old versions of HTTP: persistent if has "keep-alive" */ - return httpHeaderHasConnDir(hdr, "keep-alive"); - } -} --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/HttpMsg.cc Wed Feb 14 01:07:36 2007 @@ -0,0 +1,116 @@ + +/* + * $Id: HttpMsg.cc,v 1.1.2.1 2002/10/08 11:11:34 rbcollins Exp $ + * + * DEBUG: section 74 HTTP Message + * AUTHOR: Alex Rousskov + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + + +/* find end of headers */ +int +httpMsgIsolateHeaders(const char **parse_start, const char **blk_start, const char **blk_end) +{ + /* + * parse_start points to the first line of HTTP message *headers*, + * not including the request or status lines + */ + size_t l = strlen(*parse_start); + size_t end = headersEnd(*parse_start, l); + int nnl; + if (end) { + *blk_start = *parse_start; + *blk_end = *parse_start + end - 1; + /* + * leave blk_end pointing to the first character after the + * first newline which terminates the headers + */ + assert(**blk_end == '\n'); + while (*(*blk_end - 1) == '\r') + (*blk_end)--; + assert(*(*blk_end - 1) == '\n'); + *parse_start += end; + return 1; + } + /* + * If we didn't find the end of headers, and parse_start does + * NOT point to a CR or NL character, then return failure + */ + if (**parse_start != '\r' && **parse_start != '\n') + return 0; /* failure */ + /* + * If we didn't find the end of headers, and parse_start does point + * to an empty line, then we have empty headers. Skip all CR and + * NL characters up to the first NL. Leave parse_start pointing at + * the first character after the first NL. + */ + *blk_start = *parse_start; + *blk_end = *blk_start; + for (nnl = 0; nnl == 0; (*parse_start)++) { + if (**parse_start == '\r') + (void) 0; + else if (**parse_start == '\n') + nnl++; + else + break; + } + return 1; +} + +/* returns true if connection should be "persistent" + * after processing this message */ +int +httpMsgIsPersistent(http_version_t http_ver, const HttpHeader * hdr) +{ + if ((http_ver.major >= 1) && (http_ver.minor >= 1)) { + /* + * for modern versions of HTTP: persistent unless there is + * a "Connection: close" header. + */ + return !httpHeaderHasConnDir(hdr, "close"); + } else { + /* + * Persistent connections in Netscape 3.x are allegedly broken, + * return false if it is a browser connection. If there is a + * VIA header, then we assume this is NOT a browser connection. + */ + const char *agent = httpHeaderGetStr(hdr, HDR_USER_AGENT); + if (agent && !httpHeaderHas(hdr, HDR_VIA)) { + if (!strncasecmp(agent, "Mozilla/3.", 10)) + return 0; + if (!strncasecmp(agent, "Netscape/3.", 11)) + return 0; + } + /* for old versions of HTTP: persistent if has "keep-alive" */ + return httpHeaderHasConnDir(hdr, "keep-alive"); + } +} --- squid/src/HttpReply.c Wed Feb 14 01:07:36 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,491 +0,0 @@ - -/* - * $Id: HttpReply.c,v 1.10 2001/10/24 09:42:11 squidadm Exp $ - * - * DEBUG: section 58 HTTP Reply (Response) - * AUTHOR: Alex Rousskov - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid.h" - - -/* local constants */ - -/* these entity-headers must be ignored if a bogus server sends them in 304 */ -static HttpHeaderMask Denied304HeadersMask; -static http_hdr_type Denied304HeadersArr[] = -{ - HDR_ALLOW, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE, HDR_CONTENT_LENGTH, - HDR_CONTENT_LOCATION, HDR_CONTENT_RANGE, HDR_LAST_MODIFIED, HDR_LINK, - HDR_OTHER -}; - -/* local routines */ -static void httpReplyInit(HttpReply * rep); -static void httpReplyClean(HttpReply * rep); -static void httpReplyDoDestroy(HttpReply * rep); -static void httpReplyHdrCacheInit(HttpReply * rep); -static void httpReplyHdrCacheClean(HttpReply * rep); -static int httpReplyParseStep(HttpReply * rep, const char *parse_start, int atEnd); -static int httpReplyParseError(HttpReply * rep); -static int httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end); -static time_t httpReplyHdrExpirationTime(const HttpReply * rep); - - -/* module initialization */ -void -httpReplyInitModule(void) -{ - httpHeaderMaskInit(&Denied304HeadersMask, 0); - httpHeaderCalcMask(&Denied304HeadersMask, (const int *) Denied304HeadersArr, countof(Denied304HeadersArr)); -} - - -HttpReply * -httpReplyCreate(void) -{ - HttpReply *rep = memAllocate(MEM_HTTP_REPLY); - debug(58, 7) ("creating rep: %p\n", rep); - httpReplyInit(rep); - return rep; -} - -static void -httpReplyInit(HttpReply * rep) -{ - assert(rep); - rep->hdr_sz = 0; - rep->maxBodySize = 0; - rep->pstate = psReadyToParseStartLine; - httpBodyInit(&rep->body); - httpHeaderInit(&rep->header, hoReply); - httpReplyHdrCacheInit(rep); - httpStatusLineInit(&rep->sline); -} - -static void -httpReplyClean(HttpReply * rep) -{ - assert(rep); - httpBodyClean(&rep->body); - httpReplyHdrCacheClean(rep); - httpHeaderClean(&rep->header); - httpStatusLineClean(&rep->sline); -} - -void -httpReplyDestroy(HttpReply * rep) -{ - assert(rep); - debug(58, 7) ("destroying rep: %p\n", rep); - httpReplyClean(rep); - httpReplyDoDestroy(rep); -} - -void -httpReplyReset(HttpReply * rep) -{ - httpReplyClean(rep); - httpReplyInit(rep); -} - -/* absorb: copy the contents of a new reply to the old one, destroy new one */ -void -httpReplyAbsorb(HttpReply * rep, HttpReply * new_rep) -{ - assert(rep && new_rep); - httpReplyClean(rep); - *rep = *new_rep; - /* cannot use Clean() on new reply now! */ - httpReplyDoDestroy(new_rep); -} - -/* - * httpReplyParse takes character buffer of HTTP headers (buf), - * which may not be NULL-terminated, and fills in an HttpReply - * structure (rep). The parameter 'end' specifies the offset to - * the end of the reply headers. The caller may know where the - * end is, but is unable to NULL-terminate the buffer. This function - * returns true on success. - */ -int -httpReplyParse(HttpReply * rep, const char *buf, ssize_t end) -{ - /* - * this extra buffer/copy will be eliminated when headers become - * meta-data in store. Currently we have to xstrncpy the buffer - * becuase somebody may feed a non NULL-terminated buffer to - * us. - */ - char *headers = memAllocate(MEM_4K_BUF); - int success; - size_t s = XMIN(end + 1, 4096); - /* reset current state, because we are not used in incremental fashion */ - httpReplyReset(rep); - /* put a string terminator. s is how many bytes to touch in - * 'buf' including the terminating NULL. */ - xstrncpy(headers, buf, s); - success = httpReplyParseStep(rep, headers, 0); - memFree(headers, MEM_4K_BUF); - return success == 1; -} - -void -httpReplyPackInto(const HttpReply * rep, Packer * p) -{ - assert(rep); - httpStatusLinePackInto(&rep->sline, p); - httpHeaderPackInto(&rep->header, p); - packerAppend(p, "\r\n", 2); - httpBodyPackInto(&rep->body, p); -} - -/* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */ -MemBuf -httpReplyPack(const HttpReply * rep) -{ - MemBuf mb; - Packer p; - assert(rep); - - memBufDefInit(&mb); - packerToMemInit(&p, &mb); - httpReplyPackInto(rep, &p); - packerClean(&p); - return mb; -} - -/* swap: create swap-based packer, pack, destroy packer */ -void -httpReplySwapOut(const HttpReply * rep, StoreEntry * e) -{ - Packer p; - assert(rep && e); - - packerToStoreInit(&p, e); - httpReplyPackInto(rep, &p); - packerClean(&p); -} - -MemBuf -httpPackedReply(http_version_t ver, http_status status, const char *ctype, - int clen, time_t lmt, time_t expires) -{ - HttpReply *rep = httpReplyCreate(); - MemBuf mb; - httpReplySetHeaders(rep, ver, status, ctype, NULL, clen, lmt, expires); - mb = httpReplyPack(rep); - httpReplyDestroy(rep); - return mb; -} - -MemBuf -httpPacked304Reply(const HttpReply * rep) -{ - static const http_hdr_type ImsEntries[] = - {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER}; - int t; - MemBuf mb; - Packer p; - HttpHeaderEntry *e; - assert(rep); - - memBufDefInit(&mb); - packerToMemInit(&p, &mb); - memBufPrintf(&mb, "%s", "HTTP/1.0 304 Not Modified\r\n"); - for (t = 0; ImsEntries[t] != HDR_OTHER; ++t) - if ((e = httpHeaderFindEntry(&rep->header, ImsEntries[t]))) - httpHeaderEntryPackInto(e, &p); - memBufAppend(&mb, "\r\n", 2); - packerClean(&p); - return mb; -} - -void -httpReplySetHeaders(HttpReply * reply, http_version_t ver, http_status status, const char *reason, - const char *ctype, int clen, time_t lmt, time_t expires) -{ - HttpHeader *hdr; - assert(reply); - httpStatusLineSet(&reply->sline, ver, status, reason); - hdr = &reply->header; - httpHeaderPutStr(hdr, HDR_SERVER, full_appname_string); - httpHeaderPutStr(hdr, HDR_MIME_VERSION, "1.0"); - httpHeaderPutTime(hdr, HDR_DATE, squid_curtime); - if (ctype) { - httpHeaderPutStr(hdr, HDR_CONTENT_TYPE, ctype); - stringInit(&reply->content_type, ctype); - } else - reply->content_type = StringNull; - if (clen >= 0) - httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, clen); - if (expires >= 0) - httpHeaderPutTime(hdr, HDR_EXPIRES, expires); - if (lmt > 0) /* this used to be lmt != 0 @?@ */ - httpHeaderPutTime(hdr, HDR_LAST_MODIFIED, lmt); - reply->date = squid_curtime; - reply->content_length = clen; - reply->expires = expires; - reply->last_modified = lmt; -} - -void -httpRedirectReply(HttpReply * reply, http_status status, const char *loc) -{ - HttpHeader *hdr; - http_version_t ver; - assert(reply); - httpBuildVersion(&ver, 1, 0); - httpStatusLineSet(&reply->sline, ver, status, httpStatusString(status)); - hdr = &reply->header; - httpHeaderPutStr(hdr, HDR_SERVER, full_appname_string); - httpHeaderPutTime(hdr, HDR_DATE, squid_curtime); - httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, 0); - httpHeaderPutStr(hdr, HDR_LOCATION, loc); - reply->date = squid_curtime; - reply->content_length = 0; -} - -void -httpReplyUpdateOnNotModified(HttpReply * rep, HttpReply * freshRep) -{ - assert(rep && freshRep); - /* clean cache */ - httpReplyHdrCacheClean(rep); - /* update raw headers */ - httpHeaderUpdate(&rep->header, &freshRep->header, - (const HttpHeaderMask *) &Denied304HeadersMask); - /* init cache */ - httpReplyHdrCacheInit(rep); -} - - -/* internal routines */ - -/* internal function used by Destroy and Absorb */ -static void -httpReplyDoDestroy(HttpReply * rep) -{ - memFree(rep, MEM_HTTP_REPLY); -} - -static time_t -httpReplyHdrExpirationTime(const HttpReply * rep) -{ - /* The s-maxage and max-age directive takes priority over Expires */ - if (rep->cache_control) { - if (rep->date >= 0) { - if (rep->cache_control->s_maxage >= 0) - return rep->date + rep->cache_control->s_maxage; - if (rep->cache_control->max_age >= 0) - return rep->date + rep->cache_control->max_age; - } else { - /* - * Conservatively handle the case when we have a max-age - * header, but no Date for reference? - */ - if (rep->cache_control->s_maxage >= 0) - return squid_curtime; - if (rep->cache_control->max_age >= 0) - return squid_curtime; - } - } - if (Config.onoff.vary_ignore_expire && - httpHeaderHas(&rep->header, HDR_VARY)) { - const time_t d = httpHeaderGetTime(&rep->header, HDR_DATE); - const time_t e = httpHeaderGetTime(&rep->header, HDR_EXPIRES); - if (d == e) - return -1; - } - if (httpHeaderHas(&rep->header, HDR_EXPIRES)) { - const time_t e = httpHeaderGetTime(&rep->header, HDR_EXPIRES); - /* - * HTTP/1.0 says that robust implementations should consider - * bad or malformed Expires header as equivalent to "expires - * immediately." - */ - return e < 0 ? squid_curtime : e; - } - return -1; -} - -/* sync this routine when you update HttpReply struct */ -static void -httpReplyHdrCacheInit(HttpReply * rep) -{ - const HttpHeader *hdr = &rep->header; - const char *str; - rep->content_length = httpHeaderGetInt(hdr, HDR_CONTENT_LENGTH); - rep->date = httpHeaderGetTime(hdr, HDR_DATE); - rep->last_modified = httpHeaderGetTime(hdr, HDR_LAST_MODIFIED); - str = httpHeaderGetStr(hdr, HDR_CONTENT_TYPE); - if (str) - stringLimitInit(&rep->content_type, str, strcspn(str, ";\t ")); - else - rep->content_type = StringNull; - rep->cache_control = httpHeaderGetCc(hdr); - rep->content_range = httpHeaderGetContRange(hdr); - rep->keep_alive = httpMsgIsPersistent(rep->sline.version, &rep->header); - /* be sure to set expires after date and cache-control */ - rep->expires = httpReplyHdrExpirationTime(rep); -} - -/* sync this routine when you update HttpReply struct */ -static void -httpReplyHdrCacheClean(HttpReply * rep) -{ - stringClean(&rep->content_type); - if (rep->cache_control) - httpHdrCcDestroy(rep->cache_control); - if (rep->content_range) - httpHdrContRangeDestroy(rep->content_range); -} - -/* - * parses a 0-terminating buffer into HttpReply. - * Returns: - * +1 -- success - * 0 -- need more data (partial parse) - * -1 -- parse error - */ -static int -httpReplyParseStep(HttpReply * rep, const char *buf, int atEnd) -{ - const char *parse_start = buf; - const char *blk_start, *blk_end; - const char **parse_end_ptr = &blk_end; - assert(rep); - assert(parse_start); - assert(rep->pstate < psParsed); - - *parse_end_ptr = parse_start; - if (rep->pstate == psReadyToParseStartLine) { - if (!httpReplyIsolateStart(&parse_start, &blk_start, &blk_end)) - return 0; - if (!httpStatusLineParse(&rep->sline, blk_start, blk_end)) - return httpReplyParseError(rep); - - *parse_end_ptr = parse_start; - rep->hdr_sz = *parse_end_ptr - buf; - rep->pstate++; - } - if (rep->pstate == psReadyToParseHeaders) { - if (!httpMsgIsolateHeaders(&parse_start, &blk_start, &blk_end)) { - if (atEnd) - blk_start = parse_start, blk_end = blk_start + strlen(blk_start); - else - return 0; - } - if (!httpHeaderParse(&rep->header, blk_start, blk_end)) - return httpReplyParseError(rep); - - httpReplyHdrCacheInit(rep); - - *parse_end_ptr = parse_start; - rep->hdr_sz = *parse_end_ptr - buf; - rep->pstate++; - } - return 1; -} - -/* handy: resets and returns -1 */ -static int -httpReplyParseError(HttpReply * rep) -{ - assert(rep); - /* reset */ - httpReplyReset(rep); - /* indicate an error */ - rep->sline.status = HTTP_INVALID_HEADER; - return -1; -} - -/* find first CRLF */ -static int -httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end) -{ - int slen = strcspn(*parse_start, "\r\n"); - if (!(*parse_start)[slen]) /* no CRLF found */ - return 0; - - *blk_start = *parse_start; - *blk_end = *blk_start + slen; - while (**blk_end == '\r') /* CR */ - (*blk_end)++; - if (**blk_end == '\n') /* LF */ - (*blk_end)++; - - *parse_start = *blk_end; - return 1; -} - -/* - * Returns the body size of a HTTP response - */ -int -httpReplyBodySize(method_t method, HttpReply * reply) -{ - if (METHOD_HEAD == method) - return 0; - else if (reply->sline.status == HTTP_OK) - (void) 0; /* common case, continue */ - else if (reply->sline.status == HTTP_NO_CONTENT) - return 0; - else if (reply->sline.status == HTTP_NOT_MODIFIED) - return 0; - else if (reply->sline.status < HTTP_OK) - return 0; - return reply->content_length; -} - -/* - * Calculates the maximum size allowed for an HTTP response - */ -void -httpReplyBodyBuildSize(request_t * request, HttpReply * reply, dlink_list * bodylist) -{ - body_size *bs; - aclCheck_t *checklist; - bs = (body_size *) bodylist->head; - while (bs) { - checklist = aclChecklistCreate(bs->access_list, request, NULL); - checklist->reply = reply; - if (1 != aclCheckFast(bs->access_list, checklist)) { - /* deny - skip this entry */ - bs = (body_size *) bs->node.next; - } else { - /* Allow - use this entry */ - reply->maxBodySize = bs->maxsize; - bs = NULL; - debug(58, 3) ("httpReplyBodyBuildSize: Setting maxBodySize to %ld\n", (long int) reply->maxBodySize); - } - aclChecklistFree(checklist); - } -} --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/HttpReply.cc Wed Feb 14 01:07:36 2007 @@ -0,0 +1,498 @@ + +/* + * $Id: HttpReply.cc,v 1.1.2.1 2002/10/08 11:11:34 rbcollins Exp $ + * + * DEBUG: section 58 HTTP Reply (Response) + * AUTHOR: Alex Rousskov + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + + +/* local constants */ + +/* these entity-headers must be ignored if a bogus server sends them in 304 */ +static HttpHeaderMask Denied304HeadersMask; +static http_hdr_type Denied304HeadersArr[] = +{ + HDR_ALLOW, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE, HDR_CONTENT_LENGTH, + HDR_CONTENT_LOCATION, HDR_CONTENT_RANGE, HDR_LAST_MODIFIED, HDR_LINK, + HDR_OTHER +}; + +HttpMsgParseState &operator++ (HttpMsgParseState &aState) +{ + aState = (HttpMsgParseState)(++(int)aState); + return aState; +} + + +/* local routines */ +static void httpReplyInit(HttpReply * rep); +static void httpReplyClean(HttpReply * rep); +static void httpReplyDoDestroy(HttpReply * rep); +static void httpReplyHdrCacheInit(HttpReply * rep); +static void httpReplyHdrCacheClean(HttpReply * rep); +static int httpReplyParseStep(HttpReply * rep, const char *parse_start, int atEnd); +static int httpReplyParseError(HttpReply * rep); +static int httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end); +static time_t httpReplyHdrExpirationTime(const HttpReply * rep); + + +/* module initialization */ +void +httpReplyInitModule(void) +{ + httpHeaderMaskInit(&Denied304HeadersMask, 0); + httpHeaderCalcMask(&Denied304HeadersMask, (const int *) Denied304HeadersArr, countof(Denied304HeadersArr)); +} + + +HttpReply * +httpReplyCreate(void) +{ + HttpReply *rep = (HttpReply *)memAllocate(MEM_HTTP_REPLY); + debug(58, 7) ("creating rep: %p\n", rep); + httpReplyInit(rep); + return rep; +} + +static void +httpReplyInit(HttpReply * rep) +{ + assert(rep); + rep->hdr_sz = 0; + rep->maxBodySize = 0; + rep->pstate = psReadyToParseStartLine; + httpBodyInit(&rep->body); + httpHeaderInit(&rep->header, hoReply); + httpReplyHdrCacheInit(rep); + httpStatusLineInit(&rep->sline); +} + +static void +httpReplyClean(HttpReply * rep) +{ + assert(rep); + httpBodyClean(&rep->body); + httpReplyHdrCacheClean(rep); + httpHeaderClean(&rep->header); + httpStatusLineClean(&rep->sline); +} + +void +httpReplyDestroy(HttpReply * rep) +{ + assert(rep); + debug(58, 7) ("destroying rep: %p\n", rep); + httpReplyClean(rep); + httpReplyDoDestroy(rep); +} + +void +httpReplyReset(HttpReply * rep) +{ + httpReplyClean(rep); + httpReplyInit(rep); +} + +/* absorb: copy the contents of a new reply to the old one, destroy new one */ +void +httpReplyAbsorb(HttpReply * rep, HttpReply * new_rep) +{ + assert(rep && new_rep); + httpReplyClean(rep); + *rep = *new_rep; + /* cannot use Clean() on new reply now! */ + httpReplyDoDestroy(new_rep); +} + +/* + * httpReplyParse takes character buffer of HTTP headers (buf), + * which may not be NULL-terminated, and fills in an HttpReply + * structure (rep). The parameter 'end' specifies the offset to + * the end of the reply headers. The caller may know where the + * end is, but is unable to NULL-terminate the buffer. This function + * returns true on success. + */ +int +httpReplyParse(HttpReply * rep, const char *buf, ssize_t end) +{ + /* + * this extra buffer/copy will be eliminated when headers become + * meta-data in store. Currently we have to xstrncpy the buffer + * becuase somebody may feed a non NULL-terminated buffer to + * us. + */ + char *headers = (char *)memAllocate(MEM_4K_BUF); + int success; + size_t s = XMIN(end + 1, 4096); + /* reset current state, because we are not used in incremental fashion */ + httpReplyReset(rep); + /* put a string terminator. s is how many bytes to touch in + * 'buf' including the terminating NULL. */ + xstrncpy(headers, buf, s); + success = httpReplyParseStep(rep, headers, 0); + memFree(headers, MEM_4K_BUF); + return success == 1; +} + +void +httpReplyPackInto(const HttpReply * rep, Packer * p) +{ + assert(rep); + httpStatusLinePackInto(&rep->sline, p); + httpHeaderPackInto(&rep->header, p); + packerAppend(p, "\r\n", 2); + httpBodyPackInto(&rep->body, p); +} + +/* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */ +MemBuf +httpReplyPack(const HttpReply * rep) +{ + MemBuf mb; + Packer p; + assert(rep); + + memBufDefInit(&mb); + packerToMemInit(&p, &mb); + httpReplyPackInto(rep, &p); + packerClean(&p); + return mb; +} + +/* swap: create swap-based packer, pack, destroy packer */ +void +httpReplySwapOut(const HttpReply * rep, StoreEntry * e) +{ + Packer p; + assert(rep && e); + + packerToStoreInit(&p, e); + httpReplyPackInto(rep, &p); + packerClean(&p); +} + +MemBuf +httpPackedReply(http_version_t ver, http_status status, const char *ctype, + int clen, time_t lmt, time_t expires) +{ + HttpReply *rep = httpReplyCreate(); + MemBuf mb; + httpReplySetHeaders(rep, ver, status, ctype, NULL, clen, lmt, expires); + mb = httpReplyPack(rep); + httpReplyDestroy(rep); + return mb; +} + +MemBuf +httpPacked304Reply(const HttpReply * rep) +{ + static const http_hdr_type ImsEntries[] = + {HDR_DATE, HDR_CONTENT_TYPE, HDR_EXPIRES, HDR_LAST_MODIFIED, /* eof */ HDR_OTHER}; + int t; + MemBuf mb; + Packer p; + HttpHeaderEntry *e; + assert(rep); + + memBufDefInit(&mb); + packerToMemInit(&p, &mb); + memBufPrintf(&mb, "%s", "HTTP/1.0 304 Not Modified\r\n"); + for (t = 0; ImsEntries[t] != HDR_OTHER; ++t) + if ((e = httpHeaderFindEntry(&rep->header, ImsEntries[t]))) + httpHeaderEntryPackInto(e, &p); + memBufAppend(&mb, "\r\n", 2); + packerClean(&p); + return mb; +} + +void +httpReplySetHeaders(HttpReply * reply, http_version_t ver, http_status status, const char *reason, + const char *ctype, int clen, time_t lmt, time_t expires) +{ + HttpHeader *hdr; + assert(reply); + httpStatusLineSet(&reply->sline, ver, status, reason); + hdr = &reply->header; + httpHeaderPutStr(hdr, HDR_SERVER, full_appname_string); + httpHeaderPutStr(hdr, HDR_MIME_VERSION, "1.0"); + httpHeaderPutTime(hdr, HDR_DATE, squid_curtime); + if (ctype) { + httpHeaderPutStr(hdr, HDR_CONTENT_TYPE, ctype); + stringInit(&reply->content_type, ctype); + } else + reply->content_type = StringNull; + if (clen >= 0) + httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, clen); + if (expires >= 0) + httpHeaderPutTime(hdr, HDR_EXPIRES, expires); + if (lmt > 0) /* this used to be lmt != 0 @?@ */ + httpHeaderPutTime(hdr, HDR_LAST_MODIFIED, lmt); + reply->date = squid_curtime; + reply->content_length = clen; + reply->expires = expires; + reply->last_modified = lmt; +} + +void +httpRedirectReply(HttpReply * reply, http_status status, const char *loc) +{ + HttpHeader *hdr; + http_version_t ver; + assert(reply); + httpBuildVersion(&ver, 1, 0); + httpStatusLineSet(&reply->sline, ver, status, httpStatusString(status)); + hdr = &reply->header; + httpHeaderPutStr(hdr, HDR_SERVER, full_appname_string); + httpHeaderPutTime(hdr, HDR_DATE, squid_curtime); + httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, 0); + httpHeaderPutStr(hdr, HDR_LOCATION, loc); + reply->date = squid_curtime; + reply->content_length = 0; +} + +void +httpReplyUpdateOnNotModified(HttpReply * rep, HttpReply * freshRep) +{ + assert(rep && freshRep); + /* clean cache */ + httpReplyHdrCacheClean(rep); + /* update raw headers */ + httpHeaderUpdate(&rep->header, &freshRep->header, + (const HttpHeaderMask *) &Denied304HeadersMask); + /* init cache */ + httpReplyHdrCacheInit(rep); +} + + +/* internal routines */ + +/* internal function used by Destroy and Absorb */ +static void +httpReplyDoDestroy(HttpReply * rep) +{ + memFree(rep, MEM_HTTP_REPLY); +} + +static time_t +httpReplyHdrExpirationTime(const HttpReply * rep) +{ + /* The s-maxage and max-age directive takes priority over Expires */ + if (rep->cache_control) { + if (rep->date >= 0) { + if (rep->cache_control->s_maxage >= 0) + return rep->date + rep->cache_control->s_maxage; + if (rep->cache_control->max_age >= 0) + return rep->date + rep->cache_control->max_age; + } else { + /* + * Conservatively handle the case when we have a max-age + * header, but no Date for reference? + */ + if (rep->cache_control->s_maxage >= 0) + return squid_curtime; + if (rep->cache_control->max_age >= 0) + return squid_curtime; + } + } + if (Config.onoff.vary_ignore_expire && + httpHeaderHas(&rep->header, HDR_VARY)) { + const time_t d = httpHeaderGetTime(&rep->header, HDR_DATE); + const time_t e = httpHeaderGetTime(&rep->header, HDR_EXPIRES); + if (d == e) + return -1; + } + if (httpHeaderHas(&rep->header, HDR_EXPIRES)) { + const time_t e = httpHeaderGetTime(&rep->header, HDR_EXPIRES); + /* + * HTTP/1.0 says that robust implementations should consider + * bad or malformed Expires header as equivalent to "expires + * immediately." + */ + return e < 0 ? squid_curtime : e; + } + return -1; +} + +/* sync this routine when you update HttpReply struct */ +static void +httpReplyHdrCacheInit(HttpReply * rep) +{ + const HttpHeader *hdr = &rep->header; + const char *str; + rep->content_length = httpHeaderGetInt(hdr, HDR_CONTENT_LENGTH); + rep->date = httpHeaderGetTime(hdr, HDR_DATE); + rep->last_modified = httpHeaderGetTime(hdr, HDR_LAST_MODIFIED); + str = httpHeaderGetStr(hdr, HDR_CONTENT_TYPE); + if (str) + stringLimitInit(&rep->content_type, str, strcspn(str, ";\t ")); + else + rep->content_type = StringNull; + rep->cache_control = httpHeaderGetCc(hdr); + rep->content_range = httpHeaderGetContRange(hdr); + rep->keep_alive = httpMsgIsPersistent(rep->sline.version, &rep->header); + /* be sure to set expires after date and cache-control */ + rep->expires = httpReplyHdrExpirationTime(rep); +} + +/* sync this routine when you update HttpReply struct */ +static void +httpReplyHdrCacheClean(HttpReply * rep) +{ + stringClean(&rep->content_type); + if (rep->cache_control) + httpHdrCcDestroy(rep->cache_control); + if (rep->content_range) + httpHdrContRangeDestroy(rep->content_range); +} + +/* + * parses a 0-terminating buffer into HttpReply. + * Returns: + * +1 -- success + * 0 -- need more data (partial parse) + * -1 -- parse error + */ +static int +httpReplyParseStep(HttpReply * rep, const char *buf, int atEnd) +{ + const char *parse_start = buf; + const char *blk_start, *blk_end; + const char **parse_end_ptr = &blk_end; + assert(rep); + assert(parse_start); + assert(rep->pstate < psParsed); + + *parse_end_ptr = parse_start; + if (rep->pstate == psReadyToParseStartLine) { + if (!httpReplyIsolateStart(&parse_start, &blk_start, &blk_end)) + return 0; + if (!httpStatusLineParse(&rep->sline, blk_start, blk_end)) + return httpReplyParseError(rep); + + *parse_end_ptr = parse_start; + rep->hdr_sz = *parse_end_ptr - buf; + ++rep->pstate; + } + if (rep->pstate == psReadyToParseHeaders) { + if (!httpMsgIsolateHeaders(&parse_start, &blk_start, &blk_end)) { + if (atEnd) + blk_start = parse_start, blk_end = blk_start + strlen(blk_start); + else + return 0; + } + if (!httpHeaderParse(&rep->header, blk_start, blk_end)) + return httpReplyParseError(rep); + + httpReplyHdrCacheInit(rep); + + *parse_end_ptr = parse_start; + rep->hdr_sz = *parse_end_ptr - buf; + ++rep->pstate; + } + return 1; +} + +/* handy: resets and returns -1 */ +static int +httpReplyParseError(HttpReply * rep) +{ + assert(rep); + /* reset */ + httpReplyReset(rep); + /* indicate an error */ + rep->sline.status = HTTP_INVALID_HEADER; + return -1; +} + +/* find first CRLF */ +static int +httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end) +{ + int slen = strcspn(*parse_start, "\r\n"); + if (!(*parse_start)[slen]) /* no CRLF found */ + return 0; + + *blk_start = *parse_start; + *blk_end = *blk_start + slen; + while (**blk_end == '\r') /* CR */ + (*blk_end)++; + if (**blk_end == '\n') /* LF */ + (*blk_end)++; + + *parse_start = *blk_end; + return 1; +} + +/* + * Returns the body size of a HTTP response + */ +int +httpReplyBodySize(method_t method, HttpReply * reply) +{ + if (METHOD_HEAD == method) + return 0; + else if (reply->sline.status == HTTP_OK) + (void) 0; /* common case, continue */ + else if (reply->sline.status == HTTP_NO_CONTENT) + return 0; + else if (reply->sline.status == HTTP_NOT_MODIFIED) + return 0; + else if (reply->sline.status < HTTP_OK) + return 0; + return reply->content_length; +} + +/* + * Calculates the maximum size allowed for an HTTP response + */ +void +httpReplyBodyBuildSize(request_t * request, HttpReply * reply, dlink_list * bodylist) +{ + body_size *bs; + aclCheck_t *checklist; + bs = (body_size *) bodylist->head; + while (bs) { + checklist = aclChecklistCreate(bs->access_list, request, NULL); + checklist->reply = reply; + if (1 != aclCheckFast(bs->access_list, checklist)) { + /* deny - skip this entry */ + bs = (body_size *) bs->node.next; + } else { + /* Allow - use this entry */ + reply->maxBodySize = bs->maxsize; + bs = NULL; + debug(58, 3) ("httpReplyBodyBuildSize: Setting maxBodySize to %ld\n", (long int) reply->maxBodySize); + } + aclChecklistFree(checklist); + } +} --- squid/src/HttpStatusLine.c Wed Feb 14 01:07:36 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,239 +0,0 @@ - -/* - * $Id: HttpStatusLine.c,v 1.9 2002/09/23 21:45:36 squidadm Exp $ - * - * DEBUG: section 57 HTTP Status-line - * AUTHOR: Alex Rousskov - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid.h" - - -/* local constants */ -const char *HttpStatusLineFormat = "HTTP/%d.%d %3d %s\r\n"; - -void -httpStatusLineInit(HttpStatusLine * sline) -{ - http_version_t version; - httpBuildVersion(&version, 0, 0); - httpStatusLineSet(sline, version, HTTP_STATUS_NONE, NULL); -} - -void -httpStatusLineClean(HttpStatusLine * sline) -{ - http_version_t version; - httpBuildVersion(&version, 0, 0); - httpStatusLineSet(sline, version, HTTP_INTERNAL_SERVER_ERROR, NULL); -} - -/* set values */ -void -httpStatusLineSet(HttpStatusLine * sline, http_version_t version, http_status status, const char *reason) -{ - assert(sline); - sline->version = version; - sline->status = status; - /* Note: no xstrdup for 'reason', assumes constant 'reasons' */ - sline->reason = reason; -} - -/* parse a 0-terminating buffer and fill internal structures; returns true on success */ -void -httpStatusLinePackInto(const HttpStatusLine * sline, Packer * p) -{ - assert(sline && p); - debug(57, 9) ("packing sline %p using %p:\n", sline, p); - debug(57, 9) (HttpStatusLineFormat, sline->version.major, - sline->version.minor, sline->status, - sline->reason ? sline->reason : httpStatusString(sline->status)); - packerPrintf(p, HttpStatusLineFormat, sline->version.major, - sline->version.minor, sline->status, httpStatusLineReason(sline)); -} - -/* pack fields using Packer */ -int -httpStatusLineParse(HttpStatusLine * sline, const char *start, const char *end) -{ - assert(sline); - sline->status = HTTP_INVALID_HEADER; /* Squid header parsing error */ - if (strncasecmp(start, "HTTP/", 5)) - return 0; - start += 5; - if (!xisdigit(*start)) - return 0; - if (sscanf(start, "%d.%d", &sline->version.major, &sline->version.minor) != 2) { - debug(57, 7) ("httpStatusLineParse: Invalid HTTP identifier.\n"); - } - if (!(start = strchr(start, ' '))) - return 0; - sline->status = (http_status) atoi(++start); - /* we ignore 'reason-phrase' */ - return 1; /* success */ -} - -const char * -httpStatusLineReason(const HttpStatusLine * sline) -{ - assert(sline); - return sline->reason ? sline->reason : httpStatusString(sline->status); -} - -const char * -httpStatusString(http_status status) -{ - /* why not to return matching string instead of using "p" ? @?@ */ - const char *p = NULL; - switch (status) { - case 0: - p = "Init"; /* we init .status with code 0 */ - break; - case HTTP_CONTINUE: - p = "Continue"; - break; - case HTTP_SWITCHING_PROTOCOLS: - p = "Switching Protocols"; - break; - case HTTP_OK: - p = "OK"; - break; - case HTTP_CREATED: - p = "Created"; - break; - case HTTP_ACCEPTED: - p = "Accepted"; - break; - case HTTP_NON_AUTHORITATIVE_INFORMATION: - p = "Non-Authoritative Information"; - break; - case HTTP_NO_CONTENT: - p = "No Content"; - break; - case HTTP_RESET_CONTENT: - p = "Reset Content"; - break; - case HTTP_PARTIAL_CONTENT: - p = "Partial Content"; - break; - case HTTP_MULTIPLE_CHOICES: - p = "Multiple Choices"; - break; - case HTTP_MOVED_PERMANENTLY: - p = "Moved Permanently"; - break; - case HTTP_MOVED_TEMPORARILY: - p = "Moved Temporarily"; - break; - case HTTP_SEE_OTHER: - p = "See Other"; - break; - case HTTP_NOT_MODIFIED: - p = "Not Modified"; - break; - case HTTP_USE_PROXY: - p = "Use Proxy"; - break; - case HTTP_TEMPORARY_REDIRECT: - p = "Temporary Redirect"; - break; - case HTTP_BAD_REQUEST: - p = "Bad Request"; - break; - case HTTP_UNAUTHORIZED: - p = "Unauthorized"; - break; - case HTTP_PAYMENT_REQUIRED: - p = "Payment Required"; - break; - case HTTP_FORBIDDEN: - p = "Forbidden"; - break; - case HTTP_NOT_FOUND: - p = "Not Found"; - break; - case HTTP_METHOD_NOT_ALLOWED: - p = "Method Not Allowed"; - break; - case HTTP_NOT_ACCEPTABLE: - p = "Not Acceptable"; - break; - case HTTP_PROXY_AUTHENTICATION_REQUIRED: - p = "Proxy Authentication Required"; - break; - case HTTP_REQUEST_TIMEOUT: - p = "Request Time-out"; - break; - case HTTP_CONFLICT: - p = "Conflict"; - break; - case HTTP_GONE: - p = "Gone"; - break; - case HTTP_LENGTH_REQUIRED: - p = "Length Required"; - break; - case HTTP_PRECONDITION_FAILED: - p = "Precondition Failed"; - break; - case HTTP_REQUEST_ENTITY_TOO_LARGE: - p = "Request Entity Too Large"; - break; - case HTTP_REQUEST_URI_TOO_LARGE: - p = "Request-URI Too Large"; - break; - case HTTP_UNSUPPORTED_MEDIA_TYPE: - p = "Unsupported Media Type"; - break; - case HTTP_INTERNAL_SERVER_ERROR: - p = "Internal Server Error"; - break; - case HTTP_NOT_IMPLEMENTED: - p = "Not Implemented"; - break; - case HTTP_BAD_GATEWAY: - p = "Bad Gateway"; - break; - case HTTP_SERVICE_UNAVAILABLE: - p = "Service Unavailable"; - break; - case HTTP_GATEWAY_TIMEOUT: - p = "Gateway Time-out"; - break; - case HTTP_HTTP_VERSION_NOT_SUPPORTED: - p = "HTTP Version not supported"; - break; - default: - p = "Unknown"; - debug(57, 3) ("Unknown HTTP status code: %d\n", status); - break; - } - return p; -} --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/HttpStatusLine.cc Wed Feb 14 01:07:36 2007 @@ -0,0 +1,239 @@ + +/* + * $Id: HttpStatusLine.cc,v 1.1.2.1 2002/10/08 11:11:34 rbcollins Exp $ + * + * DEBUG: section 57 HTTP Status-line + * AUTHOR: Alex Rousskov + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + + +/* local constants */ +const char *HttpStatusLineFormat = "HTTP/%d.%d %3d %s\r\n"; + +void +httpStatusLineInit(HttpStatusLine * sline) +{ + http_version_t version; + httpBuildVersion(&version, 0, 0); + httpStatusLineSet(sline, version, HTTP_STATUS_NONE, NULL); +} + +void +httpStatusLineClean(HttpStatusLine * sline) +{ + http_version_t version; + httpBuildVersion(&version, 0, 0); + httpStatusLineSet(sline, version, HTTP_INTERNAL_SERVER_ERROR, NULL); +} + +/* set values */ +void +httpStatusLineSet(HttpStatusLine * sline, http_version_t version, http_status status, const char *reason) +{ + assert(sline); + sline->version = version; + sline->status = status; + /* Note: no xstrdup for 'reason', assumes constant 'reasons' */ + sline->reason = reason; +} + +/* parse a 0-terminating buffer and fill internal structures; returns true on success */ +void +httpStatusLinePackInto(const HttpStatusLine * sline, Packer * p) +{ + assert(sline && p); + debug(57, 9) ("packing sline %p using %p:\n", sline, p); + debug(57, 9) (HttpStatusLineFormat, sline->version.major, + sline->version.minor, sline->status, + sline->reason ? sline->reason : httpStatusString(sline->status)); + packerPrintf(p, HttpStatusLineFormat, sline->version.major, + sline->version.minor, sline->status, httpStatusLineReason(sline)); +} + +/* pack fields using Packer */ +int +httpStatusLineParse(HttpStatusLine * sline, const char *start, const char *end) +{ + assert(sline); + sline->status = HTTP_INVALID_HEADER; /* Squid header parsing error */ + if (strncasecmp(start, "HTTP/", 5)) + return 0; + start += 5; + if (!xisdigit(*start)) + return 0; + if (sscanf(start, "%d.%d", &sline->version.major, &sline->version.minor) != 2) { + debug(57, 7) ("httpStatusLineParse: Invalid HTTP identifier.\n"); + } + if (!(start = strchr(start, ' '))) + return 0; + sline->status = (http_status) atoi(++start); + /* we ignore 'reason-phrase' */ + return 1; /* success */ +} + +const char * +httpStatusLineReason(const HttpStatusLine * sline) +{ + assert(sline); + return sline->reason ? sline->reason : httpStatusString(sline->status); +} + +const char * +httpStatusString(http_status status) +{ + /* why not to return matching string instead of using "p" ? @?@ */ + const char *p = NULL; + switch (status) { + case 0: + p = "Init"; /* we init .status with code 0 */ + break; + case HTTP_CONTINUE: + p = "Continue"; + break; + case HTTP_SWITCHING_PROTOCOLS: + p = "Switching Protocols"; + break; + case HTTP_OK: + p = "OK"; + break; + case HTTP_CREATED: + p = "Created"; + break; + case HTTP_ACCEPTED: + p = "Accepted"; + break; + case HTTP_NON_AUTHORITATIVE_INFORMATION: + p = "Non-Authoritative Information"; + break; + case HTTP_NO_CONTENT: + p = "No Content"; + break; + case HTTP_RESET_CONTENT: + p = "Reset Content"; + break; + case HTTP_PARTIAL_CONTENT: + p = "Partial Content"; + break; + case HTTP_MULTIPLE_CHOICES: + p = "Multiple Choices"; + break; + case HTTP_MOVED_PERMANENTLY: + p = "Moved Permanently"; + break; + case HTTP_MOVED_TEMPORARILY: + p = "Moved Temporarily"; + break; + case HTTP_SEE_OTHER: + p = "See Other"; + break; + case HTTP_NOT_MODIFIED: + p = "Not Modified"; + break; + case HTTP_USE_PROXY: + p = "Use Proxy"; + break; + case HTTP_TEMPORARY_REDIRECT: + p = "Temporary Redirect"; + break; + case HTTP_BAD_REQUEST: + p = "Bad Request"; + break; + case HTTP_UNAUTHORIZED: + p = "Unauthorized"; + break; + case HTTP_PAYMENT_REQUIRED: + p = "Payment Required"; + break; + case HTTP_FORBIDDEN: + p = "Forbidden"; + break; + case HTTP_NOT_FOUND: + p = "Not Found"; + break; + case HTTP_METHOD_NOT_ALLOWED: + p = "Method Not Allowed"; + break; + case HTTP_NOT_ACCEPTABLE: + p = "Not Acceptable"; + break; + case HTTP_PROXY_AUTHENTICATION_REQUIRED: + p = "Proxy Authentication Required"; + break; + case HTTP_REQUEST_TIMEOUT: + p = "Request Time-out"; + break; + case HTTP_CONFLICT: + p = "Conflict"; + break; + case HTTP_GONE: + p = "Gone"; + break; + case HTTP_LENGTH_REQUIRED: + p = "Length Required"; + break; + case HTTP_PRECONDITION_FAILED: + p = "Precondition Failed"; + break; + case HTTP_REQUEST_ENTITY_TOO_LARGE: + p = "Request Entity Too Large"; + break; + case HTTP_REQUEST_URI_TOO_LARGE: + p = "Request-URI Too Large"; + break; + case HTTP_UNSUPPORTED_MEDIA_TYPE: + p = "Unsupported Media Type"; + break; + case HTTP_INTERNAL_SERVER_ERROR: + p = "Internal Server Error"; + break; + case HTTP_NOT_IMPLEMENTED: + p = "Not Implemented"; + break; + case HTTP_BAD_GATEWAY: + p = "Bad Gateway"; + break; + case HTTP_SERVICE_UNAVAILABLE: + p = "Service Unavailable"; + break; + case HTTP_GATEWAY_TIMEOUT: + p = "Gateway Time-out"; + break; + case HTTP_HTTP_VERSION_NOT_SUPPORTED: + p = "HTTP Version not supported"; + break; + default: + p = "Unknown"; + debug(57, 3) ("Unknown HTTP status code: %d\n", status); + break; + } + return p; +} Index: squid/src/Makefile.am =================================================================== RCS file: /cvsroot/squid-sf//squid/src/Makefile.am,v retrieving revision 1.29.2.12 retrieving revision 1.29.2.13 diff -u -r1.29.2.12 -r1.29.2.13 --- squid/src/Makefile.am 8 Oct 2002 04:06:35 -0000 1.29.2.12 +++ squid/src/Makefile.am 8 Oct 2002 11:11:34 -0000 1.29.2.13 @@ -156,15 +156,15 @@ helper.c \ $(HTCPSOURCE) \ http.cc \ - HttpStatusLine.c \ + HttpStatusLine.cc \ HttpHdrCc.cc \ - HttpHdrRange.c \ - HttpHdrContRange.c \ - HttpHeader.c \ - HttpHeaderTools.c \ + HttpHdrRange.cc \ + HttpHdrContRange.cc \ + HttpHeader.cc \ + HttpHeaderTools.cc \ HttpBody.cc \ - HttpMsg.c \ - HttpReply.c \ + HttpMsg.cc \ + HttpReply.cc \ HttpRequest.cc \ icmp.c \ icp_v2.cc \ Index: squid/src/enums.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/enums.h,v retrieving revision 1.40.2.5 retrieving revision 1.40.2.6 diff -u -r1.40.2.5 -r1.40.2.6 --- squid/src/enums.h 8 Oct 2002 04:06:35 -0000 1.40.2.5 +++ squid/src/enums.h 8 Oct 2002 11:11:34 -0000 1.40.2.6 @@ -1,6 +1,6 @@ /* - * $Id: enums.h,v 1.40.2.5 2002/10/08 04:06:35 rbcollins Exp $ + * $Id: enums.h,v 1.40.2.6 2002/10/08 11:11:34 rbcollins Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -183,7 +183,8 @@ /* recognized or "known" header fields; @?@ add more! */ typedef enum { - HDR_ACCEPT, + HDR_BAD_HDR = -1, + HDR_ACCEPT = 0, HDR_ACCEPT_CHARSET, HDR_ACCEPT_ENCODING, HDR_ACCEPT_LANGUAGE, @@ -251,6 +252,7 @@ } http_hdr_type; typedef enum { + CC_BADHDR = -1, CC_PUBLIC = 0, CC_PRIVATE, CC_NO_CACHE,