This patch is generated from the icap-2_6 branch of HEAD in squid Tue Mar 6 01:17:22 2007 GMT See http://devel.squid-cache.org/ Index: squid/configure.in diff -u squid/configure.in:1.175 squid/configure.in:1.87.4.16 --- squid/configure.in:1.175 Sat Mar 3 10:54:22 2007 +++ squid/configure.in Mon Mar 5 14:00:30 2007 @@ -530,6 +530,17 @@ fi ]) +dnl Enable ICAP Support +AM_CONDITIONAL(USE_ICAP, false) +AC_ARG_ENABLE(icap-support, +[ --enable-icap-support Enable ICAP client capability], +[ if test "$enableval" = "yes" ; then + echo "ICAP support enabled" + AC_DEFINE(HS_FEAT_ICAP, 1, [Content filtering via ICAP servers.]) + AM_CONDITIONAL(USE_ICAP, true) + fi +]) + dnl This is a developer only option. Developers know how to set defines dnl dnl AC_ARG_ENABLE(mem-gen-trace, @@ -2348,6 +2359,8 @@ srand48 \ srandom \ statfs \ + strnstr \ + strcasestr \ strsep \ strtoll \ sysconf \ @@ -2533,6 +2546,16 @@ AM_CONDITIONAL(NEED_OWN_SNPRINTF, true) fi +AM_CONDITIONAL(NEED_OWN_STRNSTR, false) +if test "$ac_cv_func_strnstr" = "no" || test "$ac_cv_func_vstrnstr" = "no" ; then + AM_CONDITIONAL(NEED_OWN_STRNSTR, true) +fi + +AM_CONDITIONAL(NEED_OWN_STRCASESTR, false) +if test "$ac_cv_func_strcasestr" = "no" || test "$ac_cv_func_vstrcasestr" = "no"; then + AM_CONDITIONAL(NEED_OWN_STRCASESTR, true) +fi + AM_CONDITIONAL(NEED_OWN_STRSEP, false) if test "$ac_cv_func_strsep" = "no" ; then AM_CONDITIONAL(NEED_OWN_STRSEP, true) Index: squid/errors/list diff -u squid/errors/list:1.4 squid/errors/list:1.4.10.1 --- squid/errors/list:1.4 Fri Apr 28 04:10:45 2006 +++ squid/errors/list Wed May 17 10:57:59 2006 @@ -28,3 +28,4 @@ ERR_URN_RESOLVE ERR_WRITE_ERROR ERR_ZERO_SIZE_OBJECT +ERR_ICAP_FAILURE Index: squid/errors/Bulgarian/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Bulgarian/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Bulgarian/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Catalan/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Catalan/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Catalan/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Czech/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Czech/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Czech/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Danish/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Danish/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Danish/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Dutch/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Dutch/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Dutch/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/English/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/English/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/English/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Estonian/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Estonian/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Estonian/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Finnish/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Finnish/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Finnish/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/French/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/French/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/French/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/German/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/German/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/German/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,33 @@ + + +FEHLER: Der angeforderte URL konnte nicht geholt werden + + +

FEHLER

+

Der angeforderte URL konnte nicht geholt werden

+
+

+Während des Versuches, den URL
+%U + +
+zu laden, trat der folgende Fehler auf: +

+ +

+

+Es trat ein Problem bei der ICAP-Kommunikation auf. Mögliche Gründe: +

+

+ +

Ihr Cache Administrator ist %w. + Index: squid/errors/Greek/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Greek/ERR_ICAP_FAILURE:1.1.12.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Greek/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Hebrew/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Hebrew/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Hebrew/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Hungarian/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Hungarian/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Hungarian/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Italian/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Italian/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Italian/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Japanese/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Japanese/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Japanese/ERR_ICAP_FAILURE Wed May 17 10:57:59 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Korean/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Korean/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Korean/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Lithuanian/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Lithuanian/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Lithuanian/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Polish/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Polish/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Polish/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Portuguese/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Portuguese/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Portuguese/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Romanian/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Romanian/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Romanian/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Russian-1251/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Russian-1251/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Russian-1251/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Russian-koi8-r/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Russian-koi8-r/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Russian-koi8-r/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Serbian/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Serbian/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Serbian/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Simplify_Chinese/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Simplify_Chinese/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Simplify_Chinese/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Slovak/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Slovak/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Slovak/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Spanish/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Spanish/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Spanish/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Swedish/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Swedish/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Swedish/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Traditional_Chinese/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Traditional_Chinese/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Traditional_Chinese/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/errors/Turkish/ERR_ICAP_FAILURE diff -u /dev/null squid/errors/Turkish/ERR_ICAP_FAILURE:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/errors/Turkish/ERR_ICAP_FAILURE Wed May 17 10:58:00 2006 @@ -0,0 +1,31 @@ + + +ERROR: The requested URL could not be retrieved + + +

ERROR

+

The requested URL could not be retrieved

+
+

+While attempting to retrieve the URL: +%U +

+the following error was encountered: +

+ +

+

+Some aspect of the ICAP communication failed. Possible problems: +

+

+ +

Your cache administrator is %w. + Index: squid/include/util.h diff -u squid/include/util.h:1.17 squid/include/util.h:1.13.8.3 --- squid/include/util.h:1.17 Sun Dec 10 05:56:25 2006 +++ squid/include/util.h Tue Dec 12 14:49:41 2006 @@ -157,4 +157,12 @@ extern int WIN32_Close_FD_Socket(int); #endif +#ifndef HAVE_STRNSTR +extern char *strnstr(const char *haystack, const char *needle, size_t haystacklen); +#endif + +#ifndef HAVE_STRCASESTR +extern char *strcasestr(const char *haystack, const char *needle); +#endif + #endif /* SQUID_UTIL_H */ Index: squid/lib/Makefile.am diff -u squid/lib/Makefile.am:1.10 squid/lib/Makefile.am:1.7.10.4 --- squid/lib/Makefile.am:1.10 Fri Sep 8 12:50:55 2006 +++ squid/lib/Makefile.am Tue Sep 26 15:47:31 2006 @@ -8,6 +8,19 @@ else SNPRINTFSOURCE= endif + +if NEED_OWN_STRNSTR +STRNSTRSOURCE=strnstr.c +else +STRNSTRSOURCE= +endif + +if NEED_OWN_STRCASESTR +STRCASESTRSOURCE=strcasestr.c +else +STRCASESTRSOURCE= +endif + if NEED_OWN_STRSEP STRSEPSOURCE=strsep.c else @@ -63,6 +76,8 @@ $(SNPRINTFSOURCE) \ splay.c \ Stack.c \ + $(STRNSTRSOURCE) \ + $(STRCASESTRSOURCE) \ $(STRSEPSOURCE) \ stub_memaccount.c \ util.c \ Index: squid/lib/strcasestr.c diff -u /dev/null squid/lib/strcasestr.c:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/lib/strcasestr.c Wed May 17 10:58:00 2006 @@ -0,0 +1,126 @@ +/* Return the offset of one string within another. + Copyright (C) 1994,1996,1997,1998,1999,2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * My personal strstr() implementation that beats most other algorithms. + * Until someone tells me otherwise, I assume that this is the + * fastest implementation of strstr() in C. + * I deliberately chose not to comment it. You should have at least + * as much fun trying to understand it, as I had to write it :-). + * + * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */ + +/* + * modified to work outside of glibc (rhorstmann, 06/04/2004) + */ + +#include "config.h" +#ifndef HAVE_STRCASESTR +#include + +typedef unsigned chartype; + +char * +strcasestr (phaystack, pneedle) + const char *phaystack; + const char *pneedle; +{ + register const unsigned char *haystack, *needle; + register chartype b, c; + + haystack = (const unsigned char *) phaystack; + needle = (const unsigned char *) pneedle; + + b = tolower (*needle); + if (b != '\0') + { + haystack--; /* possible ANSI violation */ + do + { + c = *++haystack; + if (c == '\0') + goto ret0; + } + while (tolower (c) != (int) b); + + c = tolower (*++needle); + if (c == '\0') + goto foundneedle; + ++needle; + goto jin; + + for (;;) + { + register chartype a; + register const unsigned char *rhaystack, *rneedle; + + do + { + a = *++haystack; + if (a == '\0') + goto ret0; + if (tolower (a) == (int) b) + break; + a = *++haystack; + if (a == '\0') + goto ret0; +shloop: + ; + } + while (tolower (a) != (int) b); + +jin: a = *++haystack; + if (a == '\0') + goto ret0; + + if (tolower (a) != (int) c) + goto shloop; + + rhaystack = haystack-- + 1; + rneedle = needle; + a = tolower (*rneedle); + + if (tolower (*rhaystack) == (int) a) + do + { + if (a == '\0') + goto foundneedle; + ++rhaystack; + a = tolower (*++needle); + if (tolower (*rhaystack) != (int) a) + break; + if (a == '\0') + goto foundneedle; + ++rhaystack; + a = tolower (*++needle); + } + while (tolower (*rhaystack) == (int) a); + + needle = rneedle; /* took the register-poor approach */ + + if (a == '\0') + break; + } + } +foundneedle: + return (char*) haystack; +ret0: + return 0; +} +#endif Index: squid/lib/strnstr.c diff -u /dev/null squid/lib/strnstr.c:1.1.14.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/lib/strnstr.c Wed May 17 10:58:00 2006 @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2003 Nikos Mavroyanopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + /* + * DW 2003/10/17: + * Changed 'ssize_t' types to 'size_t' + */ + +#include "config.h" +#ifndef HAVE_STRNSTR +#include +#include + +char *strnstr(const char *haystack, const char *needle, size_t haystacklen) +{ + char *p; + size_t plen; + size_t len = strlen(needle); + + if (*needle == '\0') /* everything matches empty string */ + return (char*) haystack; + + plen = haystacklen; + for (p = (char*) haystack; p != NULL; p = memchr(p + 1, *needle, plen-1)) { + plen = haystacklen - (p - haystack); + + if (plen < len) return NULL; + + if (strncmp(p, needle, len) == 0) + return (p); + } + return NULL; +} +#endif Index: squid/src/Makefile.am diff -u squid/src/Makefile.am:1.49 squid/src/Makefile.am:1.34.4.10 --- squid/src/Makefile.am:1.49 Sun Feb 25 03:54:33 2007 +++ squid/src/Makefile.am Tue Feb 27 13:57:17 2007 @@ -6,6 +6,12 @@ # Uncomment and customize the following to suit your needs: # +if USE_ICAP +ICAPSOURCE = icap_common.c icap_reqmod.c icap_respmod.c icap_opt.c +else +ICAPSOURCE = +endif + if USE_DNSSERVER DNSSOURCE = dns.c DNSSERVER = dnsserver @@ -181,6 +187,7 @@ HttpMsg.c \ HttpReply.c \ HttpRequest.c \ + $(ICAPSOURCE) \ icmp.c \ icp_v2.c \ icp_v3.c \ Index: squid/src/MemBuf.c diff -u squid/src/MemBuf.c:1.11 squid/src/MemBuf.c:1.9.10.4 --- squid/src/MemBuf.c:1.11 Tue Aug 15 17:53:02 2006 +++ squid/src/MemBuf.c Mon Aug 21 12:48:09 2006 @@ -341,3 +341,15 @@ assert(mb); memBufPrintf(mb, "memBufReport is not yet implemented @?@\n"); } + +int +memBufRead(int fd, MemBuf * mb) +{ + int len; + if (mb->capacity == mb->size) + memBufGrow(mb, SQUID_TCP_SO_RCVBUF); + len = FD_READ_METHOD(fd, mb->buf + mb->size, mb->capacity - mb->size); + if (len > 0) + mb->size += len; + return len; +} Index: squid/src/cache_cf.c diff -u squid/src/cache_cf.c:1.92 squid/src/cache_cf.c:1.61.4.14 --- squid/src/cache_cf.c:1.92 Sun Feb 25 03:54:33 2007 +++ squid/src/cache_cf.c Tue Feb 27 13:57:17 2007 @@ -2387,6 +2387,587 @@ return bodylist.head == NULL; } +#ifdef HS_FEAT_ICAP + +/*************************************************** + * prototypes + */ +static int icap_service_process(icap_service * s); +static void icap_service_init(icap_service * s); +static void icap_service_destroy(icap_service * s); +icap_service *icap_service_lookup(char *name); +static int icap_class_process(icap_class * c); +static void icap_class_destroy(icap_class * c); +static void icap_access_destroy(icap_access * a); +static void dump_wordlist(StoreEntry * entry, const char *name, const wordlist * list); +static void icap_class_add(icap_class * c); + +/*************************************************** + * icap_service + */ + +/* + * example: + * icap_service reqmode_precache 0 icap://192.168.0.1:1344/respmod + */ + +static void +parse_icap_service_type(IcapConfig * cfg) +{ + char *token; + icap_service *A = NULL; + icap_service *B = NULL; + icap_service **T = NULL; + + A = cbdataAlloc(icap_service); + icap_service_init(A); + parse_string(&A->name); + parse_string(&A->type_name); + parse_ushort(&A->bypass); + parse_string(&A->uri); + while ((token = strtok(NULL, w_space))) { + if (strcasecmp(token, "no-keep-alive") == 0) { + A->keep_alive = 0; + } else { + debug(3, 0) ("parse_peer: token='%s'\n", token); + self_destruct(); + } + } + debug(3, 5) ("parse_icap_service_type (line %d): %s %s %d %s\n", config_lineno, A->name, A->type_name, A->bypass, A->name); + if (icap_service_process(A)) { + /* put into linked list */ + for (B = cfg->service_head, T = &cfg->service_head; B; T = &B->next, B = B->next); + *T = A; + } else { + /* clean up structure */ + debug(3, 0) ("parse_icap_service_type (line %d): skipping %s\n", config_lineno, A->name); + icap_service_destroy(A); + cbdataFree(A); + } + +} + +static void +dump_icap_service_type(StoreEntry * e, const char *name, IcapConfig cfg) +{ + icap_service *current_node = NULL; + + if (!cfg.service_head) { + storeAppendPrintf(e, "%s 0\n", name); + return; + } + current_node = cfg.service_head; + + while (current_node) { + storeAppendPrintf(e, "%s %s %s %d %s", name, current_node->name, current_node->type_name, current_node->bypass, current_node->uri); + if (current_node->keep_alive == 0) { + storeAppendPrintf(e, " no-keep-alive"); + } + storeAppendPrintf(e, "\n"); + current_node = current_node->next; + } + +} + +static void +free_icap_service_type(IcapConfig * cfg) +{ + while (cfg->service_head) { + icap_service *current_node = cfg->service_head; + cfg->service_head = current_node->next; + icap_service_destroy(current_node); + cbdataFree(current_node); + } +} + +/* + * parse the raw string and cache some parts that are needed later + * returns 1 if everything was ok + */ +static int +icap_service_process(icap_service * s) +{ + char *start, *end, *tempEnd; + char *tailp; + unsigned int len; + int port_in_uri, resource_in_uri = 0; + s->type = icapServiceToType(s->type_name); + if (s->type >= ICAP_SERVICE_MAX) { + debug(3, 0) ("icap_service_process (line %d): wrong service type %s\n", config_lineno, s->type_name); + return 0; + } + if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) + s->method = ICAP_METHOD_REQMOD; + else if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) + s->method = ICAP_METHOD_REQMOD; + else if (s->type == ICAP_SERVICE_REQMOD_POSTCACHE) + s->method = ICAP_METHOD_REQMOD; + else if (s->type == ICAP_SERVICE_RESPMOD_PRECACHE) + s->method = ICAP_METHOD_RESPMOD; + else if (s->type == ICAP_SERVICE_RESPMOD_POSTCACHE) + s->method = ICAP_METHOD_RESPMOD; + debug(3, 5) ("icap_service_process (line %d): type=%s\n", config_lineno, icapServiceToStr(s->type)); + if (strncmp(s->uri, "icap://", 7) != 0) { + debug(3, 0) ("icap_service_process (line %d): wrong uri: %s\n", config_lineno, s->uri); + return 0; + } + start = s->uri + 7; + if ((end = strchr(start, ':')) != NULL) { + /* ok */ + port_in_uri = 1; + debug(3, 5) ("icap_service_process (line %d): port given\n", config_lineno); + } else { + /* ok */ + port_in_uri = 0; + debug(3, 5) ("icap_service_process (line %d): no port given\n", config_lineno); + } + + if ((tempEnd = strchr(start, '/')) != NULL) { + /* ok */ + resource_in_uri = 1; + debug(3, 5) ("icap_service_process (line %d): resource given\n", config_lineno); + if (end == '\0') { + end = tempEnd; + } + } else { + /* ok */ + resource_in_uri = 0; + debug(3, 5) ("icap_service_process (line %d): no resource given\n", config_lineno); + } + + tempEnd = strchr(start, '\0'); + if (end == '\0') { + end = tempEnd; + } + len = end - start; + s->hostname = xstrndup(start, len + 1); + s->hostname[len] = 0; + debug(3, 5) ("icap_service_process (line %d): hostname=%s\n", config_lineno, s->hostname); + start = end; + + if (port_in_uri) { + start++; /* skip ':' */ + if (resource_in_uri) + end = strchr(start, '/'); + else + end = strchr(start, '\0'); + s->port = strtoul(start, &tailp, 0) % 65536; + if (tailp != end) { + debug(3, 0) ("icap_service_process (line %d): wrong service uri (port could not be parsed): %s\n", config_lineno, s->uri); + return 0; + } + debug(3, 5) ("icap_service_process (line %d): port=%d\n", config_lineno, s->port); + start = end; + } else { + /* no explicit ICAP port; first ask by getservbyname or default to + * hardwired port 1344 per ICAP specification section 4.2 */ + struct servent *serv = getservbyname("icap", "tcp"); + if (serv) { + s->port = htons(serv->s_port); + debug(3, 5) ("icap_service_process (line %d): default port=%d getservbyname(icap,tcp)\n", config_lineno, s->port); + } else { + s->port = 1344; + debug(3, 5) ("icap_service_process (line %d): default hardwired port=%d\n", config_lineno, s->port); + } + } + + if (resource_in_uri) { + start++; /* skip '/' */ + /* the rest is resource name */ + end = strchr(start, '\0'); + len = end - start; + if (len > 1024) { + debug(3, 0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno); + } + s->resource = xstrndup(start, len + 1); + s->resource[len] = 0; + debug(3, 5) ("icap_service_process (line %d): service=%s\n", config_lineno, s->resource); + } + /* check bypass */ + if ((s->bypass != 0) && (s->bypass != 1)) { + debug(3, 0) ("icap_service_process (line %d): invalid bypass value\n", config_lineno); + return 0; + } + return 1; +} + +/* + * constructor + */ +static void +icap_service_init(icap_service * s) +{ + s->type = ICAP_SERVICE_MAX; /* means undefined */ + s->preview = Config.icapcfg.preview_size; + s->opt = 0; + s->keep_alive = 1; + s->istag = StringNull; + s->transfer_preview = StringNull; + s->transfer_ignore = StringNull; + s->transfer_complete = StringNull; +} + +/* + * destructor + * frees only strings, but don't touch the linked list + */ +static void +icap_service_destroy(icap_service * s) +{ + xfree(s->name); + xfree(s->uri); + xfree(s->type_name); + xfree(s->hostname); + xfree(s->resource); + assert(s->opt == 0); /* there should be no opt request running now */ + stringClean(&s->istag); + stringClean(&s->transfer_preview); + stringClean(&s->transfer_ignore); + stringClean(&s->transfer_complete); +} + +icap_service * +icap_service_lookup(char *name) +{ + icap_service *iter; + for (iter = Config.icapcfg.service_head; iter; iter = iter->next) { + if (!strcmp(name, iter->name)) { + return iter; + } + } + return NULL; +} + +/*************************************************** + * icap_service_list + */ + +static void +icap_service_list_add(icap_service_list ** isl, char *service_name) +{ + icap_service_list **iter; + icap_service_list *new; + icap_service *gbl_service; + int i; + int max_services; + + new = memAllocate(MEM_ICAP_SERVICE_LIST); + /* Found all services with that name, and add to the array */ + max_services = sizeof(new->services) / sizeof(icap_service *); + gbl_service = Config.icapcfg.service_head; + i = 0; + while (gbl_service && i < max_services) { + if (!strcmp(service_name, gbl_service->name)) + new->services[i++] = gbl_service; + gbl_service = gbl_service->next; + } + new->nservices = i; + + if (*isl) { + iter = isl; + while ((*iter)->next) + iter = &((*iter)->next); + (*iter)->next = new; + } else { + *isl = new; + } +} + +/* + * free the linked list without touching references icap_service + */ +static void +icap_service_list_destroy(icap_service_list * isl) +{ + icap_service_list *current; + icap_service_list *next; + + current = isl; + while (current) { + next = current->next; + memFree(current, MEM_ICAP_SERVICE_LIST); + current = next; + } +} + +/*************************************************** + * icap_class + */ +static void +parse_icap_class_type(IcapConfig * cfg) +{ + icap_class *s = NULL; + + s = memAllocate(MEM_ICAP_CLASS); + parse_string(&s->name); + parse_wordlist(&s->services); + + if (icap_class_process(s)) { + /* if ok, put into linked list */ + icap_class_add(s); + } else { + /* clean up structure */ + debug(3, 0) ("parse_icap_class_type (line %d): skipping %s\n", config_lineno, s->name); + icap_class_destroy(s); + memFree(s, MEM_ICAP_CLASS); + } +} + +static void +dump_icap_class_type(StoreEntry * e, const char *name, IcapConfig cfg) +{ + icap_class *current_node = NULL; + LOCAL_ARRAY(char, nom, 64); + + if (!cfg.class_head) { + storeAppendPrintf(e, "%s 0\n", name); + return; + } + current_node = cfg.class_head; + + while (current_node) { + snprintf(nom, 64, "%s %s", name, current_node->name); + dump_wordlist(e, nom, current_node->services); + current_node = current_node->next; + } +} + +static void +free_icap_class_type(IcapConfig * cfg) +{ + while (cfg->class_head) { + icap_class *current_node = cfg->class_head; + cfg->class_head = current_node->next; + icap_class_destroy(current_node); + memFree(current_node, MEM_ICAP_CLASS); + } +} + +/* + * process services list, return 1, if at least one service was found + */ +static int +icap_class_process(icap_class * c) +{ + icap_service_list *isl = NULL; + wordlist *iter; + icap_service *service; + /* take services list and build icap_service_list from it */ + for (iter = c->services; iter; iter = iter->next) { + service = icap_service_lookup(iter->key); + if (service) { + icap_service_list_add(&isl, iter->key); + } else { + debug(3, 0) ("icap_class_process (line %d): skipping service %s in class %s\n", config_lineno, iter->key, c->name); + } + } + + if (isl) { + c->isl = isl; + return 1; + } + return 0; +} + +/* + * search for an icap_class in the global IcapConfig + * classes with hidden-flag are skipped + */ +static icap_class * +icap_class_lookup(char *name) +{ + icap_class *iter; + for (iter = Config.icapcfg.class_head; iter; iter = iter->next) { + if ((!strcmp(name, iter->name)) && (!iter->hidden)) { + return iter; + } + } + return NULL; +} + +/* + * adds an icap_class to the global IcapConfig + */ +static void +icap_class_add(icap_class * c) +{ + icap_class *cp = NULL; + icap_class **t = NULL; + IcapConfig *cfg = &Config.icapcfg; + if (c) { + for (cp = cfg->class_head, t = &cfg->class_head; cp; t = &cp->next, cp = cp->next); + *t = c; + } +} + +/* + * free allocated memory inside icap_class + */ +static void +icap_class_destroy(icap_class * c) +{ + xfree(c->name); + wordlistDestroy(&c->services); + icap_service_list_destroy(c->isl); +} + +/*************************************************** + * icap_access + */ + +/* format: icap_access {allow|deny} acl, ... */ +static void +parse_icap_access_type(IcapConfig * cfg) +{ + icap_access *A = NULL; + icap_access *B = NULL; + icap_access **T = NULL; + icap_service *s = NULL; + icap_class *c = NULL; + ushort no_class = 0; + + A = memAllocate(MEM_ICAP_ACCESS); + parse_string(&A->service_name); + + /* + * try to find a class with the given name first. if not found, search + * the services. if a service is found, create a new hidden class with + * only this service. this is for backward compatibility. + * + * the special classname All is allowed only in deny rules, because + * the class is not used there. + */ + if (!strcmp(A->service_name, "None")) { + no_class = 1; + } else { + A->class = icap_class_lookup(A->service_name); + if (!A->class) { + s = icap_service_lookup(A->service_name); + if (s) { + c = memAllocate(MEM_ICAP_CLASS); + c->name = xstrdup("(hidden)"); + c->hidden = 1; + wordlistAdd(&c->services, A->service_name); + c->isl = memAllocate(MEM_ICAP_SERVICE_LIST); + /* FIXME:luc: check what access do */ + c->isl->services[0] = s; + c->isl->nservices = 1; + icap_class_add(c); + A->class = c; + } else { + debug(3, 0) ("parse_icap_access_type (line %d): servicename %s not found. skipping.\n", config_lineno, A->service_name); + memFree(A, MEM_ICAP_ACCESS); + return; + } + } + } + + aclParseAccessLine(&(A->access)); + debug(3, 5) ("parse_icap_access_type (line %d): %s\n", config_lineno, A->service_name); + + /* check that All class is only used in deny rule */ + if (no_class && A->access->allow) { + memFree(A, MEM_ICAP_ACCESS); + debug(3, 0) ("parse_icap_access (line %d): special class 'None' only allowed in deny rule. skipping.\n", config_lineno); + return; + } + if (A->access) { + for (B = cfg->access_head, T = &cfg->access_head; B; T = &B->next, B = B->next); + *T = A; + } else { + debug(3, 0) ("parse_icap_access_type (line %d): invalid line skipped\n", config_lineno); + memFree(A, MEM_ICAP_ACCESS); + } +} + +static void +dump_icap_access_type(StoreEntry * e, const char *name, IcapConfig cfg) +{ + icap_access *current_node = NULL; + LOCAL_ARRAY(char, nom, 64); + + if (!cfg.access_head) { + storeAppendPrintf(e, "%s 0\n", name); + return; + } + current_node = cfg.access_head; + + while (current_node) { + snprintf(nom, 64, "%s %s", name, current_node->service_name); + dump_acl_access(e, nom, current_node->access); + current_node = current_node->next; + } +} + +static void +free_icap_access_type(IcapConfig * cfg) +{ + while (cfg->access_head) { + icap_access *current_node = cfg->access_head; + cfg->access_head = current_node->next; + icap_access_destroy(current_node); + memFree(current_node, MEM_ICAP_ACCESS); + } +} + +/* + * destructor + * frees everything but the linked list + */ +static void +icap_access_destroy(icap_access * a) +{ + xfree(a->service_name); + aclDestroyAccessList(&a->access); +} + +/*************************************************** + * for debugging purposes only + */ +void +dump_icap_config(IcapConfig * cfg) +{ + icap_service *s_iter; + icap_class *c_iter; + icap_access *a_iter; + icap_service_list *isl_iter; + acl_list *l; + debug(3, 0) ("IcapConfig: onoff = %d\n", cfg->onoff); + debug(3, 0) ("IcapConfig: service_head = %d\n", (int) cfg->service_head); + debug(3, 0) ("IcapConfig: class_head = %d\n", (int) cfg->class_head); + debug(3, 0) ("IcapConfig: access_head = %d\n", (int) cfg->access_head); + + debug(3, 0) ("IcapConfig: services =\n"); + for (s_iter = cfg->service_head; s_iter; s_iter = s_iter->next) { + printf(" %s: \n", s_iter->name); + printf(" bypass = %d\n", s_iter->bypass); + printf(" hostname = %s\n", s_iter->hostname); + printf(" port = %d\n", s_iter->port); + printf(" resource = %s\n", s_iter->resource); + } + debug(3, 0) ("IcapConfig: classes =\n"); + for (c_iter = cfg->class_head; c_iter; c_iter = c_iter->next) { + printf(" %s: \n", c_iter->name); + printf(" services = \n"); + for (isl_iter = c_iter->isl; isl_iter; isl_iter = isl_iter->next) { + int i; + for (i = 0; i < isl_iter->nservices; i++) + printf(" %s\n", isl_iter->services[i]->name); + } + } + debug(3, 0) ("IcapConfig: access =\n"); + for (a_iter = cfg->access_head; a_iter; a_iter = a_iter->next) { + printf(" service_name = %s\n", a_iter->service_name); + printf(" access = %s", a_iter->access->allow ? "allow" : "deny"); + for (l = a_iter->access->acl_list; l != NULL; l = l->next) { + printf(" %s%s", + l->op ? null_string : "!", + l->acl->name); + } + printf("\n"); + } +} +#endif /* HS_FEAT_ICAP */ static void parse_kb_size_t(squid_off_t * var) Index: squid/src/cbdata.c diff -u squid/src/cbdata.c:1.18 squid/src/cbdata.c:1.18.8.1 --- squid/src/cbdata.c:1.18 Fri May 12 15:51:56 2006 +++ squid/src/cbdata.c Wed May 17 10:58:00 2006 @@ -179,6 +179,10 @@ CREATE_CBDATA(statefulhelper); CREATE_CBDATA(helper_stateful_server); CREATE_CBDATA(HttpStateData); +#ifdef HS_FEAT_ICAP + CREATE_CBDATA(IcapStateData); + CREATE_CBDATA(icap_service); +#endif CREATE_CBDATA_FREE(peer, peerDestroy); CREATE_CBDATA(ps_state); CREATE_CBDATA(RemovalPolicy); Index: squid/src/cf.data.pre diff -u squid/src/cf.data.pre:1.169 squid/src/cf.data.pre:1.100.4.14 --- squid/src/cf.data.pre:1.169 Sun Feb 25 03:54:34 2007 +++ squid/src/cf.data.pre Tue Feb 27 13:57:19 2007 @@ -3200,7 +3200,6 @@ ensure correct results it is best to set server_persistent_connections to off when using this directive in such configurations. DOC_END - NAME: reply_header_max_size COMMENT: (KB) TYPE: b_size_t @@ -3469,6 +3468,187 @@ DOC_END COMMENT_START + ICAP OPTIONS + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: icap_enable +TYPE: onoff +IFDEF: HS_FEAT_ICAP +COMMENT: on|off +LOC: Config.icapcfg.onoff +DEFAULT: off +DOC_START + If you want to enable the ICAP client module, set this to on. +DOC_END + +NAME: icap_preview_enable +TYPE: onoff +IFDEF: HS_FEAT_ICAP +COMMENT: on|off +LOC: Config.icapcfg.preview_enable +DEFAULT: off +DOC_START + Set this to 'on' if you want to enable the ICAP preview + feature in Squid. +DOC_END + +NAME: icap_preview_size +TYPE: int +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg.preview_size +DEFAULT: -1 +DOC_START + The default size of preview data to be sent to the ICAP server. + -1 means no preview. This value might be overwritten on a per server + basis by OPTIONS requests. +DOC_END + +NAME: icap_check_interval +TYPE: int +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg.check_interval +DEFAULT: 300 +DOC_START + If an ICAP server does not respond, it gets marked as unreachable. Squid + will try again to reach it after this time. +DOC_END + +NAME: icap_send_client_ip +TYPE: onoff +IFDEF: HS_FEAT_ICAP +COMMENT: on|off +LOC: Config.icapcfg.send_client_ip +DEFAULT: off +DOC_START + Allows Squid to add the "X-Client-IP" header if requested by + an ICAP service in it's response to OPTIONS. +DOC_END + +NAME: icap_send_server_ip +TYPE: onoff +IFDEF: HS_FEAT_ICAP +COMMENT: on|off +LOC: Config.icapcfg.send_server_ip +DEFAULT: off +DOC_START + Allows Squid to add the "X-Server-IP" header if requested by + an ICAP service in it's response to OPTIONS. +DOC_END + +NAME: icap_send_auth_user +TYPE: onoff +IFDEF: HS_FEAT_ICAP +COMMENT: on|off +LOC: Config.icapcfg.send_auth_user +DEFAULT: off +DOC_START + Allows Squid to add the "X-Authenticated-User" header if requested + by an ICAP service in it's response to OPTIONS. +DOC_END + +NAME: icap_auth_scheme +TYPE: string +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg.auth_scheme +DEFAULT: Local://%u +DOC_START + Authentification scheme to pass to ICAP requests if + icap_send_auth_user is enabled. The first occurence of "%u" + is replaced by the authentified user name. If no "%u" is found, + the username is added at the end of the scheme. + + See http://www.ietf.org/internet-drafts/draft-stecher-icap-subid-00.txt, + section 3.4 for details on this. + + Examples: + + icap_auth_scheme Local://%u + icap_auth_scheme LDAP://ldap-server/cn=%u,dc=company,dc=com + icap_auth_scheme WinNT://nt-domain/%u + icap_auth_scheme Radius://radius-server/%u +DOC_END + +NAME: icap_service +TYPE: icap_service_type +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg +DEFAULT: none +DOC_START + Defines a single ICAP service + + icap_service servicename vectoring_point bypass service_url [options ...] + + vectoring_point = reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache + This specifies at which point of request processing the ICAP + service should be plugged in. + bypass = 1|0 + If set to 1 and the ICAP server cannot be reached, the request will go + through without being processed by an ICAP server + service_url = icap://servername:port/service + + Options: + + no-keep-alive To always close the connection to icap server + after the transaction completes + + + Note: reqmod_precache and respmod_postcache is not yet implemented + + Load-balancing and high availability: + You can obtain load-balancing and high availability by defining a + named service with different definitions. Then, the client + loops through the different entities of the service providing + load-balancing. If an entity is marked as unreachable, the client goes + one step further to the next entity: you have the high-availability. + See the service_1 definition below + +Example: +icap_service service_1 reqmod_precache 0 icap://icap1.mydomain.net:1344/reqmod +icap_service service_1 reqmod_precache 0 icap://icap2.mydomain.net:1344/reqmod no-keep-alive +icap_service service_2 respmod_precache 0 icap://icap3.mydomain.net:1344/respmod +DOC_END + +NAME: icap_class +TYPE: icap_class_type +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg +DEFAULT: none +DOC_START + Defines an ICAP service chain. If there are multiple services per + vectoring point, they are processed in the specified order. + + icap_class classname servicename... + +Example: +icap_class class_1 service_1 service_2 +icap class class_2 service_1 service_3 +DOC_END + +NAME: icap_access +TYPE: icap_access_type +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg +DEFAULT: none +DOC_START + Redirects a request through an ICAP service class, depending + on given acls + + icap_access classname allow|deny [!]aclname... + + The icap_access statements are processed in the order they appear in + this configuration file. If an access list matches, the processing stops. + For an "allow" rule, the specified class is used for the request. A "deny" + rule simply stops processing without using the class. You can also use the + special classname "None". + + For backward compatibility, it is also possible to use services + directly here. +Example: +icap_access class_1 allow all +DOC_END + +COMMENT_START MISCELLANEOUS ----------------------------------------------------------------------------- COMMENT_END Index: squid/src/cf_gen_defines diff -u squid/src/cf_gen_defines:1.7 squid/src/cf_gen_defines:1.6.8.2 --- squid/src/cf_gen_defines:1.7 Wed May 31 12:51:14 2006 +++ squid/src/cf_gen_defines Sun Jun 4 07:15:43 2006 @@ -22,11 +22,12 @@ define["USE_WCCP"]="--enable-wccp" define["USE_WCCPv2"]="--enable-wccpv2" define["WIP_FWD_LOG"]="--enable-forward-log" + define["HS_FEAT_ICAP"]="--enable-icap-support" } /^IFDEF:/ { if (define[$2] != "") DEFINE=define[$2] - else + else DEFINE="-D" $2 print "{\"" $2 "\", \"" DEFINE "\", " print "#if " $2 Index: squid/src/client_side.c diff -u squid/src/client_side.c:1.173 squid/src/client_side.c:1.89.4.16 --- squid/src/client_side.c:1.173 Mon Feb 26 18:51:07 2007 +++ squid/src/client_side.c Tue Feb 27 13:57:21 2007 @@ -109,7 +109,7 @@ static CWCB clientWriteComplete; static CWCB clientWriteBodyComplete; static PF clientReadRequest; -static PF connStateFree; +PF connStateFree; static PF requestTimeout; static PF clientLifetimeTimeout; static int clientCheckTransferDone(clientHttpRequest *); @@ -141,12 +141,12 @@ static void clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb); static void clientPackTermBound(String boundary, MemBuf * mb); static void clientInterpretRequestHeaders(clientHttpRequest *); -static void clientProcessRequest(clientHttpRequest *); +void clientProcessRequest(clientHttpRequest *); static void clientProcessExpired(void *data); static void clientProcessOnlyIfCachedMiss(clientHttpRequest * http); -static int clientCachable(clientHttpRequest * http); -static int clientHierarchical(clientHttpRequest * http); -static int clientCheckContentLength(request_t * r); +int clientCachable(clientHttpRequest * http); +int clientHierarchical(clientHttpRequest * http); +int clientCheckContentLength(request_t * r); static DEFER httpAcceptDefer; static log_type clientProcessRequest2(clientHttpRequest * http); static int clientReplyBodyTooLarge(clientHttpRequest *, squid_off_t clen); @@ -157,15 +157,18 @@ static void clientAccessCheckDone(int answer, void *data); static void clientAccessCheck2(void *data); static void clientAccessCheckDone2(int answer, void *data); -static BODY_HANDLER clientReadBody; +BODY_HANDLER clientReadBody; static void clientAbortBody(request_t * req); #if USE_SSL static void httpsAcceptSSL(ConnStateData * connState, SSL_CTX * sslContext); #endif static int varyEvaluateMatch(StoreEntry * entry, request_t * request); static int modifiedSince(StoreEntry *, request_t *); -static StoreEntry *clientCreateStoreEntry(clientHttpRequest *, method_t, request_flags); +StoreEntry *clientCreateStoreEntry(clientHttpRequest *, method_t, request_flags); static inline int clientNatLookup(ConnStateData * conn); +#if HS_FEAT_ICAP +static int clientIcapReqMod(clientHttpRequest * http); +#endif #if USE_IDENT static void @@ -383,7 +386,7 @@ EBIT_TEST(r->cache_control->mask, CC_ONLY_IF_CACHED); } -static StoreEntry * +StoreEntry * clientCreateStoreEntry(clientHttpRequest * h, method_t m, request_flags flags) { StoreEntry *e; @@ -640,6 +643,10 @@ if (urlgroup && *urlgroup) http->request->urlgroup = xstrdup(urlgroup); clientInterpretRequestHeaders(http); +#if HS_FEAT_ICAP + if (Config.icapcfg.onoff) + icapCheckAcl(http); +#endif #if HEADERS_LOG headersLog(0, 1, request->method, request); #endif @@ -1360,11 +1367,22 @@ /* Unlink us from the clients request list */ dlinkDelete(&http->node, &http->conn->reqs); dlinkDelete(&http->active, &ClientActiveRequests); +#if HS_FEAT_ICAP + /*In the case that the upload of data breaks, we need this code here .... */ + if (NULL != http->icap_reqmod) { + if (cbdataValid(http->icap_reqmod)) + if (http->icap_reqmod->icap_fd > -1) { + comm_close(http->icap_reqmod->icap_fd); + } + cbdataUnlock(http->icap_reqmod); + http->icap_reqmod = NULL; + } +#endif cbdataFree(http); } /* This is a handler normally called by comm_close() */ -static void +void connStateFree(int fd, void *data) { ConnStateData *connState = data; @@ -1384,8 +1402,9 @@ authenticateAuthUserRequestUnlock(connState->auth_user_request); connState->auth_user_request = NULL; authenticateOnCloseConnection(connState); - memFreeBuf(connState->in.size, connState->in.buf); - pconnHistCount(0, connState->nrequests); + if (connState->in.buf) + memFreeBuf(connState->in.size, connState->in.buf); +/* pconnHistCount(0, connState->nrequests);*/ if (connState->pinning.fd >= 0) comm_close(connState->pinning.fd); cbdataFree(connState); @@ -1582,7 +1601,7 @@ } } -static int +int clientCheckContentLength(request_t * r) { switch (r->method) { @@ -1601,7 +1620,7 @@ /* NOT REACHED */ } -static int +int clientCachable(clientHttpRequest * http) { request_t *req = http->request; @@ -1625,7 +1644,7 @@ } /* Return true if we can query our neighbors for this object */ -static int +int clientHierarchical(clientHttpRequest * http) { const char *url = http->uri; @@ -3315,7 +3334,7 @@ return LOG_TCP_HIT; } -static void +void clientProcessRequest(clientHttpRequest * http) { char *url = http->uri; @@ -3325,6 +3344,11 @@ RequestMethods[r->method].str, url); r->flags.collapsed = 0; +#if HS_FEAT_ICAP + if (clientIcapReqMod(http)) { + return; + } +#endif if (r->method == METHOD_CONNECT && !http->redirect.status) { http->log_type = LOG_TCP_MISS; #if USE_SSL && SSL_CONNECT_INTERCEPT @@ -3769,7 +3793,7 @@ int parser_return_code = 0; request_t *request = NULL; HttpMsgBuf msg; - + fde *F = &fd_table[fd]; /* Skip leading (and trailing) whitespace */ while (conn->in.offset > 0 && xisspace(conn->in.buf[0])) { @@ -3802,6 +3826,8 @@ /* add to the client request queue */ dlinkAddTail(http, &http->node, &conn->reqs); conn->nrequests++; + F->pconn.uses++; + F->pconn.type = 0; commSetTimeout(fd, Config.Timeout.lifetime, clientLifetimeTimeout, http); if (parser_return_code < 0) { debug(33, 1) ("clientReadRequest: FD %d (%s:%d) Invalid Request\n", fd, fd_table[fd].ipaddr, fd_table[fd].remote_port); @@ -3987,6 +4013,20 @@ (long) conn->in.offset, (long) conn->in.size); len = conn->in.size - conn->in.offset - 1; } +#if HS_FEAT_ICAP + /* + * This check exists because ICAP doesn't always work well + * with persistent (reused) connections. One version of the + * REQMOD code creates a fake ConnStateData, which doesn't have + * an in.buf. We want to make sure that the fake ConnStateData + * doesn't get used here. + */ + if (NULL == conn->in.buf) { + debug(33, 1) ("clientReadRequest: FD %d aborted; conn->in.buf is NULL\n", fd); + comm_close(fd); + return; + } +#endif statCounter.syscalls.sock.reads++; size = FD_READ_METHOD(fd, conn->in.buf + conn->in.offset, len); if (size > 0) { @@ -4072,7 +4112,7 @@ } /* file_read like function, for reading body content */ -static void +void clientReadBody(request_t * request, char *buf, size_t size, CBCB * callback, void *cbdata) { ConnStateData *conn = request->body_reader_data; @@ -4201,7 +4241,7 @@ } /* Abort a body request */ -static void +void clientAbortBody(request_t * request) { ConnStateData *conn = request->body_reader_data; @@ -4243,7 +4283,7 @@ * Some data has been sent to the client, just close the FD */ comm_close(fd); - } else if (conn->nrequests) { + } else if (fd_table[fd].pconn.uses) { /* * assume its a persistent connection; just close it */ @@ -4965,6 +5005,52 @@ } } +#if HS_FEAT_ICAP +static int +clientIcapReqMod(clientHttpRequest * http) +{ + ErrorState *err; + icap_service *service; + if (http->flags.did_icap_reqmod) + return 0; + if (NULL == (service = icapService(ICAP_SERVICE_REQMOD_PRECACHE, http->request))) + return 0; + debug(33, 3) ("clientIcapReqMod: calling icapReqModStart for %p\n", http); + /* + * Note, we pass 'start' and 'log_addr' to ICAP so the access.log + * entry comes out right. The 'clientHttpRequest' created by + * the ICAP side is the one that gets logged. The first + * 'clientHttpRequest' does not get logged because its out.size + * is zero and log_type is unset. + */ + http->icap_reqmod = icapReqModStart(service, + http->uri, + http->request, + http->conn->fd, + http->start, + http->conn->log_addr, + (void *) http->conn); + if (NULL == http->icap_reqmod) { + return 0; + } else if (-1 == (int) http->icap_reqmod) { + /* produce error */ + http->icap_reqmod = NULL; + debug(33, 2) ("clientIcapReqMod: icap told us to send an error\n"); + http->log_type = LOG_TCP_DENIED; + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, http->orig_request); + err->xerrno = ETIMEDOUT; + err->request = requestLink(http->request); + err->src_addr = http->conn->peer.sin_addr; + http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags); + errorAppendEntry(http->entry, err); + return 1; + } + cbdataLock(http->icap_reqmod); + http->flags.did_icap_reqmod = 1; + return 1; +} +#endif + /* This is a handler normally called by comm_close() */ static void clientPinnedConnectionClosed(int fd, void *data) Index: squid/src/comm.c diff -u squid/src/comm.c:1.50 squid/src/comm.c:1.29.10.10 --- squid/src/comm.c:1.50 Sun Jan 21 06:03:45 2007 +++ squid/src/comm.c Wed Jan 31 10:10:44 2007 @@ -742,8 +742,8 @@ F->flags.closing = 1; CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING); commCallCloseHandlers(fd); - if (F->uses) /* assume persistent connect count */ - pconnHistCount(1, F->uses); + if (F->pconn.uses) + pconnHistCount(F->pconn.type, F->pconn.uses); #if USE_SSL if (F->ssl) { if (!F->flags.close_request) { Index: squid/src/enums.h diff -u squid/src/enums.h:1.60 squid/src/enums.h:1.45.4.8 --- squid/src/enums.h:1.60 Sat Mar 3 10:54:24 2007 +++ squid/src/enums.h Mon Mar 5 14:00:41 2007 @@ -94,6 +94,7 @@ ERR_ONLY_IF_CACHED_MISS, /* failure to satisfy only-if-cached request */ ERR_TOO_BIG, TCP_RESET, + ERR_ICAP_FAILURE, ERR_INVALID_RESP, ERR_MAX } err_type; @@ -461,6 +462,9 @@ PROTO_WHOIS, PROTO_INTERNAL, PROTO_HTTPS, +#if HS_FEAT_ICAP + PROTO_ICAP, +#endif PROTO_MAX } protocol_t; @@ -636,6 +640,12 @@ #if USE_SSL MEM_ACL_CERT_DATA, #endif +#if HS_FEAT_ICAP + MEM_ICAP_OPT_DATA, + MEM_ICAP_SERVICE_LIST, + MEM_ICAP_CLASS, + MEM_ICAP_ACCESS, +#endif MEM_MAX } mem_type; @@ -736,9 +746,14 @@ CBDATA_RemovalPolicyWalker, CBDATA_RemovalPurgeWalker, CBDATA_store_client, +#ifdef HS_FEAT_ICAP + CBDATA_IcapStateData, + CBDATA_icap_service, +#endif CBDATA_FIRST_CUSTOM_TYPE = 1000 } cbdata_type; + /* * Return codes from checkVary(request) */ @@ -787,4 +802,68 @@ ST_OP_CREATE } store_op_t; +#if HS_FEAT_ICAP +typedef enum { + ICAP_STATUS_NONE = 0, + ICAP_STATUS_CONTINUE = 100, + ICAP_STATUS_SWITCHING_PROTOCOLS = 101, + ICAP_STATUS_STATUS_OK = 200, + ICAP_CREATED = 201, + ICAP_STATUS_ACCEPTED = 202, + ICAP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, + ICAP_STATUS_NO_MODIFICATION_NEEDED = 204, + ICAP_STATUS_RESET_CONTENT = 205, + ICAP_STATUS_PARTIAL_CONTENT = 206, + ICAP_STATUS_MULTIPLE_CHOICES = 300, + ICAP_STATUS_MOVED_PERMANENTLY = 301, + ICAP_STATUS_MOVED_TEMPORARILY = 302, + ICAP_STATUS_SEE_OTHER = 303, + ICAP_STATUS_NOT_MODIFIED = 304, + ICAP_STATUS_USE_PROXY = 305, + ICAP_STATUS_BAD_REQUEST = 400, + ICAP_STATUS_UNAUTHORIZED = 401, + ICAP_STATUS_PAYMENT_REQUIRED = 402, + ICAP_STATUS_FORBIDDEN = 403, + ICAP_STATUS_SERVICE_NOT_FOUND = 404, + ICAP_STATUS_METHOD_NOT_ALLOWED = 405, + ICAP_STATUS_NOT_ACCEPTABLE = 406, + ICAP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, + ICAP_STATUS_REQUEST_TIMEOUT = 408, + ICAP_STATUS_CONFLICT = 409, + ICAP_STATUS_GONE = 410, + ICAP_STATUS_LENGTH_REQUIRED = 411, + ICAP_STATUS_PRECONDITION_FAILED = 412, + ICAP_STATUS_REQUEST_ENTITY_TOO_LARGE = 413, + ICAP_STATUS_REQUEST_URI_TOO_LARGE = 414, + ICAP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, + ICAP_STATUS_INTERNAL_SERVER_ERROR = 500, + ICAP_STATUS_NOT_IMPLEMENTED = 501, + ICAP_STATUS_BAD_GATEWAY = 502, + ICAP_STATUS_SERVICE_OVERLOADED = 503, + ICAP_STATUS_GATEWAY_TIMEOUT = 504, + ICAP_STATUS_ICAP_VERSION_NOT_SUPPORTED = 505, + ICAP_STATUS_INVALID_HEADER = 600 +} icap_status; + +/* + * these values are used as index in an array, so it seems to be better to + * assign some numbers + */ +typedef enum { + ICAP_SERVICE_REQMOD_PRECACHE = 0, + ICAP_SERVICE_REQMOD_POSTCACHE = 1, + ICAP_SERVICE_RESPMOD_PRECACHE = 2, + ICAP_SERVICE_RESPMOD_POSTCACHE = 3, + ICAP_SERVICE_MAX = 4 +} icap_service_t; + +typedef enum { + ICAP_METHOD_NONE, + ICAP_METHOD_OPTION, + ICAP_METHOD_REQMOD, + ICAP_METHOD_RESPMOD +} icap_method_t; + +#endif /* HS_FEAT_ICAP */ + #endif /* SQUID_ENUMS_H */ Index: squid/src/forward.c diff -u squid/src/forward.c:1.48 squid/src/forward.c:1.20.4.12 --- squid/src/forward.c:1.48 Sun Feb 25 03:54:36 2007 +++ squid/src/forward.c Tue Feb 27 13:57:24 2007 @@ -358,8 +358,9 @@ } else { debug(17, 3) ("fwdConnectDone: FD %d: '%s'\n", server_fd, storeUrl(fwdState->entry)); fd_note(server_fd, storeUrl(fwdState->entry)); - fd_table[server_fd].uses++; - if (fd_table[server_fd].uses == 1 && fs->peer) + fd_table[server_fd].pconn.uses++; + fd_table[server_fd].pconn.type = 1; + if (fd_table[server_fd].pconn.uses ==1 && fs->peer) peerConnectSucceded(fs->peer); #if USE_SSL if ((fs->peer && fs->peer->use_ssl) || @@ -939,6 +940,8 @@ void fwdFail(FwdState * fwdState, ErrorState * errorState) { + if (NULL == fwdState) + return; debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n", err_type_str[errorState->type], httpStatusString(errorState->http_status), @@ -977,6 +980,8 @@ void fwdUnregister(int fd, FwdState * fwdState) { + if (NULL == fwdState) + return; debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry)); assert(fd == fwdState->server_fd); assert(fd > -1); @@ -996,7 +1001,10 @@ void fwdComplete(FwdState * fwdState) { - StoreEntry *e = fwdState->entry; + StoreEntry *e; + if (NULL == fwdState) + return; + e = fwdState->entry; assert(e->store_status == STORE_PENDING); debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e), e->mem_obj->reply->sline.status); Index: squid/src/globals.h diff -u squid/src/globals.h:1.29 squid/src/globals.h:1.22.4.6 --- squid/src/globals.h:1.29 Sun Jan 21 06:03:49 2007 +++ squid/src/globals.h Wed Jan 31 10:11:10 2007 @@ -171,6 +171,9 @@ #if HAVE_SBRK extern void *sbrk_start; /* 0 */ #endif +#if HS_FEAT_ICAP +extern const char *icap_service_type_str[]; +#endif extern int opt_send_signal; /* -1 */ extern int opt_no_daemon; /* 0 */ #if LINUX_TPROXY Index: squid/src/http.c diff -u squid/src/http.c:1.58 squid/src/http.c:1.28.4.13 --- squid/src/http.c:1.58 Sat Feb 24 03:52:43 2007 +++ squid/src/http.c Tue Feb 27 13:57:26 2007 @@ -47,7 +47,7 @@ static PF httpReadReply; static void httpSendRequest(HttpStateData *); -static PF httpStateFree; +PF httpStateFree; static PF httpTimeout; static void httpCacheNegatively(StoreEntry *); static void httpMakePrivate(StoreEntry *); @@ -56,12 +56,13 @@ static void httpMaybeRemovePublic(StoreEntry *, http_status); static int peer_supports_connection_pinning(HttpStateData * httpState); -static void +void httpStateFree(int fd, void *data) { HttpStateData *httpState = data; #if DELAY_POOLS - delayClearNoDelay(fd); + if (fd >= 0) + delayClearNoDelay(fd); #endif if (httpState == NULL) return; @@ -81,6 +82,9 @@ httpState->request = NULL; httpState->orig_request = NULL; stringClean(&httpState->chunkhdr); +#if HS_FEAT_ICAP + cbdataUnlock(httpState->icap_writer); +#endif cbdataFree(httpState); } @@ -410,7 +414,7 @@ } /* rewrite this later using new interfaces @?@ */ -static size_t +size_t httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) { StoreEntry *entry = httpState->entry; @@ -640,11 +644,25 @@ if (size > httpState->chunk_size) size = httpState->chunk_size; httpState->chunk_size -= size; +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) { + debug(81, 5) ("calling icapRespModAddBodyData from %s:%d\n", __FILE__, __LINE__); + icapRespModAddBodyData(httpState->icap_writer, buf, size); + httpState->icap_writer->fake_content_length += size; + } else +#endif storeAppend(httpState->entry, buf, size); buf += size; len -= size; } else if (httpState->chunk_size < 0) { /* non-chunked without content-length */ +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) { + debug(81, 5) ("calling icapRespModAddBodyData from %s:%d\n", __FILE__, __LINE__); + icapRespModAddBodyData(httpState->icap_writer, buf, len); + httpState->icap_writer->fake_content_length += len; + } else +#endif storeAppend(httpState->entry, buf, len); len = 0; } else if (httpState->flags.chunked) { @@ -699,6 +717,15 @@ /* Don't know what to do with this data. Bail out */ break; } +#if HS_FEAT_ICAP + if (httpState->icap_writer) { + if (!httpState->icap_writer->respmod.entry) { + debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); + comm_close(fd); + return; + } + } else +#endif if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { /* * the above storeAppend() call could ABORT this entry, @@ -720,6 +747,10 @@ if (!httpState->chunk_size && !httpState->flags.chunked) complete = 1; if (!complete && len == 0) { +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) + icapSendRespMod(httpState->icap_writer, 0); +#endif /* Wait for more data or EOF condition */ if (httpState->flags.keepalive_broken) { commSetTimeout(fd, 10, NULL, NULL); @@ -779,6 +810,10 @@ */ if (!entry->mem_obj->reply->keep_alive) keep_alive = 0; +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) + icapSendRespMod(httpState->icap_writer, 1); +#endif if (keep_alive) { int pinned = 0; #if LINUX_TPROXY @@ -838,6 +873,17 @@ #endif int buffer_filled; +#if HS_FEAT_ICAP + if (httpState->icap_writer) { + if (!httpState->icap_writer->respmod.entry) { + debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); + comm_close(fd); + return; + } + /*The folowing entry can not be marked as aborted. + * The StoreEntry icap_writer->respmod.entry used when the icap_write used...... */ + } else +#endif if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { comm_close(fd); return; @@ -849,7 +895,35 @@ else delay_id = delayMostBytesAllowed(entry->mem_obj, &read_sz); #endif +#if HS_FEAT_ICAP + if (httpState->icap_writer) { + IcapStateData *icap = httpState->icap_writer; + /* + * Ok we have a received a response from the web server, so try to + * connect the icap server if it's the first attemps. If we try + * to connect to the icap server, defer this request (do not read + * the buffer), and defer until icapConnectOver() is not called. + */ + if (icap->flags.connect_requested == 0) { + debug(81, 2) ("icapSendRespMod: Create a new connection to icap server\n"); + if (!icapConnect(icap, icapConnectOver)) { + debug(81, 2) ("icapSendRespMod: Something strange while creating a socket to icap server\n"); + commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); + return; + } + debug(81, 2) ("icapSendRespMod: new connection to icap server (using FD=%d)\n", icap->icap_fd); + icap->flags.connect_requested = 1; + /* Wait for more data or EOF condition */ + commSetTimeout(fd, httpState->flags.keepalive_broken ? 10 : Config.Timeout.read, NULL, NULL); + commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); + return; + } + if(icap->flags.no_content == 1) { + commSetDefer(fd, fwdCheckDeferRead, icap->respmod.entry); + } + } +#endif errno = 0; statCounter.syscalls.sock.reads++; len = FD_READ_METHOD(fd, buf, read_sz); @@ -868,7 +942,13 @@ IOStats.Http.read_hist[bin]++; buf[len] = '\0'; } - if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].uses > 1) { +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) + (void) 0; + else +#endif + + if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].pconn.uses > 1) { /* Skip whitespace */ while (len > 0 && xisspace(*buf)) xmemmove(buf, buf + 1, len--); @@ -971,12 +1051,49 @@ commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); return; } +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) { + debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); + if (cbdataValid(httpState->icap_writer)) { + icapRespModAddResponceHeaders(httpState->icap_writer, buf, done); + httpState->icap_writer->fake_content_length += done; + } + } +#endif } httpAppendBody(httpState, buf + done, len - done, buffer_filled); return; } } +#ifdef HS_FEAT_ICAP +static int +httpReadReplyWaitForIcap(int fd, void *data) +{ + HttpStateData *httpState = data; + if (NULL == httpState->icap_writer) + return 0; + /* + * Do not defer when we are not connected to the icap server. + * Defer when the icap server connection is not established but pending + * Defer when the icap server is busy (writing on the socket) + */ + debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_requested=%d\n", + fd, httpState->icap_writer->flags.connect_requested); + if (!httpState->icap_writer->flags.connect_requested) + return 0; + debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_pending=%d\n", + fd, httpState->icap_writer->flags.connect_pending); + if (httpState->icap_writer->flags.connect_pending) + return 1; + debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, write_pending=%d\n", + fd, httpState->icap_writer->flags.write_pending); + if (httpState->icap_writer->flags.write_pending) + return 1; + return 0; +} +#endif + /* This will be called when request write is complete. Schedule read of * reply. */ static void @@ -1004,6 +1121,63 @@ comm_close(fd); return; } else { + /* Schedule read reply. */ +#ifdef HS_FEAT_ICAP + if (icapService(ICAP_SERVICE_RESPMOD_PRECACHE, httpState->orig_request)) { + httpState->icap_writer = icapRespModStart( + ICAP_SERVICE_RESPMOD_PRECACHE, + httpState->orig_request, httpState->entry, httpState->flags); + if (-1 == (int) httpState->icap_writer) { + /* TODO: send error here and exit */ + ErrorState *err; + httpState->icap_writer = 0; + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, httpState->fwd->request); + err->xerrno = errno; + err->request = requestLink(httpState->orig_request); + errorAppendEntry(entry, err); + comm_close(fd); + return; + } else if (httpState->icap_writer) { + request_flags fake_flags = httpState->orig_request->flags; + method_t fake_method = entry->mem_obj->method; + const char *fake_msg = "this is a fake entry for " + " response sent to an ICAP RESPMOD server"; + cbdataLock(httpState->icap_writer); + /* + * this httpState will give the data it reads to + * the icap server, rather than put it into + * a StoreEntry + */ + storeClientUnregisterAbort(httpState->entry); + storeUnlockObject(httpState->entry); + /* + * create a bogus entry because the code assumes one is + * always there. + */ + fake_flags.cachable = 0; + fake_flags.hierarchical = 0; /* force private key */ + httpState->entry = storeCreateEntry("fake", fake_flags, fake_method); + storeAppend(httpState->entry, fake_msg, strlen(fake_msg)); + /* + * pull a switcheroo on fwdState->entry. + */ + storeUnlockObject(httpState->fwd->entry); + httpState->fwd->entry = httpState->entry; + storeLockObject(httpState->fwd->entry); + /* + * Note that we leave fwdState connected to httpState, + * but we changed the entry. So when fwdComplete + * or whatever is called it does no harm -- its + * just the fake entry. + */ + } else { + /* + * failed to open connection to ICAP server. + * But bypass request, so just continue here. + */ + } + } +#endif /* * Set the read timeout here because it hasn't been set yet. * We only set the read timeout after the request has been @@ -1012,8 +1186,18 @@ * the timeout for POST/PUT requests that have very large * request bodies. */ + + /* removed in stable5: + * commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); + */ commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); - commSetDefer(fd, fwdCheckDeferRead, entry); +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) { + debug(11, 5) ("FD %d, setting httpReadReplyWaitForIcap\n", httpState->fd); + commSetDefer(httpState->fd, httpReadReplyWaitForIcap, httpState); + } else +#endif + commSetDefer(httpState->fd, fwdCheckDeferRead, entry); } httpState->flags.request_sent = 1; } @@ -1317,8 +1501,11 @@ if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) { const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request); httpHdrCcSetMaxAge(cc, getMaxAge(url)); +#ifndef HS_FEAT_ICAP + /* Don;t bother - if the url you want to cache is redirected? */ if (strLen(request->urlpath)) assert(strstr(url, strBuf(request->urlpath))); +#endif } /* Set no-cache if determined needed but not found */ if (orig_request->flags.nocache && !httpHeaderHas(hdr_in, HDR_PRAGMA)) @@ -1444,6 +1631,7 @@ int fd = fwd->server_fd; HttpStateData *httpState; request_t *proxy_req; + /* ErrorState *err; */ request_t *orig_req = fwd->request; debug(11, 3) ("httpStart: \"%s %s\"\n", RequestMethods[orig_req->method].str, @@ -1486,12 +1674,22 @@ httpState->request = requestLink(orig_req); httpState->orig_request = requestLink(orig_req); } +#ifdef HS_FEAT_ICAP + if (icapService(ICAP_SERVICE_REQMOD_POSTCACHE, httpState->orig_request)) { + httpState->icap_writer = icapRespModStart(ICAP_SERVICE_REQMOD_POSTCACHE, + httpState->orig_request, httpState->entry, httpState->flags); + if (httpState->icap_writer) { + return; + } + } +#endif /* * register the handler to free HTTP state data when the FD closes */ comm_add_close_handler(fd, httpStateFree, httpState); statCounter.server.all.requests++; statCounter.server.http.requests++; + httpSendRequest(httpState); /* * We used to set the read timeout here, but not any more. Index: squid/src/icap_common.c diff -u /dev/null squid/src/icap_common.c:1.1.14.3 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/icap_common.c Fri May 26 12:24:02 2006 @@ -0,0 +1,815 @@ +/* + * $Id$ + * + * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client + * AUTHOR: Geetha Manjunath, Hewlett Packard Company + * + * 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. + * + */ + +/* _GNU_SOURCE is required for strcasestr */ +#define _GNU_SOURCE 1 + +#include "squid.h" +#include "util.h" + +extern PF httpStateFree; + +#define EXPECTED_ICAP_HEADER_LEN 256 +#define ICAP_OPTIONS_REQUEST + + +void +icapInit() +{ +#ifdef ICAP_OPTIONS_REQUEST + if (Config.icapcfg.onoff) { + icapOptInit(); + } +#endif +} + +void +icapClose() +{ + icapOptShutdown(); +} + +/* + * search for a HTTP-like header in the buffer. + * Note, buf must be 0-terminated + * + * This function is not very good. It should probably look for + * header tokens only at the start of a line, not just anywhere in + * the buffer. + */ +int +icapFindHeader(const char *buf, const char *hdr, const char **Start, + const char **End) +{ + const char *start = NULL; + const char *end = NULL; + start = strcasestr(buf, hdr); + if (NULL == start) + return 0; + end = start + strcspn(start, "\r\n"); + if (start == end) + return 0; + *Start = start; + *End = end; + return 1; +} + +/* + * parse the contents of the encapsulated header (buffer between enc_start + * and enc_end) and put the result into IcapStateData + */ +void +icapParseEncapsulated(IcapStateData * icap, const char *enc_start, + const char *enc_end) +{ + char *current, *end; + + assert(icap); + assert(enc_start); + assert(enc_end); + + current = strchr(enc_start, ':'); + current++; + while (current < enc_end) { + while (isspace(*current)) + current++; + if (!strncmp(current, "res-hdr=", 8)) { + current += 8; + icap->enc.res_hdr = strtol(current, &end, 10); + } else if (!strncmp(current, "req-hdr=", 8)) { + current += 8; + icap->enc.req_hdr = strtol(current, &end, 10); + } else if (!strncmp(current, "null-body=", 10)) { + current += 10; + icap->enc.null_body = strtol(current, &end, 10); + } else if (!strncmp(current, "res-body=", 9)) { + current += 9; + icap->enc.res_body = strtol(current, &end, 10); + } else if (!strncmp(current, "req-body=", 9)) { + current += 9; + icap->enc.req_body = strtol(current, &end, 10); + } else if (!strncmp(current, "opt-body=", 9)) { + current += 9; + icap->enc.opt_body = strtol(current, &end, 10); + } else { + /* invalid header */ + debug(81, 5) ("icapParseEncapsulated: error in: %s\n", current); + return; + } + current = end; + current = strchr(current, ','); + if (current == NULL) + break; + else + current++; /* skip ',' */ + } + debug(81, + 3) ("icapParseEncapsulated: res-hdr=%d, req-hdr=%d, null-body=%d, " + "res-body=%d, req-body=%d, opt-body=%d\n", icap->enc.res_hdr, + icap->enc.req_hdr, icap->enc.null_body, icap->enc.res_body, + icap->enc.req_body, icap->enc.opt_body); + +} + +icap_service * +icapService(icap_service_t type, request_t * r) +{ + icap_service_list *isl_iter; + int is_iter; + int nb_unreachable = 0; + icap_service *unreachable_one = NULL; + + debug(81, 8) ("icapService: type=%s\n", icapServiceToStr(type)); + if (NULL == r) { + debug(81, 8) ("icapService: no request_t\n"); + return NULL; + } + if (NULL == r->class) { + debug(81, 8) ("icapService: no class\n"); + return NULL; + } + for (isl_iter = r->class->isl; isl_iter; isl_iter = isl_iter->next) { + /* TODO:luc: Do a round-robin, choose a random value ? + * For now, we use a simple round robin with checking is the + * icap server is available */ + is_iter = isl_iter->last_service_used; + do { + is_iter = (is_iter + 1) % isl_iter->nservices; + debug(81, 8) ("icapService: checking service %s/id=%d\n", + isl_iter->services[is_iter]->name, is_iter); + if (type == isl_iter->services[is_iter]->type) { + if (!isl_iter->services[is_iter]->unreachable) { + debug(81, 8) ("icapService: found service %s/id=%d\n", + isl_iter->services[is_iter]->name, is_iter); + isl_iter->last_service_used = is_iter; + return isl_iter->services[is_iter]; + } + debug(81, + 8) + ("icapService: found service %s/id=%d, but it's unreachable. I don't want to use it\n", + isl_iter->services[is_iter]->name, is_iter); + unreachable_one = isl_iter->services[is_iter]; + nb_unreachable++; + /* FIXME:luc: in response mod, if we return an NULL pointer, user can bypass + * the filter, is it normal ? */ + } + } while (is_iter != isl_iter->last_service_used); + } + debug(81, 8) ("icapService: no service found\n"); + isl_iter = r->class->isl; + + if (nb_unreachable > 0) { + debug(81, + 8) + ("All the services are unreachable, returning an unreachable one\n"); + return unreachable_one; + } else { + return NULL; + } +} + +int +icapConnect(IcapStateData * icap, CNCB * theCallback) +{ + int rc; + icap->icap_fd = pconnPop(icap->current_service->hostname, + icap->current_service->port, NULL, NULL, 0); + if (icap->icap_fd >= 0) { + debug(81, 3) ("icapConnect: reused pconn FD %d\n", icap->icap_fd); + fd_note(icap->icap_fd, icap->current_service->uri); + comm_add_close_handler(icap->icap_fd, icapStateFree, icap); + theCallback(icap->icap_fd, 0, icap); + return 1; + } + icap->icap_fd = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), 0, + COMM_NONBLOCKING, icap->current_service->uri); + debug(81, 5) ("icapConnect: new socket, FD %d, local address %s\n", + icap->icap_fd, inet_ntoa(getOutgoingAddr(NULL))); + if (icap->icap_fd < 0) { + icapStateFree(-1, icap); /* XXX test */ + return 0; + } + icap->flags.connect_pending = 1; + /* + * Configure timeout and close handler before calling + * connect because commConnectStart() might get an error + * immediately and close the descriptor before it returns. + */ + commSetTimeout(icap->icap_fd, Config.Timeout.connect, + icapConnectTimeout, icap); + comm_add_close_handler(icap->icap_fd, icapStateFree, icap); + /* + * This sucks. commConnectStart() may fail before returning, + * so lets lock the data and check its validity afterwards. + */ + cbdataLock(icap); + commConnectStart(icap->icap_fd, + icap->current_service->hostname, + icap->current_service->port, theCallback, icap); + rc = cbdataValid(icap); + cbdataUnlock(icap); + debug(81, 3) ("icapConnect: returning %d\n", rc); + return rc; +} + +IcapStateData * +icapAllocate(void) +{ + IcapStateData *icap; + + if (!Config.icapcfg.onoff) + return 0; + + icap = cbdataAlloc(IcapStateData); + icap->icap_fd = -1; + icap->enc.res_hdr = -1; + icap->enc.res_body = -1; + icap->enc.req_hdr = -1; + icap->enc.req_body = -1; + icap->enc.opt_body = -1; + icap->enc.null_body = -1; + icap->chunk_size = -1; + memBufDefInit(&icap->icap_hdr); + + debug(81, 3) ("New ICAP state\n"); + return icap; +} + +void +icapStateFree(int fd, void *data) +{ + IcapStateData *icap = data; + debug(81, 3) ("icapStateFree: FD %d, icap %p\n", fd, icap); + assert(icap); + assert(-1 == fd || fd == icap->icap_fd); + if (icap->respmod.entry) { + /* + * If we got some error on this side (like ECONNRESET) + * we must signal the other side(s) with a storeAbort() + * call. + */ + if (icap->respmod.entry->store_status != STORE_OK) + storeAbort(icap->respmod.entry); + storeUnlockObject(icap->respmod.entry); + icap->respmod.entry = NULL; + } + requestUnlink(icap->request); + icap->request = NULL; + if (!memBufIsNull(&icap->icap_hdr)) + memBufClean(&icap->icap_hdr); + if (!memBufIsNull(&icap->respmod.buffer)) + memBufClean(&icap->respmod.buffer); + if (!memBufIsNull(&icap->respmod.req_hdr_copy)) + memBufClean(&icap->respmod.req_hdr_copy); + if (!memBufIsNull(&icap->respmod.resp_copy)) + memBufClean(&icap->respmod.resp_copy); + if (!memBufIsNull(&icap->reqmod.hdr_buf)) + memBufClean(&icap->reqmod.hdr_buf); + if (!memBufIsNull(&icap->reqmod.http_entity.buf)) + memBufClean(&icap->reqmod.http_entity.buf); + if (!memBufIsNull(&icap->chunk_buf)) + memBufClean(&icap->chunk_buf); + if (icap->httpState) + httpStateFree(-1, icap->httpState); + cbdataUnlock(icap->reqmod.client_cookie); + cbdataFree(icap); +} + +void +icapConnectTimeout(int fd, void *data) +{ + IcapStateData *icap = data; + debug(81, 3) ("icapConnectTimeout: FD %d, unreachable=1\n", fd); + assert(fd == icap->icap_fd); + icapOptSetUnreachable(icap->current_service); + comm_close(fd); +} + +void +icapReadTimeout(int fd, void *data) +{ + IcapStateData *icap = data; + assert(fd == icap->icap_fd); + if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { + debug(81, 3) ("icapReadTimeout: FD %d, unreachable=1\n", fd); + icapOptSetUnreachable(icap->current_service); + } else + debug(81, 3) ("icapReadTimeout: FD %d, still reachable\n", fd); + comm_close(fd); +} + +icap_service_t +icapServiceToType(const char *s) +{ + if (!strcmp(s, "reqmod_precache")) + return ICAP_SERVICE_REQMOD_PRECACHE; + if (!strcmp(s, "reqmod_postcache")) + return ICAP_SERVICE_REQMOD_POSTCACHE; + if (!strcmp(s, "respmod_precache")) + return ICAP_SERVICE_RESPMOD_PRECACHE; + if (!strcmp(s, "respmod_postcache")) + return ICAP_SERVICE_RESPMOD_POSTCACHE; + return ICAP_SERVICE_MAX; +} + +const char * +icapServiceToStr(const icap_service_t type) +{ + if (type >= 0 && type < ICAP_SERVICE_MAX) + return icap_service_type_str[type]; + else + return "error"; +} + + +/* copied from clientAclChecklistCreate */ +static aclCheck_t * +icapAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http) +{ + aclCheck_t *ch; + ConnStateData *conn = http->conn; + ch = aclChecklistCreate(acl, http->request, 0); + ch->conn = conn; + cbdataLock(ch->conn); + return ch; +} + +/* + * check wether we do icap for a request + */ +int +icapCheckAcl(clientHttpRequest * http) +{ + icap_access *iter; + aclCheck_t *icapChecklist; + + for (iter = Config.icapcfg.access_head; iter; iter = iter->next) { + acl_access *A = iter->access; + icapChecklist = icapAclChecklistCreate(A, http); + if (aclMatchAclList(A->acl_list, icapChecklist)) { + debug(81, 5) ("icapCheckAcl: match for class=%s\n", + iter->class->name); + if (A->allow) { + /* allow rule, do icap and use associated class */ + http->request->class = iter->class; + aclChecklistFree(icapChecklist); + return 1; + } else { + /* deny rule, stop processing */ + aclChecklistFree(icapChecklist); + return 0; + } + } + aclChecklistFree(icapChecklist); + } + return 0; +} + +/* icapLineLength + * + * returns the amount of data until lineending ( \r\n ) + * This function is NOT tolerant of variations of \r\n. + */ +size_t +icapLineLength(const char *start, int len) +{ + size_t lineLen = 0; + char *end = (char *) memchr(start, '\r', len); + if (NULL == end) + return 0; + end++; /* advance to where '\n' should be */ + lineLen = end - start + 1; + if (lineLen > len) { + debug(0, 0) ("icapLineLength: warning lineLen (%d) > len (%d)\n", + lineLen, len); + return 0; + } + if (*end != '\n') { + debug(0, 0) ("icapLineLength: warning *end (%x) != '\\n'\n", *end); + return 0; + } + debug(81, 7) ("icapLineLength: returning %d\n", lineLen); + return lineLen; +} + +/* + * return: + * -1 if EOF before getting end of ICAP header + * 0 if we don't have the entire ICAP header yet + * 1 if we got the whole header + */ +int +icapReadHeader(int fd, IcapStateData * icap, int *isIcap) +{ + int headlen = 0; + int len = 0; + int peek_sz = EXPECTED_ICAP_HEADER_LEN; + int read_sz = 0; + LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); + for (;;) { + len = recv(fd, tmpbuf, peek_sz, MSG_PEEK); + debug(81, 5) ("recv(FD %d, ..., MSG_PEEK) ret %d\n", fd, len); + if (len < 0) { + debug(81, 1) ("icapReadHeader: FD %d recv error: %s\n", fd, + xstrerror()); + return -1; + } + if (len == 0) { + debug(81, 2) ("icapReadHeader: FD %d recv EOF\n", fd); + return -1; + } + headlen = headersEnd(tmpbuf, len); + debug(81, 3) ("headlen=%d\n", headlen); + /* + * break if we now know where the ICAP headers end + */ + if (headlen) + break; + /* + * break if we know there is no more data to read + */ + if (len < peek_sz) + break; + /* + * The ICAP header is larger than (or equal to) our read + * buffer, so double it and try to peek again. + */ + peek_sz *= 2; + if (peek_sz >= SQUID_TCP_SO_RCVBUF) { + debug(81, + 1) ("icapReadHeader: Failed to find end of ICAP header\n"); + debug(81, 1) ("\twithin first %d bytes of response\n", + SQUID_TCP_SO_RCVBUF); + debug(81, 1) ("\tpossible persistent connection bug/confusion\n"); + return -1; + } + } + /* + * Now actually read the data from the kernel + */ + if (headlen) + read_sz = headlen; + else + read_sz = len; + len = FD_READ_METHOD(fd, tmpbuf, read_sz); + assert(len == read_sz); + fd_bytes(fd, len, FD_READ); + memBufAppend(&icap->icap_hdr, tmpbuf, len); + if (headlen) { + /* End of ICAP header found */ + if (icap->icap_hdr.size < 4) + *isIcap = 0; + else if (0 == strncmp(icap->icap_hdr.buf, "ICAP", 4)) + *isIcap = 1; + else + *isIcap = 0; + return 1; + } + /* + * We don't have all the headers yet + */ + return 0; +} + +static int +icapParseConnectionClose(const IcapStateData * icap, const char *s, + const char *e) +{ + char *t; + char *q; + /* + * s points to the start of the line "Connection: ... " + * e points to *after* the last character on the line + */ + s += 11; /* skip past Connection: */ + while (s < e && isspace(*s)) + s++; + if (e - s < 5) + return 0; + /* + * create a buffer that we can use strtok on + */ + t = xmalloc(e - s + 1); + strncpy(t, s, e - s); + *(t + (e - s)) = '\0'; + for (q = strtok(t, ","); q; q = strtok(NULL, ",")) { + if (0 == strcasecmp(q, "close")) { + xfree(t); + return 1; + } + } + xfree(t); + return 0; +} + +/* returns icap status, version and subversion extracted from status line or -1 on parsing failure + * The str_status pointr points to the text returned from the icap server. + * sline probably is NOT terminated with '\0' + */ +int +icapParseStatusLine(const char *sline, int slinesize, int *version_major, + int *version_minor, const char **str_status) +{ + char *sp, *stmp, *ep = (char *) sline + slinesize; + int status; + if (slinesize < 14) /*The format of this line is: "ICAP/x.x xxx[ msg....]\r\n" */ + return -1; + + if (strncmp(sline, "ICAP/", 5) != 0) + return -1; + if (sscanf(sline + 5, "%d.%d", version_major, version_minor) != 2) + return -1; + + if (!(sp = memchr(sline, ' ', slinesize))) + return -1; + + while (sp < ep && xisspace(*++sp)); + + if (!xisdigit(*sp) || sp >= ep) + return -1; + + if ((status = strtol(sp, &stmp, 10)) <= 0) + return -1; + sp = stmp; + + while (sp < ep && xisspace(*++sp)); + *str_status = sp; + /*Must add a test for "\r\n" end headers .... */ + return status; +} + + +void +icapSetKeepAlive(IcapStateData * icap, const char *hdrs) +{ + const char *start; + const char *end; + if (0 == icap->flags.keep_alive) + return; + if (0 == icapFindHeader(hdrs, "Connection:", &start, &end)) { + icap->flags.keep_alive = 1; + return; + } + if (icapParseConnectionClose(icap, start, end)) + icap->flags.keep_alive = 0; + else + icap->flags.keep_alive = 1; +} + +/* + * icapParseChunkSize + * + * Returns the offset where the next chunk starts + * return parameter chunk_size; + */ +static int +icapParseChunkSize(const char *buf, int len, int *chunk_size) +{ + int chunkSize = 0; + char c; + size_t start; + size_t end; + size_t nextStart = 0; + debug(81, 3) ("icapParseChunkSize: buf=%p, len=%d\n", buf, len); + do { + start = nextStart; + debug(81, 3) ("icapParseChunkSize: start=%d\n", start); + if (len <= start) { + /* + * end of buffer, so far no lines or only empty lines, + * wait for more data. read chunk size with next buffer. + */ + *chunk_size = 0; + return 0; + } + end = start + icapLineLength(buf + start, len - start); + nextStart = end; + if (end <= start) { + /* + * no line found, need more code here, now we are in + * deep trouble, buffer stops with half a chunk size + * line. For now stop here. + */ + debug(81, 1) ("icapParseChunkSize: WARNING in mid-line, ret 0\n"); + *chunk_size = 0; + return 0; + } + while (start < end) { + if (NULL == strchr(w_space, buf[start])) + break; + start++; + } + while (start < end) { + if (NULL == strchr(w_space, buf[end - 1])) + break; + end--; + } + /* + * if now end <= start we got an empty line. The previous + * chunk data should stop with a CRLF. In case that the + * other end does not follow the specs and sends no CRLF + * or too many empty lines, just continue till we have a + * non-empty line. + */ + } while (end <= start); + debug(81, 3) ("icapParseChunkSize: start=%d, end=%d\n", start, end); + + /* Non-empty line: Parse the chunk size */ + while (start < end) { + c = buf[start++]; + if (c >= 'a' && c <= 'f') { + chunkSize = chunkSize * 16 + c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + chunkSize = chunkSize * 16 + c - 'A' + 10; + } else if (c >= '0' && c <= '9') { + chunkSize = chunkSize * 16 + c - '0'; + } else { + if (!(c == ';' || c == ' ' || c == '\t')) { + /*Syntax error: Chunksize expected. */ + *chunk_size = -2; /* we are done */ + return nextStart; + } + /* Next comes a chunk extension */ + break; + } + } + /* + * if we read a zero chunk, we reached the end. Mark this for + * icapPconnTransferDone + */ + *chunk_size = (chunkSize > 0) ? chunkSize : -2; + debug(81, 3) ("icapParseChunkSize: return nextStart=%d\n", nextStart); + return nextStart; +} + +/* + * icapParseChunkedBody + * + * De-chunk an HTTP entity received from the ICAP server. + * The 'store' function pointer is storeAppend() or memBufAppend(). + */ +size_t +icapParseChunkedBody(IcapStateData * icap, STRCB * store, void *store_data) +{ + int bufOffset = 0; + size_t bw = 0; + MemBuf *cb = &icap->chunk_buf; + const char *buf = cb->buf; + int len = cb->size; + + if (icap->chunk_size == -2) { + debug(81, 3) ("zero end chunk reached\n"); + return 0; + } + debug(81, 3) ("%s:%d: chunk_size=%d\n", __FILE__, __LINE__, + icap->chunk_size); + if (icap->chunk_size < 0) { + store(store_data, buf, len); + cb->size = 0; + return (size_t) len; + } + debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, + bufOffset, len); + while (bufOffset < len) { + debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, + bufOffset, len); + if (icap->chunk_size == 0) { + int x; + x = icapParseChunkSize(buf + bufOffset, + len - bufOffset, &icap->chunk_size); + if (x < 1) { + /* didn't find a valid chunk spec */ + break; + } + bufOffset += x; + debug(81, 3) ("got chunksize %d, new offset %d\n", + icap->chunk_size, bufOffset); + if (icap->chunk_size == -2) { + debug(81, 3) ("zero end chunk reached\n"); + break; + } + } + debug(81, 3) ("%s:%d: X\n", __FILE__, __LINE__); + if (icap->chunk_size > 0) { + if (icap->chunk_size >= len - bufOffset) { + store(store_data, buf + bufOffset, len - bufOffset); + bw += (len - bufOffset); + icap->chunk_size -= (len - bufOffset); + bufOffset = len; + } else { + store(store_data, buf + bufOffset, icap->chunk_size); + bufOffset += icap->chunk_size; + bw += icap->chunk_size; + icap->chunk_size = 0; + } + } + } + if (0 == bufOffset) { + (void) 0; + } else if (bufOffset == cb->size) { + cb->size = 0; + } else { + assert(bufOffset <= cb->size); + xmemmove(cb->buf, cb->buf + bufOffset, cb->size - bufOffset); + cb->size -= bufOffset; + } + return bw; +} + +/* + * icapAddAuthUserHeader + * + * Builds and adds the X-Authenticated-User header to an ICAP request headers. + */ +void +icapAddAuthUserHeader(MemBuf * mb, auth_user_request_t * auth_user_request) +{ + char *user = authenticateUserRequestUsername(auth_user_request); + char *authuser; + size_t len, userlen, schemelen, userofslen; + char *userofs; + + if (user == NULL) { + debug(81, 5) ("icapAddAuthUserHeader: NULL username\n"); + return; + } + userlen = strlen(user); + schemelen = strlen(Config.icapcfg.auth_scheme); + len = userlen + schemelen + 1; + authuser = xcalloc(len, 1); + + if ((userofs = strstr(Config.icapcfg.auth_scheme, "%u")) == NULL) { + /* simply add user at end of string */ + snprintf(authuser, len, "%s%s", Config.icapcfg.auth_scheme, user); + } else { + userofslen = userofs - Config.icapcfg.auth_scheme; + xmemcpy(authuser, Config.icapcfg.auth_scheme, userofslen); + xmemcpy(authuser + userofslen, user, userlen); + xmemcpy(authuser + userofslen + userlen, + userofs + 2, schemelen - (userofslen + 2) + 1); + } + + memBufPrintf(mb, "X-Authenticated-User: %s\r\n", base64_encode(authuser)); + xfree(authuser); +} + +/* + * icapAddOriginIP + * + * Builds and adds the X-Server-IP header to an ICAP request headers. + */ +void +icapAddOriginIP(MemBuf * mb, const char *host) +{ + const ipcache_addrs *addrs; + struct in_addr s; + + if (host == NULL) { + debug(81, 5) ("icapAddOriginIP: NULL host\n"); + return; + } + addrs = ipcache_gethostbyname(host, IP_LOOKUP_IF_MISS); + if (addrs == NULL) { + /* + * http://www.i-cap.org/spec/draft-stecher-icap-subid-00.txt : + * + * [...] If the meta information for some header is not available, + * the header field MUST be omitted. + */ + debug(81, 5) ("icapAddOriginIP: can't tell IP address\n"); + return; + } + s = addrs->in_addrs[0]; + memBufPrintf(mb, "X-Server-IP: %s\r\n", inet_ntoa(s)); +} Index: squid/src/icap_opt.c diff -u /dev/null squid/src/icap_opt.c:1.1.16.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/icap_opt.c Wed May 17 10:58:01 2006 @@ -0,0 +1,523 @@ + +/* + * $Id$ + * + * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client OPTIONS + * AUTHOR: Ralf Horstmann + * + * 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" + +/*************************************************************/ + +/* + * network related functions for OPTIONS request + */ +static void icapOptStart(void *data); +static void icapOptTimeout(int fd, void *data); +static void icapOptConnectDone(int server_fd, int status, void *data); +static void icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data); +static void icapOptReadReply(int fd, void *data); + +/* + * reply parsing functions + */ +static int icapOptParseReply(icap_service * s, IcapOptData * i); +static void icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end); +static int icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end); + +/* + * helper functions + */ +static void icapOptDataInit(IcapOptData * i); +static void icapOptDataFree(IcapOptData * i); + +/*************************************************************/ + +#define TIMEOUT 10 + +void +icapOptInit() +{ + icap_service *s; + + /* iterate over configured services */ + s = Config.icapcfg.service_head; + while (s) { + eventAdd("icapOptStart", icapOptStart, s, 5.0, 1); + s = s->next; + } +} + +void +icapOptShutdown() +{ + icap_service *s; + + s = Config.icapcfg.service_head; + while (s) { + if (eventFind(icapOptStart, s)) { + eventDelete(icapOptStart, s); + } + s = s->next; + } +} + +/* + * mark a service as unreachable + */ +void +icapOptSetUnreachable(icap_service * s) +{ + s->unreachable = 1; + debug(81, 5) ("icapOptSetUnreachable: got called for %s\n", s->uri); + /* + * if there is an options request scheduled, delete it and add + * it again to reset the time to the default check_interval. + */ + if (eventFind(icapOptStart, s)) { + eventDelete(icapOptStart, s); + eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); + } +} + +static void +icapOptStart(void *data) +{ + icap_service *s = data; + int fd; + int ctimeout = TIMEOUT; + const char *host = s->hostname; + unsigned short port = s->port; + debug(81, 3) ("icapOptStart: starting OPTIONS request for %s (%s)\n", s->name, s->uri); + fd = comm_open(SOCK_STREAM, + 0, + getOutgoingAddr(NULL), + 0, + COMM_NONBLOCKING, + "ICAP OPTIONS connection"); + if (fd < 0) { + debug(81, 4) ("icapConnectStart: %s\n", xstrerror()); + eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); + return; + } + assert(s->opt == NULL); /* if not null, another options request might be running, which should not happen */ + s->opt = memAllocate(MEM_ICAP_OPT_DATA); + icapOptDataInit(s->opt); + cbdataLock(s); + commSetTimeout(fd, ctimeout, icapOptTimeout, s); + commConnectStart(fd, host, port, icapOptConnectDone, s); +} + +static void +icapOptTimeout(int fd, void *data) +{ + icap_service *s = data; + IcapOptData *i = s->opt; + int valid; + + debug(81, 4) ("icapOptConnectTimeout: fd=%d, service=%s\n", fd, s->uri); + + comm_close(fd); + valid = cbdataValid(s); + cbdataUnlock(s); + if (!valid) { + icapOptDataFree(i); + s->opt = NULL; + return; + } + /* try again later */ + icapOptDataFree(i); + s->opt = NULL; + s->unreachable = 1; + debug(81, 3) ("icapOptConnectTimeout: unreachable=1, service=%s\n", s->uri); + eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); + +} + +static void +icapOptConnectDone(int server_fd, int status, void *data) +{ + icap_service *s = data; + IcapOptData *i = s->opt; + MemBuf request; + int valid; + + valid = cbdataValid(s); + cbdataUnlock(s); + if (!valid) { + comm_close(server_fd); + icapOptDataFree(i); + s->opt = NULL; + return; + } + if (status != COMM_OK) { + debug(81, 3) ("icapOptConnectDone: unreachable=1, service=%s\n", s->uri); + comm_close(server_fd); + icapOptDataFree(i); + s->opt = NULL; + s->unreachable = 1; + eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); + return; + } + debug(81, 3) ("icapOptConnectDone: Connection ok. Sending Options request for %s\n", s->name); + memBufDefInit(&request); + memBufPrintf(&request, "OPTIONS %s ICAP/1.0\r\n", s->uri); + memBufPrintf(&request, "Host: %s\r\n", s->hostname); + memBufPrintf(&request, "Connection: close\r\n"); + memBufPrintf(&request, "User-Agent: ICAP-Client-Squid/1.2\r\n"); + memBufPrintf(&request, "\r\n"); + cbdataLock(s); + commSetTimeout(server_fd, TIMEOUT, icapOptTimeout, s); + comm_write_mbuf(server_fd, request, icapOptWriteComplete, s); +} + +static void +icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) +{ + icap_service *s = data; + IcapOptData *i = s->opt; + int valid; + + valid = cbdataValid(s); + cbdataUnlock(s); + if (!valid) { + comm_close(fd); + icapOptDataFree(i); + s->opt = NULL; + return; + } + debug(81, 5) ("icapOptWriteComplete: FD %d: size %d: errflag %d.\n", + fd, size, errflag); + if (size > 0) { + fd_bytes(fd, size, FD_WRITE); + kb_incr(&statCounter.icap.all.kbytes_out, size); + } + if (errflag) { + /* cancel this for now */ + debug(81, 3) ("icapOptWriteComplete: unreachable=1, service=%s\n", s->uri); + icapOptDataFree(i); + s->opt = NULL; + s->unreachable = 1; + eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); + comm_close(fd); + return; + } + cbdataLock(s); + commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, s, 0); +} + +static void +icapOptReadReply(int fd, void *data) +{ + icap_service *s = data; + IcapOptData *i = s->opt; + int size; + int len = i->size - i->offset - 1; + int valid; + + valid = cbdataValid(s); + cbdataUnlock(s); + if (!valid) { + comm_close(fd); + icapOptDataFree(i); + s->opt = NULL; + return; + } + if (len == 0) { + /* Grow the request memory area to accomodate for a large request */ + printf("PANIC: not enough memory\n"); +#if 0 + i->buf = memReallocBuf(i->buf, i->size * 2, &i->size); + debug(81, 2) ("icapoptReadReply: growing reply buffer: offset=%ld size=%ld\n", + (long) i->offset, (long) i->size); + len = i->size - i->offset - 1; +#endif + } + size = FD_READ_METHOD(fd, i->buf + i->offset, len); + i->offset += size; + debug(81, 3) ("icapOptReadReply: Got %d bytes of data\n", size); + if (size > 0) { + /* do some statistics */ + fd_bytes(fd, size, FD_READ); + kb_incr(&statCounter.icap.all.kbytes_in, size); + + /* + * some icap servers seem to ignore the "Connection: close" header. so + * after getting the complete option reply we close the connection + * ourself. + */ + if ((i->headlen = headersEnd(i->buf, i->offset))) { + debug(81, 3) ("icapOptReadReply: EndOfResponse\n"); + size = 0; + } + } + if (size < 0) { + debug(81, 3) ("icapOptReadReply: FD %d: read failure: %s.\n", fd, xstrerror()); + debug(81, 3) ("icapOptReadReply: unreachable=1, service=%s.\n", s->uri); + s->unreachable = 1; + icapOptDataFree(i); + s->opt = NULL; + eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); + comm_close(fd); + } else if (size == 0) { + /* no more data, now we can parse the reply */ + debug(81, 3) ("icapOptReadReply: FD %d: connection closed\n", fd); + i->buf[i->offset] = '\0'; /* for string functions */ + debug(81, 3) ("icapOptReadReply: unreachable=0, service=%s\n", s->uri); + + if (!icapOptParseReply(s, i)) { + debug(81, 3) ("icapOptReadReply: OPTIONS request not successful. scheduling again in %d seconds\n", Config.icapcfg.check_interval); + s->unreachable = 1; + } else + s->unreachable = 0; + + if (s->options_ttl <= 0) + s->options_ttl = Config.icapcfg.check_interval; + eventAdd("icapOptStart", icapOptStart, s, s->options_ttl, 1); + + icapOptDataFree(i); + s->opt = NULL; + comm_close(fd); + } else { + /* data received */ + /* commSetSelect(fd, Type, handler, client_data, timeout) */ + cbdataLock(s); + commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, data, 0); + } +} + +static int +icapIsolateLine(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 */ + return 0; + + if (slen == 0) /* empty line */ + return 0; + + *blk_start = *parse_start; + *blk_end = *blk_start + slen; + + /* set it to the beginning of next line */ + *parse_start = *blk_end; + while (**parse_start == '\r') /* CR */ + (*parse_start)++; + if (**parse_start == '\n') /* LF */ + (*parse_start)++; + return 1; +} + +/* process a single header entry between blk_start and blk_end */ +static void +icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end) +{ + const char *name_end = strchr(blk_start, ':'); + const int name_len = name_end ? name_end - blk_start : 0; + const char *value_start = blk_start + name_len + 1; /* skip ':' */ + int value_len; + int new; + + if (!name_len || name_end > blk_end) { + debug(81, 5) ("icapOptParseEntry: strange header. skipping\n"); + return; + } + if (name_len > 65536) { + debug(81, 5) ("icapOptParseEntry: unusual long header item. skipping.\n"); + return; + } + while (xisspace(*value_start) && value_start < blk_end) { + value_start++; + } + if (value_start >= blk_end) { + debug(81, 5) ("icapOptParseEntry: no value found\n"); + return; + } + value_len = blk_end - value_start; + + + /* extract information */ + if (!strncasecmp("Allow", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Allow\n"); + if (!strncmp("204", value_start, 3)) { + s->flags.allow_204 = 1; + } else { + debug(81, 3) ("icapOptParseEntry: Allow value unknown"); + } + } else if (!strncasecmp("Connection", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Connection\n"); + } else if (!strncasecmp("Encapsulated", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Encapsulated\n"); + } else if (!strncasecmp("ISTAG", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found ISTAG\n"); + stringClean(&s->istag); + stringLimitInit(&s->istag, value_start, value_len); + } else if (!strncasecmp("Max-Connections", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Max-Connections\n"); + errno = 0; + new = strtol(value_start, NULL, 10); + if (errno) { + debug(81, 5) ("icapOptParseEntry: Max-Connections: could not parse value\n"); + } else { + debug(81, 5) ("icapOptParseEntry: Max-Connections: new value=%d\n", new); + s->max_connections = new; + } + } else if (!strncasecmp("Methods", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Methods\n"); + } else if (!strncasecmp("Options-TTL", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Options-TTL\n"); + errno = 0; + new = strtol(value_start, NULL, 10); + if (errno) { + debug(81, 5) ("icapOptParseEntry: Options-TTL: could not parse value\n"); + } else { + debug(81, 5) ("icapOptParseEntry: Options-TTL: new value=%d\n", new); + s->options_ttl = new; + } + } else if (!strncasecmp("Preview", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Preview\n"); + errno = 0; + new = strtol(value_start, NULL, 10); + if (errno) { + debug(81, 5) ("icapOptParseEntry: Preview: could not parse value\n"); + } else { + debug(81, 5) ("icapOptParseEntry: Preview: new value=%d\n", new); + s->preview = new; + } + } else if (!strncasecmp("Service", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Service\n"); + } else if (!strncasecmp("Service-ID", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Service-ID\n"); + } else if (!strncasecmp("Transfer-Preview", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Transfer-Preview\n"); + stringClean(&s->transfer_preview); + stringLimitInit(&s->transfer_preview, value_start, value_len); + } else if (!strncasecmp("Transfer-Ignore", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Transfer-Ignore\n"); + stringClean(&s->transfer_ignore); + stringLimitInit(&s->transfer_ignore, value_start, value_len); + } else if (!strncasecmp("Transfer-Complete", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Transfer-Complete\n"); + stringClean(&s->transfer_complete); + stringLimitInit(&s->transfer_complete, value_start, value_len); + } else if (!strncasecmp("X-Include", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found X-Include\n"); + if (strstr(value_start, "X-Client-IP")) { + debug(81, 5) ("icapOptParseEntry: X-Include: found X-Client-IP\n"); + s->flags.need_x_client_ip = 1; + } + if (strstr(value_start, "X-Server-IP")) { + debug(81, 5) ("icapOptParseEntry: X-Include: found X-Server-IP\n"); + s->flags.need_x_server_ip = 1; + } + if (strstr(value_start, "X-Authenticated-User")) { + debug(81, 5) ("icapOptParseEntry: X-Include: found X-Authenticated-User\n"); + s->flags.need_x_authenticated_user = 1; + } + } else { + debug(81, 5) ("icapOptParseEntry: unknown options header\n"); + } +} + +/* parse OPTIONS reply */ +static int +icapOptParseReply(icap_service * s, IcapOptData * i) +{ + int version_major, version_minor; + const char *str_status; + int status; + const char *buf = i->buf; + const char *parse_start; + const char *head_end; + const char *blk_start; + const char *blk_end; + + if ((status = + icapParseStatusLine(i->buf, i->offset, + &version_major, &version_minor, &str_status)) < 0) { + debug(81, 2) ("icapOptParseReply: bad status line <%s>\n", i->buf); + return 0; + } + debug(81, 3) ("icapOptParseReply: got reply: \n", version_major, version_minor, status, str_status); + + if (status != 200) { + debug(81, 3) ("icapOptParseReply: status = %d != 200\n", status); + return 0; + } + parse_start = buf; + if (i->headlen == 0) + i->headlen = headersEnd(parse_start, s->opt->offset); + + if (!i->headlen) { + debug(81, 2) ("icapOptParseReply: end of headers could not be found\n"); + return 0; + } + head_end = parse_start + i->headlen - 1; + while (*(head_end - 1) == '\r') + head_end--; + assert(*(head_end - 1) == '\n'); + if (*head_end != '\r' && *head_end != '\n') + return 0; /* failure */ + + /* skip status line */ + if (!icapIsolateLine(&parse_start, &blk_start, &blk_end)) { + debug(81, 3) ("icapOptParseReply: failure in isolating status line\n"); + return 0; + + } + /* now we might start real parsing */ + while (icapIsolateLine(&parse_start, &blk_start, &blk_end)) { + if (blk_end > head_end || blk_start > head_end || blk_start >= blk_end) { + debug(81, 3) ("icapOptParseReply: header limit exceeded. finished.\n"); + break; + } + icapOptParseEntry(s, blk_start, blk_end); + } + return 1; +} + +static void +icapOptDataInit(IcapOptData * i) +{ + i->buf = memAllocBuf(HTTP_REPLY_BUF_SZ, &i->size); + i->offset = 0; + i->headlen = 0; +} + +static void +icapOptDataFree(IcapOptData * i) +{ + if (i) { + memFreeBuf(i->size, i->buf); + memFree(i, MEM_ICAP_OPT_DATA); + } +} Index: squid/src/icap_reqmod.c diff -u /dev/null squid/src/icap_reqmod.c:1.1.14.10 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/icap_reqmod.c Wed Jan 31 10:11:13 2007 @@ -0,0 +1,989 @@ + +/* + * $Id$ + * + * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client + * AUTHOR: Geetha Manjunath, Hewlett Packard Company + * + * 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 Founda