diff -ruN squid-2.6.STABLE5/icap-2_6.patch squid-icap-2.6.STABLE5/icap-2_6.patch --- squid-2.6.STABLE5/icap-2_6.patch 2006-12-10 18:50:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/icap-2_6.patch 1970-01-01 02:00:00.000000000 +0200 @@ -1,7065 +0,0 @@ -This patch is generated from the icap-2_6 branch of HEAD in squid -Tue Nov 28 01:15:46 2006 GMT -See http://devel.squid-cache.org/ - -Index: squid/configure.in -diff -u squid/configure.in:1.165 squid/configure.in:1.87.4.12 ---- squid/configure.in:1.165 Fri Nov 3 05:54:34 2006 -+++ squid/configure.in Fri Nov 3 10:47:04 2006 -@@ -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, -@@ -2346,6 +2357,8 @@ - srand48 \ - srandom \ - statfs \ -+ strnstr \ -+ strcasestr \ - strsep \ - strtoll \ - sysconf \ -@@ -2450,6 +2463,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.15 squid/include/util.h:1.13.8.2 ---- squid/include/util.h:1.15 Mon Sep 18 16:52:30 2006 -+++ squid/include/util.h Tue Sep 26 15:47:30 2006 -@@ -151,4 +151,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.48 squid/src/Makefile.am:1.34.4.9 ---- squid/src/Makefile.am:1.48 Sun Oct 29 16:51:32 2006 -+++ squid/src/Makefile.am Fri Nov 3 10:47:05 2006 -@@ -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.85 squid/src/cache_cf.c:1.61.4.11 ---- squid/src/cache_cf.c:1.85 Sat Sep 30 14:52:27 2006 -+++ squid/src/cache_cf.c Fri Nov 3 10:47:06 2006 -@@ -2385,6 +2385,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.158 squid/src/cf.data.pre:1.100.4.11 ---- squid/src/cf.data.pre:1.158 Thu Oct 12 13:51:58 2006 -+++ squid/src/cf.data.pre Fri Nov 3 10:47:06 2006 -@@ -3184,7 +3184,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 -@@ -3453,6 +3452,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.147 squid/src/client_side.c:1.89.4.13 ---- squid/src/client_side.c:1.147 Sat Sep 30 14:52:27 2006 -+++ squid/src/client_side.c Fri Nov 3 10:47:06 2006 -@@ -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,14 +157,17 @@ - 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); -+#if HS_FEAT_ICAP -+static int clientIcapReqMod(clientHttpRequest * http); -+#endif - - #if USE_IDENT - static void -@@ -382,7 +385,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; -@@ -638,6 +641,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 -@@ -1372,11 +1379,22 @@ - *H = http->next; - http->next = NULL; - 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; -@@ -1393,8 +1411,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); -@@ -1592,7 +1611,7 @@ - } - } - --static int -+int - clientCheckContentLength(request_t * r) - { - switch (r->method) { -@@ -1611,7 +1630,7 @@ - /* NOT REACHED */ - } - --static int -+int - clientCachable(clientHttpRequest * http) - { - request_t *req = http->request; -@@ -1637,7 +1656,7 @@ - } - - /* Return true if we can query our neighbors for this object */ --static int -+int - clientHierarchical(clientHttpRequest * http) - { - const char *url = http->uri; -@@ -3351,7 +3370,7 @@ - return LOG_TCP_HIT; - } - --static void -+void - clientProcessRequest(clientHttpRequest * http) - { - char *url = http->uri; -@@ -3362,6 +3381,11 @@ - RequestMethodStr[r->method], - 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 -@@ -3828,6 +3852,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) { -@@ -3931,7 +3969,15 @@ - /* add to the client request queue */ - for (H = &conn->chr; *H; H = &(*H)->next); - *H = http; -- conn->nrequests++; -+ F->pconn.uses++; -+ F->pconn.type = 0; -+ /* -+ * I wanted to lock 'http' here since its callback data for -+ * clientLifetimeTimeout(), but there's no logical place to -+ * cbdataUnlock if the timeout never happens. Maybe its safe -+ * enough to assume that if the FD is open, and the timeout -+ * triggers, that 'http' is valid. -+ */ - 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); -@@ -4102,7 +4148,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; -@@ -4231,7 +4277,7 @@ - } - - /* Abort a body request */ --static void -+void - clientAbortBody(request_t * request) - { - ConnStateData *conn = request->body_reader_data; -@@ -4273,7 +4319,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 - */ -@@ -5015,6 +5061,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.49 squid/src/comm.c:1.29.10.9 ---- squid/src/comm.c:1.49 Mon Oct 23 04:52:53 2006 -+++ squid/src/comm.c Fri Nov 3 10:47:12 2006 -@@ -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.57 squid/src/enums.h:1.45.4.6 ---- squid/src/enums.h:1.57 Sat Sep 30 14:52:28 2006 -+++ squid/src/enums.h Fri Nov 3 10:47:13 2006 -@@ -93,6 +93,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; -@@ -455,6 +456,9 @@ - PROTO_WHOIS, - PROTO_INTERNAL, - PROTO_HTTPS, -+#if HS_FEAT_ICAP -+ PROTO_ICAP, -+#endif - PROTO_MAX - } protocol_t; - -@@ -630,6 +634,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; - -@@ -730,9 +740,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) - */ -@@ -781,4 +796,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.42 squid/src/forward.c:1.20.4.9 ---- squid/src/forward.c:1.42 Sat Sep 30 14:52:28 2006 -+++ squid/src/forward.c Fri Nov 3 10:47:13 2006 -@@ -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) || -@@ -935,6 +936,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), -@@ -973,6 +976,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); -@@ -992,7 +997,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.27 squid/src/globals.h:1.22.4.5 ---- squid/src/globals.h:1.27 Mon Sep 25 12:51:46 2006 -+++ squid/src/globals.h Tue Sep 26 15:47:36 2006 -@@ -171,6 +171,9 @@ - #if HAVE_SBRK - extern void *sbrk_start; /* 0 */ - #endif -+#if HS_FEAT_ICAP -+extern 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.49 squid/src/http.c:1.28.4.11 ---- squid/src/http.c:1.49 Mon Oct 23 14:53:15 2006 -+++ squid/src/http.c Fri Nov 3 10:47:13 2006 -@@ -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; -@@ -80,6 +81,9 @@ - requestUnlink(httpState->orig_request); - httpState->request = NULL; - httpState->orig_request = NULL; -+#if HS_FEAT_ICAP -+ cbdataUnlock(httpState->icap_writer); -+#endif - cbdataFree(httpState); - } - -@@ -409,7 +413,7 @@ - } - - /* rewrite this later using new interfaces @?@ */ --static void -+void - httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) - { - StoreEntry *entry = httpState->entry; -@@ -552,24 +556,35 @@ - MemObject *mem = httpState->entry->mem_obj; - HttpReply *reply = mem->reply; - squid_off_t clen; -+ squid_off_t content_bytes_read; - debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); - debug(11, 5) ("httpPconnTransferDone: content_length=%" PRINTF_OFF_T "\n", - reply->content_length); - /* If we haven't seen the end of reply headers, we are not done */ -- if (httpState->reply_hdr_state < 2) -+ if (httpState->reply_hdr_state < 2) { -+ debug(11, 3) ("httpPconnTransferDone: reply_hdr_state=%d, returning 0\n", -+ httpState->reply_hdr_state); - return 0; -+ } - clen = httpReplyBodySize(httpState->request->method, reply); -+#ifdef HS_FEAT_ICAP -+ if (httpState->icap_writer) { -+ content_bytes_read = httpState->icap_writer->fake_content_length; -+ debug(11, 3) ("using fake conten length %" PRINTF_OFF_T "\n", content_bytes_read); -+ } else -+#endif -+ content_bytes_read = mem->inmem_hi; - /* If the body size is unknown we must wait for EOF */ - if (clen < 0) - return 0; - /* Barf if we got more than we asked for */ -- if (mem->inmem_hi > clen + reply->hdr_sz) -+ if (content_bytes_read > clen + reply->hdr_sz) - return -1; - /* If there is no message body, we can be persistent */ - if (0 == clen) - return 1; - /* If the body size is known, we must wait until we've gotten all of it. */ -- if (mem->inmem_hi < clen + reply->hdr_sz) -+ if (content_bytes_read < clen + reply->hdr_sz) - return 0; - /* We got it all */ - return 1; -@@ -636,6 +651,17 @@ - delay_id delay_id; - #endif - -+#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; -@@ -647,7 +673,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); -@@ -664,7 +718,13 @@ - clen >>= 1; - IOStats.Http.read_hist[bin]++; - } -- 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--); -@@ -694,6 +754,12 @@ - } else if (len == 0) { - /* Connection closed; retrieval done. */ - httpState->eof = 1; -+#ifdef HS_FEAT_ICAP -+ if (httpState->icap_writer && cbdataValid(httpState->icap_writer)) { -+ debug(81, 3) ("httpReadReply: EOF for ICAP writer\n"); -+ icapSendRespMod(httpState->icap_writer, buf, len, 1); -+ } -+#endif - if (httpState->reply_hdr_state < 2) - /* - * Yes Henrik, there is a point to doing this. When we -@@ -746,7 +812,28 @@ - EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); - } - } -- storeAppend(entry, buf, len); -+#ifdef HS_FEAT_ICAP -+ if (httpState->icap_writer) { -+ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); -+ if (cbdataValid(httpState->icap_writer)) { -+ icapSendRespMod(httpState->icap_writer, buf, len, 0); -+ httpState->icap_writer->fake_content_length += len; -+ } -+ } else -+#endif -+ storeAppend(entry, buf, len); -+ -+ -+ debug(11, 5) ("httpReadReply: after storeAppend FD %d read %d\n", fd, len); -+#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, -@@ -793,10 +880,21 @@ - ("httpReadReply: Excess data from \"%s %s\"\n", - RequestMethodStr[orig_request->method], - storeUrl(entry)); -- storeAppend(entry, buf, len); -+#ifdef HS_FEAT_ICAP -+ if (httpState->icap_writer) { -+ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); -+ icapSendRespMod(httpState->icap_writer, buf, len, 0); -+ httpState->icap_writer->fake_content_length += len; -+ } else -+#endif -+ storeAppend(entry, buf, len); - keep_alive = 0; - } - } -+#ifdef HS_FEAT_ICAP -+ if (httpState->icap_writer) -+ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); -+#endif - if (keep_alive) { - int pinned = 0; - #if LINUX_TPROXY -@@ -852,6 +950,10 @@ - ("httpReadReply: Excess data from \"%s %s\"\n", - RequestMethodStr[orig_request->method], - storeUrl(entry)); -+#ifdef HS_FEAT_ICAP -+ if (httpState->icap_writer) -+ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); -+#endif - fwdComplete(httpState->fwd); - comm_close(fd); - return; -@@ -862,6 +964,34 @@ - } - } - -+#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 -@@ -889,6 +1019,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", 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 -@@ -897,8 +1084,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; - } -@@ -1192,8 +1389,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)) -@@ -1319,6 +1519,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", - RequestMethodStr[orig_req->method], -@@ -1361,12 +1562,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.8 ---- /dev/null Thu Jan 1 01:00:00 1970 -+++ squid/src/icap_reqmod.c Mon Nov 27 13:45:59 2006 -@@ -0,0 +1,988 @@ -+ -+/* -+ * $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. -+ * -+ */ -+ -+#include "squid.h" -+ -+#define ICAP_PROXY_KEEP_ALIVE 0 -+ -+/* -+ * These once-static functions are required to be global for ICAP -+ */ -+ -+PF clientReadRequest; -+PF connStateFree; -+StoreEntry *clientCreateStoreEntry(clientHttpRequest *, method_t, request_flags); -+int clientReadDefer(int fd, void *data); -+int clientCheckContentLength(request_t * r); -+void clientProcessRequest(clientHttpRequest *); -+int clientCachable(clientHttpRequest *); -+int clientHierarchical(clientHttpRequest *); -+void clientReadBody(request_t * request, char *buf, size_t size, -+ CBCB * callback, void *cbdata); -+static void icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, -+ CBCB * callback, void *cbdata); -+ -+static PF icapReqModReadHttpHdrs; -+static PF icapReqModReadHttpBody; -+static CWCB icapReqModSendBodyChunk; -+static CBCB icapReqModBodyHandler; -+static BODY_HANDLER icapReqModBodyReader; -+static STRCB icapReqModMemBufAppend; -+ -+#define EXPECTED_ICAP_HEADER_LEN 256 -+static const char *crlf = "\r\n"; -+ -+/* -+ * icapExpectedHttpReqHdrSize -+ * -+ * calculate the size of the HTTP headers that we expect -+ * to read from the ICAP server. -+ */ -+static int -+icapExpectedHttpReqHdrSize(IcapStateData * icap) -+{ -+ if (icap->enc.req_body > -1 && icap->enc.req_hdr > -1) -+ return (icap->enc.req_body - icap->enc.req_hdr); -+ if (icap->enc.null_body > -1) -+ return icap->enc.null_body; -+ fatal("icapExpectedHttpReqHdrSize: unexpected case"); -+ return 0; -+} -+ -+/* -+ * icapReqModCreateClientState -+ * -+ * Creates fake client_side data structures so we can use -+ * that module to read/parse the HTTP request that we read -+ * from the ICAP server. -+ */ -+static clientHttpRequest * -+icapReqModCreateClientState(IcapStateData * icap, request_t * request) -+{ -+ clientHttpRequest *http; -+ if (!cbdataValid(icap->reqmod.client_cookie)) { -+ debug(81, 3) ("Whups, client cookie invalid\n"); -+ icap->reqmod.client_fd = -1; -+ return NULL; -+ } -+ http = cbdataAlloc(clientHttpRequest); -+ /* -+ * use our own urlCanonicalClean here, because urlCanonicalClean -+ * may strip everything after a question-mark. As http->uri -+ * is used when doing a request to a parent proxy, we need the full -+ * url here. -+ */ -+ http->uri = xstrdup(urlCanonical(icap->request)); -+ http->log_uri = xstrndup(http->uri, MAX_URL); -+ http->range_iter.boundary = StringNull; -+ http->request = requestLink(request ? request : icap->request); -+ http->flags.did_icap_reqmod = 1; -+ http->start = icap->reqmod.start; -+ if (request) -+ http->http_ver = request->http_ver; -+#if ICAP_PROXY_KEEP_ALIVE -+ /* -+ * Here it is possible becouse we are using as client_cookie the original http->conn -+ * if we will keep this code we must declare an icap->conn field........ -+ * Will work if pipeline_prefetch is not enabled -+ * We are using a dummy ConnStateData structure, just to free -+ * old clientHttpRequest :-( -+ * OK,all this code is a hack and possibly must not exists in cvs ...... -+ */ -+ -+ http->conn = icap->reqmod.client_cookie; -+ assert(http->conn->chr->next == NULL); -+ { -+ ConnStateData *dummyconn; -+ dummyconn = cbdataAlloc(ConnStateData); -+ dummyconn->fd = icap->reqmod.client_fd; -+ dummyconn->pinning.fd = -1; -+ dummyconn->chr = http->conn->chr; -+ dummyconn->chr->conn = dummyconn; -+ comm_add_close_handler(dummyconn->fd, connStateFree, dummyconn); -+ } -+ http->conn->chr = http; -+#else -+ http->conn = cbdataAlloc(ConnStateData); -+ http->conn->fd = icap->reqmod.client_fd; -+ http->conn->pinning.fd = -1; -+ http->conn->in.size = 0; -+ http->conn->in.buf = NULL; -+ http->conn->log_addr = icap->reqmod.log_addr; -+ http->conn->chr = http; -+ comm_add_close_handler(http->conn->fd, connStateFree, http->conn); -+#endif -+ http->icap_reqmod = NULL; -+ return http; -+} -+ -+/* -+ * icapReqModInterpretHttpRequest -+ * -+ * Interpret an HTTP request that we read from the ICAP server. -+ * Create some "fake" clientHttpRequest and ConnStateData structures -+ * so we can pass this new request off to the routines in -+ * client_side.c. -+ */ -+static void -+icapReqModInterpretHttpRequest(IcapStateData * icap, request_t * request) -+{ -+ clientHttpRequest *http = icapReqModCreateClientState(icap, request); -+ if (NULL == http) -+ return; -+ /* -+ * bits from clientReadRequest -+ */ -+ request->content_length = httpHeaderGetSize(&request->header, -+ HDR_CONTENT_LENGTH); -+ if (!urlCheckRequest(request) || -+ httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) { -+ ErrorState *err; -+ err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED, request); -+ request->flags.proxy_keepalive = 0; -+ http->entry = -+ clientCreateStoreEntry(http, request->method, null_request_flags); -+ errorAppendEntry(http->entry, err); -+ return; -+ } -+ if (!clientCheckContentLength(request)) { -+ ErrorState *err; -+ err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED, request); -+ http->entry = -+ clientCreateStoreEntry(http, request->method, null_request_flags); -+ errorAppendEntry(http->entry, err); -+ return; -+ } -+ /* Do we expect a request-body? */ -+ if (request->content_length > 0) { -+ debug(81, 5) ("handing request bodies in ICAP REQMOD\n"); -+ if (request->body_reader_data) -+ cbdataUnlock(request->body_reader_data); -+ request->body_reader = icapReqModBodyReader; -+ request->body_reader_data = icap; /* XXX cbdataLock? */ -+ cbdataLock(icap); /*Yes sure ..... */ -+ memBufDefInit(&icap->reqmod.http_entity.buf); -+ } -+ if (clientCachable(http)) -+ request->flags.cachable = 1; -+ if (clientHierarchical(http)) -+ request->flags.hierarchical = 1; -+ clientProcessRequest(http); -+} -+ -+/* -+ * icapReqModParseHttpError -+ * -+ * Handle an error when parsing the new HTTP request we read -+ * from the ICAP server. -+ */ -+static void -+icapReqModParseHttpError(IcapStateData * icap, const char *reason) -+{ -+ debug(81, 1) ("icapReqModParseHttpError: %s\n", reason); -+} -+ -+/* -+ * icapEntryError -+ * -+ * A wrapper for errorCon() and errorAppendEntry(). -+ */ -+static void -+icapEntryError(IcapStateData * icap, err_type et, http_status hs, int xerrno) -+{ -+ ErrorState *err; -+ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); -+ if (NULL == http) -+ return; -+ http->entry = clientCreateStoreEntry(http, -+ icap->request->method, null_request_flags); -+ err = errorCon(et, hs, icap->request); -+ err->xerrno = xerrno; -+ errorAppendEntry(http->entry, err); -+} -+ -+/* -+ * icapReqModParseHttpRequest -+ * -+ * Parse the HTTP request that we read from the ICAP server. -+ * Creates and fills in the request_t structure. -+ */ -+static void -+icapReqModParseHttpRequest(IcapStateData * icap) -+{ -+ char *mstr; -+ char *uri; -+ char *inbuf; -+ char *t; -+ char *token; -+ char *headers; -+ method_t method; -+ request_t *request; -+ http_version_t http_ver; -+ int reqlen = icap->reqmod.hdr_buf.size; -+ int hdrlen; -+ -+ /* -+ * Lazy, make a copy of the buf so I can chop it up with strtok() -+ */ -+ inbuf = xcalloc(reqlen + 1, 1); -+ memcpy(inbuf, icap->reqmod.hdr_buf.buf, reqlen); -+ -+ if ((mstr = strtok(inbuf, "\t ")) == NULL) { -+ debug(81, 1) ("icapReqModParseHttpRequest: Can't get request method\n"); -+ icapReqModParseHttpError(icap, "error:invalid-request-method"); -+ xfree(inbuf); -+ return; -+ } -+ method = urlParseMethod(mstr); -+ if (method == METHOD_NONE) { -+ debug(81, 1) ("icapReqModParseHttpRequest: Unsupported method '%s'\n", -+ mstr); -+ icapReqModParseHttpError(icap, "error:unsupported-request-method"); -+ xfree(inbuf); -+ return; -+ } -+ /* look for URL+HTTP/x.x */ -+ if ((uri = strtok(NULL, "\n")) == NULL) { -+ debug(81, 1) ("icapReqModParseHttpRequest: Missing URI\n"); -+ icapReqModParseHttpError(icap, "error:missing-url"); -+ xfree(inbuf); -+ return; -+ } -+ while (xisspace(*uri)) -+ uri++; -+ t = uri + strlen(uri); -+ assert(*t == '\0'); -+ token = NULL; -+ while (t > uri) { -+ t--; -+ if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) { -+ token = t + 1; -+ break; -+ } -+ } -+ while (t > uri && xisspace(*t)) -+ *(t--) = '\0'; -+ debug(81, 5) ("icapReqModParseHttpRequest: URI is '%s'\n", uri); -+ if (token == NULL) { -+ debug(81, 3) ("icapReqModParseHttpRequest: Missing HTTP identifier\n"); -+ icapReqModParseHttpError(icap, "error:missing-http-ident"); -+ xfree(inbuf); -+ return; -+ } -+ if (sscanf(token + 5, "%d.%d", &http_ver.major, &http_ver.minor) != 2) { -+ debug(81, 3) ("icapReqModParseHttpRequest: Invalid HTTP identifier.\n"); -+ icapReqModParseHttpError(icap, "error:invalid-http-ident"); -+ xfree(inbuf); -+ return; -+ } -+ debug(81, 6) ("icapReqModParseHttpRequest: Client HTTP version %d.%d.\n", -+ http_ver.major, http_ver.minor); -+ -+ headers = strtok(NULL, null_string); -+ hdrlen = inbuf + reqlen - headers; -+ -+ if ((request = urlParse(method, uri)) == NULL) { -+ debug(81, 3) ("Invalid URL: %s at %s:%d\n", uri, __FILE__, __LINE__); -+ icapEntryError(icap, ERR_INVALID_URL, HTTP_BAD_REQUEST, 0); -+ xfree(inbuf); -+ return; -+ } -+ /* compile headers */ -+ if (!httpHeaderParse(&request->header, headers, headers + hdrlen)) { -+ debug(81, 3) ("Failed to parse HTTP headers for: %s at %s:%d", -+ uri, __FILE__, __LINE__); -+ icapEntryError(icap, ERR_INVALID_REQ, HTTP_BAD_REQUEST, 0); -+ xfree(inbuf); -+ return; -+ } -+ debug(81, -+ 3) -+ ("icapReqModParseHttpRequest: successfully parsed the HTTP request\n"); -+ request->http_ver = http_ver; -+ request->client_addr = icap->request->client_addr; -+ request->client_port = icap->request->client_port; -+ request->my_addr = icap->request->my_addr; -+ request->my_port = icap->request->my_port; -+ request->class = icap->request->class; -+ if (icap->request->auth_user_request) { -+ /* Copy authentification info in new request */ -+ request->auth_user_request = icap->request->auth_user_request; -+ authenticateAuthUserRequestLock(request->auth_user_request); -+ } -+ request->content_length = httpHeaderGetSize(&request->header, -+ HDR_CONTENT_LENGTH); -+ if (strBuf(icap->request->extacl_log)) -+ request->extacl_log = stringDup(&icap->request->extacl_log); -+ if (icap->request->extacl_user) -+ request->extacl_user = xstrdup(icap->request->extacl_user); -+ if (icap->request->extacl_passwd) -+ request->extacl_passwd = xstrdup(icap->request->extacl_passwd); -+#if ICAP_PROXY_KEEP_ALIVE -+ /* -+ * Copy the proxy_keepalive flag from the original request -+ */ -+ request->flags.proxy_keepalive = icap->request->flags.proxy_keepalive; -+ /* -+ * If proxy_keepalive was set for the original request, make -+ * sure that the adapated request also has the necessary headers -+ * for keepalive -+ */ -+ if (request->flags.proxy_keepalive) { -+ if (!httpMsgIsPersistent(http_ver, &request->header)) -+ request->flags.proxy_keepalive = 0; -+ } -+#endif -+ icapReqModInterpretHttpRequest(icap, request); -+ xfree(inbuf); -+} -+ -+/* -+ * icapReqModHandoffRespMod -+ * -+ * Handles the case where a REQMOD request results in an HTTP REPLY -+ * (instead of an ICAP REPLY that contains a new HTTP REQUEST). We -+ * prepare the IcapStateData for passing off to the icap_reqmod -+ * code, where we have functions for reading HTTP replies in ICAP -+ * messages. -+ */ -+static void -+icapReqModHandoffRespMod(IcapStateData * icap) -+{ -+ extern PF icapReadReply; -+ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); -+ if (NULL == http) -+ return; -+ assert(icap->request); -+ http->entry = clientCreateStoreEntry(http, -+ icap->request->method, icap->request->flags); -+ icap->respmod.entry = http->entry; -+ storeLockObject(icap->respmod.entry); -+ -+ /* icap->http_flags = ? */ -+ memBufDefInit(&icap->respmod.buffer); -+ memBufDefInit(&icap->chunk_buf); -+ assert(icap->current_service); -+ icapReadReply(icap->icap_fd, icap); -+} -+ -+/* -+ * icapReqModKeepAliveOrClose -+ * -+ * Called when we are done reading from the ICAP server. -+ * Either close the connection or keep it open for a future -+ * transaction. -+ */ -+static void -+icapReqModKeepAliveOrClose(IcapStateData * icap) -+{ -+ int fd = icap->icap_fd; -+ debug(81, 3) ("%s:%d FD %d\n", __FILE__, __LINE__, fd); -+ if (fd < 0) -+ return; -+ if (!icap->flags.keep_alive) { -+ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, -+ __LINE__); -+ comm_close(fd); -+ return; -+ } -+ if (icap->request->content_length < 0) { -+ /* no message body */ -+ debug(81, 3) ("%s:%d no message body\n", __FILE__, __LINE__); -+ if (1 != icap->reqmod.hdr_state) { -+ /* didn't get to end of HTTP headers */ -+ debug(81, 3) ("%s:%d didnt find end of headers, closing\n", -+ __FILE__, __LINE__); -+ comm_close(fd); -+ return; -+ } -+ } else if (icap->reqmod.http_entity.bytes_read != -+ icap->request->content_length) { -+ debug(81, 3) ("%s:%d bytes_read (%" PRINTF_OFF_T ") != content_length (%" PRINTF_OFF_T ")\n", -+ __FILE__, __LINE__, icap->reqmod.http_entity.bytes_read, -+ icap->request->content_length); -+ /* an error */ -+ comm_close(fd); -+ return; -+ } -+ debug(81, 3) ("%s:%d looks good, keeping alive\n", __FILE__, __LINE__); -+ commSetDefer(fd, NULL, NULL); -+ commSetTimeout(fd, -1, NULL, NULL); -+ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); -+ comm_remove_close_handler(fd, icapStateFree, icap); -+ pconnPush(fd, icap->current_service->hostname, icap->current_service->port, NULL, NULL, 0); -+ icap->icap_fd = -1; -+ icapStateFree(-1, icap); -+} -+ -+/* -+ * icapReqModReadHttpHdrs -+ * -+ * Read the HTTP reply from the ICAP server. Uses the values -+ * from the ICAP Encapsulation header to know how many bytes -+ * to read. -+ */ -+static void -+icapReqModReadHttpHdrs(int fd, void *data) -+{ -+ IcapStateData *icap = data; -+ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); -+ int rl; -+ debug(81, 3) ("icapReqModReadHttpHdrs:\n"); -+ assert(fd == icap->icap_fd); -+ assert(icap->enc.req_hdr == 0); -+ if (0 == icap->reqmod.hdr_state) { -+ int expect = icapExpectedHttpReqHdrSize(icap); -+ int so_far = icap->http_header_bytes_read_so_far; -+ int needed = expect - so_far; -+ debug(81, 3) ("expect=%d\n", expect); -+ debug(81, 3) ("so_far=%d\n", so_far); -+ debug(81, 3) ("needed=%d\n", needed); -+ assert(needed >= 0); -+ if (0 == expect) { -+ fatalf("unexpected condition in %s:%d", __FILE__, __LINE__); -+ } -+ rl = FD_READ_METHOD(fd, tmpbuf, needed); -+ debug(81, 3) ("icapReqModReadHttpHdrs: read %d bytes\n", rl); -+ if (rl < 0) { -+ fatalf("need to handle read error at %s:%d", __FILE__, __LINE__); -+ } -+ fd_bytes(fd, rl, FD_READ); -+ kb_incr(&statCounter.icap.all.kbytes_in, rl); -+ memBufAppend(&icap->reqmod.hdr_buf, tmpbuf, rl); -+ icap->http_header_bytes_read_so_far += rl; -+ if (rl != needed) { -+ /* still more header data to read */ -+ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, -+ 0); -+ return; -+ } -+ icap->reqmod.hdr_state = 1; -+ } -+ assert(1 == icap->reqmod.hdr_state); -+ debug(81, 3) ("icapReqModReadHttpHdrs: read the entire request headers\n"); -+ icapReqModParseHttpRequest(icap); -+ if (-1 == icap->reqmod.client_fd) { -+ /* we detected that the original client_side went away */ -+ icapReqModKeepAliveOrClose(icap); -+ } else if (icap->enc.req_body > -1) { -+ icap->chunk_size = 0; -+ memBufDefInit(&icap->chunk_buf); -+ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); -+ } else { -+ icapReqModKeepAliveOrClose(icap); -+ } -+} -+ -+ -+/* -+ * icapReqModReadIcapPart -+ * -+ * Read the ICAP reply header. -+ */ -+static void -+icapReqModReadIcapPart(int fd, void *data) -+{ -+ IcapStateData *icap = data; -+ int version_major, version_minor; -+ const char *str_status; -+ int x; -+ const char *start; -+ const char *end; -+ int status; -+ int isIcap = 0; -+ int directResponse = 0; -+ -+ debug(81, 5) ("icapReqModReadIcapPart: FD %d httpState = %p\n", fd, data); -+ statCounter.syscalls.sock.reads++; -+ -+ x = icapReadHeader(fd, icap, &isIcap); -+ if (x < 0) { -+ /* Did not find a proper ICAP response */ -+ debug(81, 3) ("ICAP : Error path!\n"); -+ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, -+ errno); -+ comm_close(fd); -+ return; -+ } -+ if (x == 0) { -+ /* -+ * Waiting for more headers. Schedule new read hander, but -+ * don't reset timeout. -+ */ -+ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); -+ return; -+ } -+ /* -+ * Parse the ICAP header -+ */ -+ assert(icap->icap_hdr.size); -+ debug(81, 3) ("Read icap header : <%s>\n", icap->icap_hdr.buf); -+ if ((status = -+ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, -+ &version_major, &version_minor, &str_status)) < 0) { -+ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); -+ /* is this correct in case of ICAP protocol error? */ -+ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, -+ errno); -+ comm_close(fd); -+ return; -+ }; -+ if (200 != status && 201 != status) { -+ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); -+ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, -+ errno); -+ comm_close(fd); -+ return; -+ } -+ icapSetKeepAlive(icap, icap->icap_hdr.buf); -+ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { -+ icapParseEncapsulated(icap, start, end); -+ } else { -+ debug(81, -+ 1) -+ ("WARNING: icapReqModReadIcapPart() did not find 'Encapsulated' header\n"); -+ } -+ if (icap->enc.res_hdr > -1) -+ directResponse = 1; -+ else if (icap->enc.res_body > -1) -+ directResponse = 1; -+ else -+ directResponse = 0; -+ debug(81, 3) ("icapReqModReadIcapPart: directResponse=%d\n", -+ directResponse); -+ -+ /* Check whether it is a direct reply - if so over to http part */ -+ if (directResponse) { -+ debug(81, -+ 3) -+ ("icapReqModReadIcapPart: FD %d, processing HTTP response for REQMOD!\n", -+ fd); -+ /* got the reply, no need to come here again */ -+ icap->flags.wait_for_reply = 0; -+ icap->flags.got_reply = 1; -+ icapReqModHandoffRespMod(icap); -+ return; -+ } -+ memBufDefInit(&icap->reqmod.hdr_buf); -+ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, 0); -+ return; -+} -+ -+/* -+ * icapSendReqModDone -+ * -+ * Called after we've sent the ICAP request. Checks for errors -+ * and installs the handler functions for the next step. -+ */ -+static void -+icapSendReqModDone(int fd, char *bufnotused, size_t size, int errflag, -+ void *data) -+{ -+ IcapStateData *icap = data; -+ -+ debug(81, 5) ("icapSendReqModDone: 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 == COMM_ERR_CLOSING) -+ return; -+ if (errflag) { -+ debug(81, 3) ("icapSendReqModDone: unreachable=1, service=%s\n", -+ icap->current_service->uri); -+ icapOptSetUnreachable(icap->current_service); -+ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, -+ errno); -+ comm_close(fd); -+ return; -+ } -+ /* Schedule read reply. */ -+ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); -+ /* -+ * Set the read timeout here because it hasn't been set yet. -+ * We only set the read timeout after the request has been -+ * fully written to the server-side. If we start the timeout -+ * after connection establishment, then we are likely to hit -+ * the timeout for POST/PUT requests that have very large -+ * request bodies. -+ */ -+ commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, icap); -+} -+ -+ -+/* -+ * icapSendReqMod -+ * -+ * Send the ICAP request, including HTTP request, to the ICAP server -+ * after connection has been established. -+ */ -+static void -+icapSendReqMod(int fd, int status, void *data) -+{ -+ MemBuf mb; -+ MemBuf mb_hdr; -+ Packer p; -+ IcapStateData *icap = data; -+ char *client_addr; -+ int icap_fd = icap->icap_fd; -+ icap_service *service; -+ CWCB *theCallback; -+ -+ debug(81, 5) ("icapSendReqMod FD %d, status %d\n", fd, status); -+ icap->flags.connect_pending = 0; -+ -+ if (COMM_OK != status) { -+ debug(81, 1) ("Could not connect to ICAP server %s:%d: %s\n", -+ icap->current_service->hostname, -+ icap->current_service->port, xstrerror()); -+ debug(81, 3) ("icapSendReqMod: unreachable=1, service=%s\n", -+ icap->current_service->uri); -+ icapOptSetUnreachable(icap->current_service); -+ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_SERVICE_UNAVAILABLE, errno); -+ comm_close(fd); -+ return; -+ } -+ fd_table[fd].pconn.uses++; -+ fd_table[fd].pconn.type = 2; -+ if (icap->request->content_length > 0) -+ theCallback = icapReqModSendBodyChunk; -+ else -+ theCallback = icapSendReqModDone; -+ -+ memBufDefInit(&mb); -+ memBufDefInit(&mb_hdr); -+ memBufPrintf(&mb_hdr, "%s %s HTTP/%d.%d\r\n", -+ RequestMethodStr[icap->request->method], -+ icap->reqmod.uri, -+ icap->request->http_ver.major, icap->request->http_ver.minor); -+ packerToMemInit(&p, &mb_hdr); -+ httpHeaderPackInto(&icap->request->header, &p); -+ packerClean(&p); -+ memBufAppend(&mb_hdr, crlf, 2); -+ service = icap->current_service; -+ assert(service); -+ client_addr = inet_ntoa(icap->request->client_addr); -+ -+ memBufPrintf(&mb, "REQMOD %s ICAP/1.0\r\n", service->uri); -+ memBufPrintf(&mb, "Encapsulated: req-hdr=0"); -+ /* TODO: Change the offset using 'request' if needed */ -+ if (icap->request->content_length > 0) -+ memBufPrintf(&mb, ", req-body=%d", mb_hdr.size); -+ else -+ memBufPrintf(&mb, ", null-body=%d", mb_hdr.size); -+ memBufAppend(&mb, crlf, 2); -+ -+ if (service->flags.need_x_client_ip && Config.icapcfg.send_client_ip) -+ memBufPrintf(&mb, "X-Client-IP: %s\r\n", client_addr); -+ -+ if (service->flags.need_x_server_ip && Config.icapcfg.send_server_ip) -+ icapAddOriginIP(&mb, icap->request->host); -+ -+ if ((service->flags.need_x_authenticated_user -+ && Config.icapcfg.send_auth_user) -+ && (icap->request->auth_user_request != NULL)) -+ icapAddAuthUserHeader(&mb, icap->request->auth_user_request); -+ if (service->keep_alive) { -+ icap->flags.keep_alive = 1; -+ } else { -+ icap->flags.keep_alive = 0; -+ memBufAppend(&mb, "Connection: close\r\n", 19); -+ } -+ memBufAppend(&mb, crlf, 2); -+ memBufAppend(&mb, mb_hdr.buf, mb_hdr.size); -+ memBufClean(&mb_hdr); -+ -+ debug(81, 5) ("icapSendReqMod: FD %d writing {%s}\n", icap->icap_fd, -+ mb.buf); -+ comm_write_mbuf(icap_fd, mb, theCallback, icap); -+} -+ -+/* -+ * icapReqModStart -+ * -+ * Initiate an ICAP REQMOD transaction. Create and fill in IcapStateData -+ * structure and request a TCP connection to the server. -+ */ -+IcapStateData * -+icapReqModStart(icap_service * service, const char *uri, request_t * request, -+ int fd, struct timeval start, struct in_addr log_addr, void *cookie) -+{ -+ IcapStateData *icap = NULL; -+ -+ debug(81, 3) ("icapReqModStart: type=%d\n", (int) service->type); -+ -+ switch (service->type) { -+ case ICAP_SERVICE_REQMOD_PRECACHE: -+ break; -+ default: -+ fatalf("icapReqModStart: unsupported service type '%s'\n", -+ icap_service_type_str[service->type]); -+ break; -+ } -+ -+ if (service->unreachable) { -+ if (service->bypass) { -+ debug(81, -+ 5) ("icapReqModStart: BYPASS because service unreachable: %s\n", -+ service->uri); -+ return NULL; -+ } else { -+ debug(81, -+ 5) ("icapReqModStart: ERROR because service unreachable: %s\n", -+ service->uri); -+ return (IcapStateData *) - 1; -+ } -+ } -+ icap = icapAllocate(); -+ if (!icap) { -+ debug(81, 3) ("icapReqModStart: icapAllocate() failed\n"); -+ return NULL; -+ } -+ icap->current_service = service; -+ icap->preview_size = service->preview; -+ icap->reqmod.uri = uri; /* XXX should be xstrdup? */ -+ icap->reqmod.start = start; -+ icap->reqmod.log_addr = log_addr; -+ icap->request = requestLink(request); -+ icap->reqmod.hdr_state = 0; -+ icap->reqmod.client_fd = fd; -+ icap->reqmod.client_cookie = cookie; -+ cbdataLock(icap->reqmod.client_cookie); -+ -+ if (!icapConnect(icap, icapSendReqMod)) -+ return NULL; -+ -+ statCounter.icap.all.requests++; -+ debug(81, 3) ("icapReqModStart: returning %p\n", icap); -+ return icap; -+} -+ -+/* -+ * icapReqModSendBodyChunk -+ * -+ * A "comm_write" callback. This is called after comm_write() does -+ * its job to let us know how things went. If there are no errors, -+ * get another chunk of the body from client_side. -+ */ -+static void -+icapReqModSendBodyChunk(int fd, char *bufnotused, size_t size, int errflag, -+ void *data) -+{ -+ IcapStateData *icap = data; -+ debug(81, 3) ("icapReqModSendBodyChunk: FD %d wrote %d errflag %d.\n", -+ fd, (int) size, errflag); -+ if (errflag == COMM_ERR_CLOSING) -+ return; -+ if (errflag) { -+ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, -+ errno); -+ comm_close(fd); -+ return; -+ } -+ clientReadBody(icap->request, -+ memAllocate(MEM_8K_BUF), 8192, icapReqModBodyHandler, icap); -+} -+ -+/* -+ * icapReqModBodyHandler -+ * -+ * Called after Squid gets a chunk of the request entity from the -+ * client side. The body is chunkified and passed to comm_write. -+ * The comm_write callback depends on whether or not this is the -+ * last chunk. -+ */ -+static void -+icapReqModBodyHandler(char *buf, ssize_t size, void *data) -+{ -+ IcapStateData *icap = data; -+ MemBuf mb; -+ CWCB *theCallback = icapReqModSendBodyChunk; -+ if (size < 0) { -+ debug(81, 1) ("icapReqModBodyHandler: %s\n", xstrerror()); -+ memFree8K(buf); -+ return; -+ } -+ memBufDefInit(&mb); -+ debug(81, 3) ("icapReqModBodyHandler: writing chunk size %d\n", size); -+ memBufPrintf(&mb, "%x\r\n", size); -+ if (size) -+ memBufAppend(&mb, buf, size); -+ else -+ theCallback = icapSendReqModDone; -+ memBufAppend(&mb, crlf, 2); -+ memFree8K(buf); -+ comm_write_mbuf(icap->icap_fd, mb, theCallback, icap); -+} -+ -+/* -+ * icapReqModReadHttpBody -+ * -+ * The read handler for the client's HTTP connection when reading -+ * message bodies. Called by comm_select(). -+ */ -+static void -+icapReqModReadHttpBody(int fd, void *data) -+{ -+ IcapStateData *icap = data; -+ int len; -+ debug(81, 3) ("icapReqModReadHttpBody: FD %d called\n", fd); -+ len = memBufRead(fd, &icap->chunk_buf); -+ debug(81, 3) ("icapReqModReadHttpBody: read returns %d\n", len); -+ if (len < 0) { -+ debug(81, 3) ("icapReqModReadHttpBody: FD %d %s\n", fd, xstrerror()); -+ if (!ignoreErrno(errno)) -+ icap->flags.reqmod_http_entity_eof = 1; -+ } else if (0 == len) { -+ debug(81, 3) ("icapReqModReadHttpBody: FD %d EOF\n", fd); -+ icap->flags.reqmod_http_entity_eof = 1; -+ } else { -+ fd_bytes(fd, len, FD_READ); -+ kb_incr(&statCounter.icap.all.kbytes_in, len); -+ icap->reqmod.http_entity.bytes_read += -+ icapParseChunkedBody(icap, -+ icapReqModMemBufAppend, &icap->reqmod.http_entity.buf); -+ } -+ if (icap->chunk_size < 0 ) -+ icap->flags.reqmod_http_entity_eof = 1; -+ -+ if (!icap->flags.reqmod_http_entity_eof) -+ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); -+ /* -+ * Notify the other side if it is waiting for data from us -+ */ -+ debug(81, 3) ("%s:%d http_entity.callback=%p\n", __FILE__, __LINE__, -+ icap->reqmod.http_entity.callback); -+ debug(81, 3) ("%s:%d http_entity.buf.size=%d\n", __FILE__, __LINE__, -+ icap->reqmod.http_entity.buf.size); -+ if (icap->reqmod.http_entity.callback) { -+ icapReqModPassHttpBody(icap, -+ icap->reqmod.http_entity.callback_buf, -+ icap->reqmod.http_entity.callback_bufsize, -+ icap->reqmod.http_entity.callback, -+ icap->reqmod.http_entity.callback_data); -+ icap->reqmod.http_entity.callback = NULL; -+ cbdataUnlock(icap->reqmod.http_entity.callback_data); -+ } -+} -+ -+/* -+ * icapReqModPassHttpBody -+ * -+ * Called from http.c after request headers have been sent. -+ * This function feeds the http.c module chunks of the request -+ * body that were stored in the http_entity.buf MemBuf. -+ */ -+static void -+icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, -+ CBCB * callback, void *cbdata) -+{ -+ debug(81, 3) ("icapReqModPassHttpBody: called\n"); -+ if (!buf) { -+ debug(81, 1) ("icapReqModPassHttpBody: FD %d called with %p, %d, %p (request aborted)\n", -+ icap->icap_fd, buf, (int) size, cbdata); -+ comm_close(icap->icap_fd); -+ return; -+ } -+ if (!cbdataValid(cbdata)) { -+ debug(81, -+ 1) -+ ("icapReqModPassHttpBody: FD %d callback data invalid, closing\n", -+ icap->icap_fd); -+ comm_close(icap->icap_fd); /*It is better to be sure that the connection will be closed..... */ -+ /*icapReqModKeepAliveOrClose(icap); */ -+ return; -+ } -+ debug(81, 3) ("icapReqModPassHttpBody: entity buf size = %d\n", -+ icap->reqmod.http_entity.buf.size); -+ if (icap->reqmod.http_entity.buf.size) { -+ int copy_sz = icap->reqmod.http_entity.buf.size; -+ if (copy_sz > size) -+ copy_sz = size; -+ xmemcpy(buf, icap->reqmod.http_entity.buf.buf, copy_sz); -+ /* XXX don't let Alex see this ugliness */ -+ xmemmove(icap->reqmod.http_entity.buf.buf, -+ icap->reqmod.http_entity.buf.buf + copy_sz, -+ icap->reqmod.http_entity.buf.size - copy_sz); -+ icap->reqmod.http_entity.buf.size -= copy_sz; -+ debug(81, 3) ("icapReqModPassHttpBody: giving %d bytes to other side\n", -+ copy_sz); -+ callback(buf, copy_sz, cbdata); -+ debug(81, 3) ("icapReqModPassHttpBody: entity buf size now = %d\n", -+ icap->reqmod.http_entity.buf.size); -+ return; -+ } -+ if (icap->flags.reqmod_http_entity_eof) { -+ debug(81, 3) ("icapReqModPassHttpBody: signalling EOF\n"); -+ callback(buf, 0, cbdata); -+ icapReqModKeepAliveOrClose(icap); -+ return; -+ } -+ /* -+ * We have no data for the other side at this point. Save all -+ * these values and use them when we do have data. -+ */ -+ assert(NULL == icap->reqmod.http_entity.callback); -+ icap->reqmod.http_entity.callback = callback; -+ icap->reqmod.http_entity.callback_data = cbdata; -+ icap->reqmod.http_entity.callback_buf = buf; -+ icap->reqmod.http_entity.callback_bufsize = size; -+ cbdataLock(icap->reqmod.http_entity.callback_data); -+} -+ -+/* -+ * Body reader handler for use with request->body_reader function -+ * Simple a wrapper for icapReqModPassHttpBody function -+ */ -+ -+static void -+icapReqModBodyReader(request_t * request, char *buf, size_t size, -+ CBCB * callback, void *cbdata) -+{ -+ IcapStateData *icap = request->body_reader_data; -+ icapReqModPassHttpBody(icap, buf, size, callback, cbdata); -+} -+ -+/* -+ * icapReqModMemBufAppend -+ * -+ * stupid wrapper to eliminate compiler warnings -+ */ -+static void -+icapReqModMemBufAppend(void *data, const char *buf, ssize_t size) -+{ -+ memBufAppend(data, buf, size); -+} -Index: squid/src/icap_respmod.c -diff -u /dev/null squid/src/icap_respmod.c:1.1.14.7 ---- /dev/null Thu Jan 1 01:00:00 1970 -+++ squid/src/icap_respmod.c Tue Sep 26 15:47:36 2006 -@@ -0,0 +1,1058 @@ -+ -+/* -+ * $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. -+ * -+ */ -+ -+#include "squid.h" -+ -+static CWCB icapSendRespModDone; -+static PF icapRespModGobble; -+extern PF icapReadReply; -+static PF icapRespModReadReply; -+static void icapRespModKeepAliveOrClose(IcapStateData * icap); -+static int icapReadReply2(IcapStateData * icap); -+static void icapReadReply3(IcapStateData * icap); -+ -+#define EXPECTED_ICAP_HEADER_LEN 256 -+const char *crlf = "\r\n"; -+ -+static void -+getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, -+ const char *client_addr, IcapStateData * icap, const icap_service * service) -+{ -+ memBufPrintf(mb, "RESPMOD %s ICAP/1.0\r\nEncapsulated:", service->uri); -+ if (o1 >= 0) -+ memBufPrintf(mb, " req-hdr=%1d", o1); -+ if (o2 >= 0) -+ memBufPrintf(mb, ", res-hdr=%1d", o2); -+ if (o3 >= 0) -+ memBufPrintf(mb, ", res-body=%1d", o3); -+ else -+ memBufPrintf(mb, ", null-body=%1d", -o3); -+ memBufPrintf(mb, crlf); -+ -+ if (service->flags.need_x_client_ip && Config.icapcfg.send_client_ip) { -+ memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); -+ } -+ if (service->flags.need_x_server_ip && Config.icapcfg.send_server_ip) -+ icapAddOriginIP(mb, icap->request->host); -+ -+ if ((service->flags.need_x_authenticated_user -+ && Config.icapcfg.send_auth_user) -+ && (icap->request->auth_user_request != NULL)) { -+ icapAddAuthUserHeader(mb, icap->request->auth_user_request); -+ } -+#if NOT_YET_FINISHED -+ if (Config.icapcfg.trailers) { -+ memBufPrintf(mb, "X-TE: trailers\r\n"); -+ } -+#endif -+} -+ -+static int -+buildRespModHeader(MemBuf * mb, IcapStateData * icap, char *buf, -+ ssize_t len, int theEnd) -+{ -+ MemBuf mb_hdr; -+ char *client_addr; -+ int o2 = 0; -+ int o3 = 0; -+ int hlen; -+ int consumed; -+ icap_service *service; -+ HttpReply *r; -+ -+ if (memBufIsNull(&icap->respmod.req_hdr_copy)) -+ memBufDefInit(&icap->respmod.req_hdr_copy); -+ -+ memBufAppend(&icap->respmod.req_hdr_copy, buf, len); -+ -+ if (icap->respmod.req_hdr_copy.size > 4 && strncmp(icap->respmod.req_hdr_copy.buf, "HTTP/", 5)) { -+ debug(81, 3) ("buildRespModHeader: Non-HTTP-compliant header: '%s'\n", buf); -+ /* -+ *Possible we can consider that we did not have http responce headers -+ *(maybe HTTP 0.9 protocol), lets returning -1... -+ */ -+ consumed = -1; -+ o2 = -1; -+ memBufDefInit(&mb_hdr); -+ httpBuildRequestPrefix(icap->request, icap->request, -+ icap->respmod.entry, &mb_hdr, icap->http_flags); -+ o3 = mb_hdr.size; -+ } else { -+ -+ hlen = headersEnd(icap->respmod.req_hdr_copy.buf, -+ icap->respmod.req_hdr_copy.size); -+ debug(81, 3) ("buildRespModHeader: headersEnd = %d(%s)\n", hlen, buf); -+ if (0 == hlen) -+ return 0; -+ -+ /* -+ * calc how many bytes from this 'buf' went towards the -+ * reply header. -+ */ -+ consumed = hlen - (icap->respmod.req_hdr_copy.size - len); -+ debug(81, 3) ("buildRespModHeader: consumed = %d\n", consumed); -+ -+ -+ /* -+ * now, truncate our req_hdr_copy at the header end. -+ * this 'if' statement might be unncessary? -+ */ -+ if (hlen < icap->respmod.req_hdr_copy.size) -+ icap->respmod.req_hdr_copy.size = hlen; -+ -+ /* Copy request header */ -+ memBufDefInit(&mb_hdr); -+ httpBuildRequestPrefix(icap->request, icap->request, -+ icap->respmod.entry, &mb_hdr, icap->http_flags); -+ o2 = mb_hdr.size; -+ -+ /* Copy response header - Append to request header mbuffer */ -+ memBufAppend(&mb_hdr, -+ icap->respmod.req_hdr_copy.buf, icap->respmod.req_hdr_copy.size); -+ o3 = mb_hdr.size; -+ } -+ -+ service = icap->current_service; -+ assert(service); -+ client_addr = inet_ntoa(icap->request->client_addr); -+ -+ r = httpReplyCreate(); -+ httpReplyParse(r, icap->respmod.req_hdr_copy.buf, -+ icap->respmod.req_hdr_copy.size); -+ icap->respmod.res_body_sz = httpReplyBodySize(icap->request->method, r); -+ httpReplyDestroy(r); -+ if (icap->respmod.res_body_sz) -+ getICAPRespModString(mb, 0, o2, o3, client_addr, icap, service); -+ else -+ getICAPRespModString(mb, 0, o2, -o3, client_addr, icap, service); -+ if (Config.icapcfg.preview_enable) -+ if (icap->preview_size >= 0) { -+ memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size); -+ icap->flags.preview_done = 0; -+ } -+ if (service->keep_alive) { -+ icap->flags.keep_alive = 1; -+ memBufAppend(mb, "Connection: keep-alive\r\n", 24); -+ } else { -+ icap->flags.keep_alive = 0; -+ memBufAppend(mb, "Connection: close\r\n", 19); -+ } -+ memBufAppend(mb, crlf, 2); -+ memBufAppend(mb, mb_hdr.buf, mb_hdr.size); -+ memBufClean(&mb_hdr); -+ -+ -+ return consumed; -+} -+ -+ -+void -+icapSendRespMod(IcapStateData * icap, char *buf, int len, int theEnd) -+{ -+ MemBuf mb; -+#if ICAP_PREVIEW -+ int size; -+ const int preview_size = icap->preview_size; -+#endif -+ debug(81, 5) ("icapSendRespMod: FD %d, len %d, theEnd %d\n", -+ icap->icap_fd, len, theEnd); -+ -+ if (icap->flags.no_content) { -+ /* -+ * ICAP server said there are no modifications to make, so -+ * just append this data to the StoreEntry -+ */ -+ if (icap->respmod.resp_copy.size) { -+ /* -+ * first copy the data that we already sent to the ICAP server -+ */ -+ memBufAppend(&icap->chunk_buf, -+ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); -+ icap->respmod.resp_copy.size = 0; -+ } -+ debug(81, 5) ("icapSendRepMod: len=%d theEnd=%d write_pending=%d\n", -+ len, theEnd, icap->flags.write_pending); -+ if (len) { -+ /* -+ * also copy any new data from the HTTP side -+ */ -+ memBufAppend(&icap->chunk_buf, buf, len); -+ } -+ (void) icapReadReply2(icap); -+ return; -+ } -+ if (theEnd) { -+ if (icap->respmod.res_body_sz) -+ icap->flags.send_zero_chunk = 1; -+ icap->flags.http_server_eof = 1; -+ } -+ /* -+ * httpReadReply is going to call us with a chunk and then -+ * right away again with an EOF if httpPconnTransferDone() is true. -+ * Since the first write is already dispatched, we'll have to -+ * hack this in somehow. -+ */ -+ if (icap->flags.write_pending) { -+ debug(81, 3) ("icapSendRespMod: oops, write_pending=1\n"); -+ assert(theEnd); -+ assert(len == 0); -+ return; -+ } -+ if (!cbdataValid(icap)) { -+ debug(81, 3) ("icapSendRespMod: failed to establish connection?\n"); -+ return; -+ } -+ memBufDefInit(&mb); -+ -+#if SUPPORT_ICAP_204 || ICAP_PREVIEW -+ /* -+ * make a copy of the response in case ICAP server gives us a 204 -+ */ -+ /* -+ * This piece of code is problematic for 204 responces outside preview. -+ * The icap->respmod.resp_copy continues to filled until we had responce -+ * If the icap server waits to gets all data before sends its responce -+ * then we are puting all downloading object to the main system memory. -+ * My opinion is that 204 responces outside preview must be disabled ..... -+ * /chtsanti -+ */ -+ -+ if (len && icap->flags.copy_response) { -+ if (memBufIsNull(&icap->respmod.resp_copy)) -+ memBufDefInit(&icap->respmod.resp_copy); -+ memBufAppend(&icap->respmod.resp_copy, buf, len); -+ } -+#endif -+ -+ if (icap->sc == 0) { -+ // http connection has been closed without sending us anything -+ if (len == 0 && theEnd == 1) { -+ ErrorState *err; -+ err = errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY, icap->request); -+ errorAppendEntry(icap->respmod.entry, err); -+ comm_close(icap->icap_fd); -+ return; -+ } -+ /* No data sent yet. Start with headers */ -+ if ((icap->sc = buildRespModHeader(&mb, icap, buf, len, theEnd)) > 0) { -+ buf += icap->sc; -+ len -= icap->sc; -+ } -+ /* -+ * Then we do not have http responce headers. All data (previous and those in buf) -+ * now are exist to icap->respmod.req_hdr_copy. Lets get them back....... -+ */ -+ if (icap->sc < 0) { -+ memBufAppend(&icap->respmod.buffer, -+ icap->respmod.req_hdr_copy.buf, -+ icap->respmod.req_hdr_copy.size); -+ icap->sc = icap->respmod.req_hdr_copy.size; -+ icap->respmod.req_hdr_copy.size = 0; -+ buf = NULL; -+ len = 0; -+ } -+ } -+ if (0 == icap->sc) { -+ /* check again; bail if we're not ready to send ICAP/HTTP hdrs */ -+ debug(81, 5) ("icapSendRespMod: dont have full HTTP response hdrs\n"); -+ memBufClean(&mb); -+ return; -+ } -+#if ICAP_PREVIEW -+ if (preview_size < 0 || !Config.icapcfg.preview_enable) /* preview feature off */ -+ icap->flags.preview_done = 1; -+ -+ if (!icap->flags.preview_done) { -+ /* preview not yet sent */ -+ if (icap->sc > 0 && icap->respmod.buffer.size <= preview_size -+ && len > 0) { -+ /* Try to collect at least preview_size+1 bytes */ -+ /* By collecting one more byte than needed for preview we know best */ -+ /* whether we have to send the ieof chunk extension */ -+ size = icap->respmod.buffer.size + len; -+ if (size > preview_size + 1) -+ size = preview_size + 1; -+ size -= icap->respmod.buffer.size; -+ debug(81, -+ 3) -+ ("icapSendRespMod: FD %d: copy %d more bytes to preview buffer.\n", -+ icap->icap_fd, size); -+ memBufAppend(&icap->respmod.buffer, buf, size); -+ buf = ((char *) buf) + size; -+ len -= size; -+ } -+ if (icap->respmod.buffer.size > preview_size || theEnd) { -+ /* we got enough bytes for preview or this is the last call */ -+ /* add preview preview now */ -+ if (icap->respmod.buffer.size > 0) { -+ size = icap->respmod.buffer.size; -+ if (size > preview_size) -+ size = preview_size; -+ memBufPrintf(&mb, "%x\r\n", size); -+ memBufAppend(&mb, icap->respmod.buffer.buf, size); -+ memBufAppend(&mb, crlf, 2); -+ icap->sc += size; -+ } -+ if (icap->respmod.buffer.size <= preview_size) { -+ /* content length is less than preview size+1 */ -+ if (icap->respmod.res_body_sz) -+ memBufAppend(&mb, "0; ieof\r\n\r\n", 11); -+ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ -+ } else { -+ char ch; -+ memBufAppend(&mb, "0\r\n\r\n", 5); -+ /* end of preview, wait for continue or 204 signal */ -+ /* copy the extra byte and all other data to the icap buffer */ -+ /* so that it can be handled next time */ -+ ch = icap->respmod.buffer.buf[preview_size]; -+ memBufReset(&icap->respmod.buffer); /* will now be used for other data */ -+ memBufAppend(&icap->respmod.buffer, &ch, 1); -+ debug(81, -+ 3) -+ ("icapSendRespMod: FD %d: sending preview and keeping %d bytes in internal buf.\n", -+ icap->icap_fd, len + 1); -+ if (len > 0) -+ memBufAppend(&icap->respmod.buffer, buf, len); -+ } -+ icap->flags.preview_done = 1; -+ icap->flags.wait_for_preview_reply = 1; -+ } -+ } else if (icap->flags.wait_for_preview_reply) { -+ /* received new data while waiting for preview response */ -+ /* add data to internal buffer and send later */ -+ debug(81, -+ 3) -+ ("icapSendRespMod: FD %d: add %d more bytes to internal buf while waiting for preview-response.\n", -+ icap->icap_fd, len); -+ if (len > 0) -+ memBufAppend(&icap->respmod.buffer, buf, len); -+ /* do not send any data now while waiting for preview response */ -+ /* but prepare for read more data on the HTTP connection */ -+ memBufClean(&mb); -+ return; -+ } else -+#endif -+ { -+ /* after preview completed and ICAP preview response received */ -+ /* there may still be some data in the buffer */ -+ if (icap->respmod.buffer.size > 0) { -+ memBufPrintf(&mb, "%x\r\n", icap->respmod.buffer.size); -+ memBufAppend(&mb, icap->respmod.buffer.buf, -+ icap->respmod.buffer.size); -+ memBufAppend(&mb, crlf, 2); -+ icap->sc += icap->respmod.buffer.size; -+ memBufReset(&icap->respmod.buffer); -+ } -+ if (len > 0) { -+ memBufPrintf(&mb, "%x\r\n", len); -+ memBufAppend(&mb, buf, len); -+ memBufAppend(&mb, crlf, 2); -+ icap->sc += len; -+ } -+ if (icap->flags.send_zero_chunk) { -+ /* send zero end chunk */ -+ icap->flags.send_zero_chunk = 0; -+ icap->flags.http_server_eof = 1; -+ memBufAppend(&mb, "0\r\n\r\n", 5); -+ } -+ /* wait for data coming from ICAP server as soon as we sent something */ -+ /* but of course only until we got the response header */ -+ if (!icap->flags.got_reply) -+ icap->flags.wait_for_reply = 1; -+ } -+ commSetTimeout(icap->icap_fd, -1, NULL, NULL); -+ -+ if (!mb.size) { -+ memBufClean(&mb); -+ return; -+ } -+ debug(81, 5) ("icapSendRespMod: FD %d writing {%s}\n", icap->icap_fd, -+ mb.buf); -+ icap->flags.write_pending = 1; -+ comm_write_mbuf(icap->icap_fd, mb, icapSendRespModDone, icap); -+} -+ -+static void -+icapRespModReadReply(int fd, void *data) -+{ -+ IcapStateData *icap = data; -+ int version_major, version_minor; -+ const char *str_status; -+ int x; -+ int status = 0; -+ int isIcap = 0; -+ int directResponse = 0; -+ ErrorState *err; -+ const char *start; -+ const char *end; -+ -+ debug(81, 5) ("icapRespModReadReply: FD %d data = %p\n", fd, data); -+ statCounter.syscalls.sock.reads++; -+ -+ x = icapReadHeader(fd, icap, &isIcap); -+ if (x < 0) { -+ /* Did not find a proper ICAP response */ -+ debug(81, 3) ("ICAP : Error path!\n"); -+ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); -+ err->xerrno = errno; -+ errorAppendEntry(icap->respmod.entry, err); -+ comm_close(fd); -+ return; -+ } -+ if (x == 0) { -+ /* -+ * Waiting for more headers. Schedule new read hander, but -+ * don't reset timeout. -+ */ -+ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); -+ return; -+ } -+ /* -+ * Parse the ICAP header -+ */ -+ assert(icap->icap_hdr.size); -+ debug(81, 3) ("Parse icap header : <%s>\n", icap->icap_hdr.buf); -+ if ((status = -+ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, -+ &version_major, &version_minor, &str_status)) < 0) { -+ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); -+ /* is this correct in case of ICAP protocol error? */ -+ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); -+ err->xerrno = errno; -+ errorAppendEntry(icap->respmod.entry, err); -+ comm_close(fd); -+ return; -+ }; -+ /* OK here we have responce. Lets stop filling the -+ * icap->respmod.resp_copy buffer .... -+ */ -+ icap->flags.copy_response = 0; -+ -+ icapSetKeepAlive(icap, icap->icap_hdr.buf); -+#if ICAP_PREVIEW -+ if (icap->flags.wait_for_preview_reply) { -+ if (100 == status) { -+ debug(81, 5) ("icapRespModReadReply: 100 Continue received\n"); -+ icap->flags.wait_for_preview_reply = 0; -+ /* if http_server_eof -+ * call again icapSendRespMod to handle data that -+ * was received while waiting for this ICAP response -+ * else let http to call icapSendRespMod when new data arrived -+ */ -+ if (icap->flags.http_server_eof) -+ icapSendRespMod(icap, NULL, 0, 0); -+ /* -+ * reset the header to send the rest of the preview -+ */ -+ if (!memBufIsNull(&icap->icap_hdr)) -+ memBufReset(&icap->icap_hdr); -+ -+ /*We do n't need it any more ....... */ -+ if (!memBufIsNull(&icap->respmod.resp_copy)) -+ memBufClean(&icap->respmod.resp_copy); -+ -+ return; -+ } -+ if (204 == status) { -+ debug(81, -+ 5) ("icapRespModReadReply: 204 No modification received\n"); -+ icap->flags.wait_for_preview_reply = 0; -+ } -+ } -+#endif /*ICAP_PREVIEW */ -+ -+#if SUPPORT_ICAP_204 || ICAP_PREVIEW -+ if (204 == status) { -+ debug(81, 3) ("got 204 status from ICAP server\n"); -+ icapRespModKeepAliveOrClose(icap); -+ -+ debug(81, 3) ("setting icap->flags.no_content\n"); -+ icap->flags.no_content = 1; -+ /* -+ * copy the response already written to the ICAP server -+ */ -+ debug(81, 3) ("copying %d bytes from resp_copy to chunk_buf\n", -+ icap->respmod.resp_copy.size); -+ memBufAppend(&icap->chunk_buf, -+ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); -+ icap->respmod.resp_copy.size = 0; -+ if (icapReadReply2(icap) < 0) -+ icapStateFree(-1, icap); -+ -+ /* -+ * XXX ideally want to clean icap->respmod.resp_copy here -+ * XXX ideally want to "close" ICAP server connection here -+ * OK do it.... -+ */ -+ if (!memBufIsNull(&icap->respmod.resp_copy)) -+ memBufClean(&icap->respmod.resp_copy); -+ return; -+ } -+#endif -+ if (200 != status && 201 != status) { -+ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); -+ /* Did not find a proper ICAP response */ -+ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); -+ err->xerrno = errno; -+ errorAppendEntry(icap->respmod.entry, err); -+ comm_close(fd); -+ return; -+ } -+ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { -+ icapParseEncapsulated(icap, start, end); -+ } else { -+ debug(81, -+ 1) -+ ("WARNING: icapRespModReadReply() did not find 'Encapsulated' header\n"); -+ } -+ if (icap->enc.res_hdr > -1) -+ directResponse = 1; -+ else if (icap->enc.res_body > -1) -+ directResponse = 1; -+ else -+ directResponse = 0; -+ -+ /* -+ * "directResponse" is the normal case here. If we don't have -+ * a response header or body, it is an error. -+ */ -+ if (!directResponse) { -+ /* Did not find a proper ICAP response */ -+ debug(81, 3) ("ICAP : Error path!\n"); -+ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); -+ err->xerrno = errno; -+ errorAppendEntry(icap->respmod.entry, err); -+ comm_close(fd); -+ return; -+ } -+ /* got the reply, no need to come here again */ -+ icap->flags.wait_for_reply = 0; -+ icap->flags.got_reply = 1; -+ /* Next, gobble any data before the HTTP response starts */ -+ if (icap->enc.res_hdr > -1) -+ icap->bytes_to_gobble = icap->enc.res_hdr; -+ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); -+} -+ -+ -+/* -+ * Gobble up (read) some bytes until we get to the start of the body -+ */ -+static void -+icapRespModGobble(int fd, void *data) -+{ -+ IcapStateData *icap = data; -+ int len; -+ LOCAL_ARRAY(char, junk, SQUID_TCP_SO_RCVBUF); -+ debug(81, 3) ("icapRespModGobble: FD %d gobbling %d bytes\n", fd, -+ icap->bytes_to_gobble); -+ len = FD_READ_METHOD(fd, junk, icap->bytes_to_gobble); -+ debug(81, 3) ("icapRespModGobble: gobbled %d bytes\n", len); -+ if (len < 0) { -+ /* XXX error */ -+ abort(); -+ } -+ icap->bytes_to_gobble -= len; -+ if (icap->bytes_to_gobble) -+ commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); -+ else -+ icapReadReply(fd, icap); -+} -+ -+ -+static void -+icapSendRespModDone(int fd, char *bufnotused, size_t size, int errflag, -+ void *data) -+{ -+ IcapStateData *icap = data; -+ ErrorState *err; -+ -+ icap->flags.write_pending = 0; -+ debug(81, 5) ("icapSendRespModDone: 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 == COMM_ERR_CLOSING) -+ return; -+ if (errflag) { -+ if (cbdataValid(icap)) -+ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); -+ else -+ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, NULL); -+ err->xerrno = errno; -+ storeEntryReset(icap->respmod.entry); -+ errorAppendEntry(icap->respmod.entry, err); -+ comm_close(fd); -+ return; -+ } -+ if (EBIT_TEST(icap->respmod.entry->flags, ENTRY_ABORTED)) { -+ debug(81, 3) ("icapSendRespModDone: Entry Aborded\n"); -+ comm_close(fd); -+ return; -+ } -+ if (icap->flags.send_zero_chunk) { -+ debug(81, -+ 3) ("icapSendRespModDone: I'm supposed to send zero chunk now\n"); -+ icap->flags.send_zero_chunk = 0; -+ icapSendRespMod(icap, NULL, 0, 1); -+ return; -+ } -+ if (icap->flags.wait_for_preview_reply || icap->flags.wait_for_reply) { -+ /* Schedule reading the ICAP response */ -+ debug(81, -+ 3) -+ ("icapSendRespModDone: FD %d: commSetSelect on read icapRespModReadReply.\n", -+ fd); -+ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); -+#if 1 -+ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); -+ commSetDefer(fd, fwdCheckDeferRead, icap->respmod.entry); -+#else -+ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { -+ /* -+ * Set the read timeout only after all data has been sent -+ * or we are waiting for a preview response -+ * If the ICAP server does not return any data till all data -+ * has been sent, we are likely to hit the timeout for large -+ * HTTP bodies -+ */ -+ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); -+ } -+#endif -+ } -+} -+ -+void -+icapConnectOver(int fd, int status, void *data) -+{ -+ ErrorState *err; -+ IcapStateData *icap = data; -+ debug(81, 3) ("icapConnectOver: FD %d, status=%d\n", fd, status); -+ icap->flags.connect_pending = 0; -+ if (status < 0) { -+ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); -+ err->xerrno = errno; -+ errorAppendEntry(icap->respmod.entry, err); -+ comm_close(fd); -+ debug(81, 3) ("icapConnectOver: status < 0, unreachable=1\n"); -+ icapOptSetUnreachable(icap->current_service); -+ return; -+ } -+ fd_table[fd].pconn.uses++; -+ fd_table[fd].pconn.type = 2; -+ commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); -+} -+ -+ -+ -+IcapStateData * -+icapRespModStart(icap_service_t type, request_t * request, StoreEntry * entry, -+ http_state_flags http_flags) -+{ -+ IcapStateData *icap = NULL; -+ CNCB *theCallback = NULL; -+ icap_service *service = NULL; -+ -+ debug(81, 3) ("icapRespModStart: type=%d\n", (int) type); -+ assert(type >= 0 && type < ICAP_SERVICE_MAX); -+ -+ service = icapService(type, request); -+ if (!service) { -+ debug(81, 3) ("icapRespModStart: no service found\n"); -+ return NULL; /* no service found */ -+ } -+ if (service->unreachable) { -+ if (service->bypass) { -+ debug(81, -+ 5) -+ ("icapRespModStart: BYPASS because service unreachable: %s\n", -+ service->uri); -+ return NULL; -+ } else { -+ debug(81, -+ 5) -+ ("icapRespModStart: ERROR because service unreachable: %s\n", -+ service->uri); -+ return (IcapStateData *) - 1; -+ } -+ } -+ switch (type) { -+ /* TODO: When we support more than ICAP_SERVICE_RESPMOD_PRECACHE, we needs to change -+ * this switch, because callbacks isn't keep */ -+ case ICAP_SERVICE_RESPMOD_PRECACHE: -+ theCallback = icapConnectOver; -+ break; -+ default: -+ fatalf("icapRespModStart: unsupported service type '%s'\n", -+ icap_service_type_str[type]); -+ break; -+ } -+ -+ icap = icapAllocate(); -+ if (!icap) { -+ debug(81, 3) ("icapRespModStart: icapAllocate() failed\n"); -+ return NULL; -+ } -+ icap->request = requestLink(request); -+ icap->respmod.entry = entry; -+ if (entry) -+ storeLockObject(entry); -+ icap->http_flags = http_flags; -+ memBufDefInit(&icap->respmod.buffer); -+ memBufDefInit(&icap->chunk_buf); -+ -+ icap->current_service = service; -+ icap->preview_size = service->preview; -+ -+ /* -+ * Don't create socket to the icap server now, but only for the first -+ * packet receive from the http server. This will resolve all timeout -+ * between the web server and icap server. -+ */ -+ debug(81, 3) ("icapRespModStart: setting connect_requested to 0\n"); -+ icap->flags.connect_requested = 0; -+ -+ /* -+ * make a copy the HTTP response that we send to the ICAP server in -+ * case it turns out to be a 204 -+ */ -+#ifdef SUPPORT_ICAP_204 -+ icap->flags.copy_response = 1; -+#elif ICAP_PREVIEW -+ if (preview_size < 0 || !Config.icapcfg.preview_enable) -+ icap->flags.copy_response = 0; -+ else -+ icap->flags.copy_response = 1; -+#else -+ icap->flags.copy_response = 0; -+#endif -+ -+ statCounter.icap.all.requests++; -+ debug(81, 3) ("icapRespModStart: returning %p\n", icap); -+ return icap; -+} -+ -+static int -+icapHttpReplyHdrState(IcapStateData * icap) -+{ -+ assert(icap); -+ if (NULL == icap->httpState) -+ return 0; -+ return icap->httpState->reply_hdr_state; -+} -+ -+static void -+icapProcessHttpReplyHeader(IcapStateData * icap, const char *buf, int size) -+{ -+ if (NULL == icap->httpState) { -+ icap->httpState = cbdataAlloc(HttpStateData); -+ icap->httpState->request = requestLink(icap->request); -+ icap->httpState->orig_request = requestLink(icap->request); -+ icap->httpState->entry = icap->respmod.entry; -+ storeLockObject(icap->httpState->entry); /* lock it */ -+ } -+ httpProcessReplyHeader(icap->httpState, buf, size); -+ if (2 == icap->httpState->reply_hdr_state) -+ EBIT_CLR(icap->httpState->entry->flags, ENTRY_FWD_HDR_WAIT); -+} -+ -+/* -+ * icapRespModKeepAliveOrClose -+ * -+ * Called when we are done reading from the ICAP server. -+ * Either close the connection or keep it open for a future -+ * transaction. -+ */ -+static void -+icapRespModKeepAliveOrClose(IcapStateData * icap) -+{ -+ int fd = icap->icap_fd; -+ if (fd < 0) -+ return; -+ debug(81, 3) ("%s:%d FD %d looks good, keeping alive\n", __FILE__, __LINE__, -+ fd); -+ commSetDefer(fd, NULL, NULL); -+ commSetTimeout(fd, -1, NULL, NULL); -+ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); -+ comm_remove_close_handler(fd, icapStateFree, icap); -+ icap->icap_fd = -1; -+ if (!icap->flags.keep_alive) { -+ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, -+ __LINE__); -+ comm_close(fd); -+ return; -+ } else { -+ pconnPush(fd, icap->current_service->hostname, icap->current_service->port, NULL, NULL, 0); -+ } -+} -+ -+ -+ -+/* -+ * copied from httpPconnTransferDone -+ * -+ */ -+static int -+icapPconnTransferDone(int fd, IcapStateData * icap) -+{ -+ debug(81, 3) ("icapPconnTransferDone: FD %d\n", fd); -+ /* -+ * Be careful with 204 responses. Normally we are done when we -+ * see the zero-end chunk, but that won't happen for 204s, so we -+ * use an EOF indicator on the HTTP side instead. -+ */ -+ if (icap->flags.no_content && icap->flags.http_server_eof) { -+ debug(81, 5) ("icapPconnTransferDone: no content, ret 1\n"); -+ return 1; -+ } -+ if (icapHttpReplyHdrState(icap) != 2) { -+ debug(81, -+ 5) ("icapPconnTransferDone: didn't see end of HTTP hdrs, ret 0\n"); -+ return 0; -+ } -+ if (icap->enc.null_body > -1) { -+ debug(81, 5) ("icapPconnTransferDone: no message body, ret 1\n"); -+ return 1; -+ } -+ if (icap->chunk_size == -2) { //AI: was != -2 ; and change content with bottom -+ /* zero end chunk reached */ -+ debug(81, 5) ("icapPconnTransferDone: got zero end chunk\n"); -+ return 1; -+ } -+ debug(81, 5) ("icapPconnTransferDone: didnt get zero end chunk yet\n"); //AI: change with second top condition -+ -+ return 0; -+} -+ -+static int -+icapExpectedHttpReplyHdrSize(IcapStateData * icap) -+{ -+ if (icap->enc.res_body > -1 && icap->enc.res_hdr > -1) -+ return (icap->enc.res_body - icap->enc.res_hdr); -+ if (icap->enc.null_body > -1 && icap->enc.res_hdr > -1) -+ return icap->enc.null_body - icap->enc.res_hdr; -+ /*The case we did not get res_hdr ..... */ -+ if (icap->enc.res_body > -1) -+ return icap->enc.res_body; -+ if (icap->enc.null_body > -1) -+ return icap->enc.null_body; -+ return -1; -+} -+ -+/* -+ * copied from httpReadReply() -+ * -+ * by the time this is called, the ICAP headers have already -+ * been read. -+ */ -+void -+icapReadReply(int fd, void *data) -+{ -+ IcapStateData *icap = data; -+ StoreEntry *entry = icap->respmod.entry; -+ const request_t *request = icap->request; -+ int len; -+ debug(81, 5) ("icapReadReply: FD %d: icap %p.\n", fd, data); -+ if (icap->flags.no_content && !icap->flags.http_server_eof) { //AI -+ -+ return; -+ } -+ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { -+ comm_close(fd); -+ return; -+ } -+ errno = 0; -+ statCounter.syscalls.sock.reads++; -+ len = memBufRead(fd, &icap->chunk_buf); -+ debug(81, 5) ("icapReadReply: FD %d: len %d.\n", fd, len); -+ if (len > 0) { -+ fd_bytes(fd, len, FD_READ); -+ kb_incr(&statCounter.icap.all.kbytes_in, len); -+ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); -+ if (icap->chunk_buf.size < icap->chunk_buf.capacity) { -+ *(icap->chunk_buf.buf + icap->chunk_buf.size) = '\0'; -+ debug(81, 9) ("{%s}\n", icap->chunk_buf.buf); -+ } -+ } -+ if (len <= 0) { -+ debug(81, 2) ("icapReadReply: FD %d: read failure: %s.\n", -+ fd, xstrerror()); -+ if (ignoreErrno(errno)) { -+ debug(81, 2) ("icapReadReply: FD %d: ignored errno\n", fd); -+ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); -+ } else if (entry->mem_obj->inmem_hi == 0) { -+ ErrorState *err; -+ debug(81, 2) ("icapReadReply: FD %d: generating error page\n", fd); -+ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, (request_t *)request); -+ err->xerrno = errno; -+ errorAppendEntry(entry, err); -+ comm_close(fd); -+ } else { -+ debug(81, 2) ("icapReadReply: FD %d: just calling comm_close()\n", -+ fd); -+ comm_close(fd); -+ } -+ return; -+ } -+ if (icapReadReply2(icap) < 0) -+ comm_close(fd); -+} -+ -+static int -+icapReadReply2(IcapStateData * icap) -+{ -+ StoreEntry *entry = icap->respmod.entry; -+ const request_t *request = icap->request; -+ debug(81, 3) ("icapReadReply2\n"); -+ if (icap->chunk_buf.size == 0 && entry->mem_obj->inmem_hi == 0) { -+ ErrorState *err; -+ err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE, (request_t *)request); -+ err->xerrno = errno; -+ errorAppendEntry(entry, err); -+ icap->flags.http_server_eof = 1; -+ return -1; -+ } -+ if (icap->chunk_buf.size == 0) { -+ /* Retrieval done. */ -+ if (icapHttpReplyHdrState(icap) < 2) -+ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, -+ icap->chunk_buf.size); -+ icap->flags.http_server_eof = 1; -+ icapReadReply3(icap); -+ return 0; -+ } -+ if (icapHttpReplyHdrState(icap) == 0) { -+ int expect = icapExpectedHttpReplyHdrSize(icap); -+ int so_far = icap->http_header_bytes_read_so_far; -+ int needed = expect - so_far; -+ debug(81, 3) ("expect=%d\n", expect); -+ debug(81, 3) ("so_far=%d\n", so_far); -+ debug(81, 3) ("needed=%d\n", needed); -+ assert(needed < 0 || needed >= 0); -+ if (0 > expect) { -+ icapProcessHttpReplyHeader(icap, -+ icap->chunk_buf.buf, icap->chunk_buf.size); -+ } else if (0 == expect) { -+ /* -+ * this icap reply doesn't give us new HTTP headers -+ * so we must copy them from our copy -+ */ -+ debug(81, 1) ("WARNING: untested code at %s:%d\n", __FILE__, -+ __LINE__); -+ if (icap->respmod.req_hdr_copy.size) { /*For HTTP 0.9 we do not have headers */ -+ storeAppend(entry, -+ icap->respmod.req_hdr_copy.buf, -+ icap->respmod.req_hdr_copy.size); -+ } -+ icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, -+ icap->chunk_buf.size); -+ assert(icapHttpReplyHdrState(icap) == 2); -+ icap->chunk_size = 0; /*we are ready to read chunks of data now.... */ -+ } else if (needed) { -+ icapProcessHttpReplyHeader(icap, -+ icap->chunk_buf.buf, icap->chunk_buf.size); -+ if (icap->chunk_buf.size >= needed) { -+ storeAppend(entry, icap->chunk_buf.buf, needed); -+ so_far += needed; -+ xmemmove(icap->chunk_buf.buf, -+ icap->chunk_buf.buf + needed, -+ icap->chunk_buf.size - needed); -+ icap->chunk_buf.size -= needed; -+ assert(icapHttpReplyHdrState(icap) == 2); -+ icap->chunk_size = 0; -+ } else { -+ /* -+ * We don't have the full HTTP reply headers yet, so keep -+ * the partial reply buffered in 'chunk_buf' and wait -+ * for more. -+ */ -+ debug(81, 3) ("We don't have full Http headers.Schedule a new read\n"); -+ commSetSelect(icap->icap_fd, COMM_SELECT_READ, icapReadReply, icap, 0); -+ } -+ } -+ icap->http_header_bytes_read_so_far = so_far; -+ } -+ debug(81, 3) ("%s:%d: icap->chunk_buf.size=%d\n", __FILE__, __LINE__, -+ (int) icap->chunk_buf.size); -+ debug(81, 3) ("%s:%d: flags.no_content=%d\n", __FILE__, __LINE__, -+ icap->flags.no_content); -+ if (icap->flags.no_content) { -+ /* data from http.c is not chunked */ -+ if (!EBIT_TEST(entry->flags, ENTRY_ABORTED)) { -+ debug(81, 3) ("copying %d bytes from chunk_buf to entry\n", -+ icap->chunk_buf.size); -+ storeAppend(entry, icap->chunk_buf.buf, icap->chunk_buf.size); -+ icap->chunk_buf.size = 0; -+ } -+ } else if (2 == icapHttpReplyHdrState(icap)) { -+ if (icap->chunk_buf.size) -+ icapParseChunkedBody(icap, (STRCB *) storeAppend, entry); -+ } -+ icapReadReply3(icap); -+ return 0; -+} -+ -+static void -+icapReadReply3(IcapStateData * icap) -+{ -+ StoreEntry *entry = icap->respmod.entry; -+ int fd = icap->icap_fd; -+ debug(81, 3) ("icapReadReply3\n"); -+ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { -+ debug(81, 3) ("icapReadReply3: Entry Aborded\n"); -+ if (icap->flags.no_content) -+ icapStateFree(-1, icap); -+ else -+ comm_close(fd); -+ } else if (icapPconnTransferDone(fd, icap)) { -+ storeComplete(entry); -+ if (icap->flags.no_content) -+ icapStateFree(-1, icap); -+ else { -+ icapRespModKeepAliveOrClose(icap); -+ icapStateFree(-1, icap); -+ } -+ } else if (!icap->flags.no_content) { -+ /* Wait for EOF condition */ -+ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); -+ debug(81, -+ 3) -+ ("icapReadReply3: Going to read mode data throught icapReadReply\n"); -+ } else { -+ debug(81, 3) ("icapReadReply3: Nothing\n"); -+ } -+} -Index: squid/src/main.c -diff -u squid/src/main.c:1.72 squid/src/main.c:1.45.4.10 ---- squid/src/main.c:1.72 Mon Oct 23 04:52:55 2006 -+++ squid/src/main.c Fri Nov 3 10:47:14 2006 -@@ -391,6 +391,9 @@ - #else - idnsShutdown(); - #endif -+#ifdef HS_FEAT_ICAP -+ icapClose(); -+#endif - redirectShutdown(); - locationRewriteShutdown(); - authenticateShutdown(); -@@ -422,6 +425,9 @@ - #endif - redirectInit(); - locationRewriteInit(); -+#ifdef HS_FEAT_ICAP -+ icapInit(); -+#endif - authenticateInit(&Config.authConfig); - externalAclInit(); - #if USE_WCCP -@@ -573,6 +579,9 @@ - redirectInit(); - locationRewriteInit(); - errorMapInit(); -+#ifdef HS_FEAT_ICAP -+ icapInit(); -+#endif - authenticateInit(&Config.authConfig); - externalAclInit(); - useragentOpenLog(); -Index: squid/src/mem.c -diff -u squid/src/mem.c:1.27 squid/src/mem.c:1.24.4.3 ---- squid/src/mem.c:1.27 Sat May 20 15:50:55 2006 -+++ squid/src/mem.c Fri May 26 11:21:32 2006 -@@ -353,6 +353,13 @@ - memDataInit(MEM_TLV, "storeSwapTLV", sizeof(tlv), 0); - memDataInit(MEM_SWAP_LOG_DATA, "storeSwapLogData", sizeof(storeSwapLogData), 0); - -+#ifdef HS_FEAT_ICAP -+ memDataInit(MEM_ICAP_OPT_DATA, "IcapOptData", sizeof(IcapOptData), 0); -+ memDataInit(MEM_ICAP_SERVICE_LIST, "icap_service_list", sizeof(icap_service_list), 0); -+ memDataInit(MEM_ICAP_CLASS, "icap_class", sizeof(icap_class), 0); -+ memDataInit(MEM_ICAP_ACCESS, "icap_access", sizeof(icap_access), 0); -+#endif -+ - /* init string pools */ - for (i = 0; i < mem_str_pool_count; i++) { - StrPools[i].pool = memPoolCreate(StrPoolsAttrs[i].name, StrPoolsAttrs[i].obj_size); -Index: squid/src/mk-string-arrays.pl -diff -u squid/src/mk-string-arrays.pl:1.2 squid/src/mk-string-arrays.pl:1.2.180.1 ---- squid/src/mk-string-arrays.pl:1.2 Mon Oct 23 08:04:21 2000 -+++ squid/src/mk-string-arrays.pl Wed May 17 10:58:01 2006 -@@ -16,6 +16,7 @@ - $pat{'icp_opcode'} = "icp_opcode_str"; - $pat{'swap_log_op'} = "swap_log_op_str"; - $pat{'lookup_t'} = "lookup_t_str"; -+$pat{'icap_service_t'} = "icap_service_type_str"; - - $state = 0; # start state - while (<>) { -Index: squid/src/pconn.c -diff -u squid/src/pconn.c:1.10 squid/src/pconn.c:1.9.4.2 ---- squid/src/pconn.c:1.10 Mon May 22 15:06:12 2006 -+++ squid/src/pconn.c Fri May 26 11:21:32 2006 -@@ -46,6 +46,9 @@ - #define PCONN_HIST_SZ (1<<16) - int client_pconn_hist[PCONN_HIST_SZ]; - int server_pconn_hist[PCONN_HIST_SZ]; -+#ifdef HS_FEAT_ICAP -+int icap_server_pconn_hist[PCONN_HIST_SZ]; -+#endif - - static PF pconnRead; - static PF pconnTimeout; -@@ -169,6 +172,20 @@ - continue; - storeAppendPrintf(e, "\t%4d %9d\n", i, server_pconn_hist[i]); - } -+#ifdef HS_FEAT_ICAP -+ storeAppendPrintf(e, -+ "\n" -+ "ICAP-server persistent connection counts:\n" -+ "\n" -+ "\treq/\n" -+ "\tconn count\n" -+ "\t---- ---------\n"); -+ for (i = 0; i < PCONN_HIST_SZ; i++) { -+ if (icap_server_pconn_hist[i] == 0) -+ continue; -+ storeAppendPrintf(e, "\t%4d %9d\n", i, icap_server_pconn_hist[i]); -+ } -+#endif - } - - /* ========== PUBLIC FUNCTIONS ============================================ */ -@@ -183,6 +200,9 @@ - for (i = 0; i < PCONN_HIST_SZ; i++) { - client_pconn_hist[i] = 0; - server_pconn_hist[i] = 0; -+#ifdef HS_FEAT_ICAP -+ icap_server_pconn_hist[i] = 0; -+#endif - } - pconn_data_pool = memPoolCreate("pconn_data", sizeof(struct _pconn)); - pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int)); -@@ -265,11 +285,15 @@ - { - if (i >= PCONN_HIST_SZ) - i = PCONN_HIST_SZ - 1; -- /* what == 0 for client, 1 for server */ -+ /* what == 0 for client, 1 for server, 2 for ICAP server */ - if (what == 0) - client_pconn_hist[i]++; - else if (what == 1) - server_pconn_hist[i]++; -+#ifdef HS_FEAT_ICAP -+ else if (what == 2) -+ icap_server_pconn_hist[i]++; -+#endif - else - assert(0); - } -Index: squid/src/protos.h -diff -u squid/src/protos.h:1.129 squid/src/protos.h:1.74.4.11 ---- squid/src/protos.h:1.129 Mon Oct 23 04:52:55 2006 -+++ squid/src/protos.h Fri Nov 3 10:47:14 2006 -@@ -302,6 +302,8 @@ - /* http.c */ - extern int httpCachable(method_t); - extern void httpStart(FwdState *); -+extern void httpParseReplyHeaders(const char *, http_reply *); -+extern void httpProcessReplyHeader(HttpStateData *, const char *, int); - extern int httpBuildRequestPrefix(request_t * request, - request_t * orig_request, - StoreEntry * entry, -@@ -624,6 +626,7 @@ - extern FREE *memBufFreeFunc(MemBuf * mb); - /* puts report on MemBuf _module_ usage into mb */ - extern void memBufReport(MemBuf * mb); -+extern int memBufRead(int fd, MemBuf * mb); - - extern char *mime_get_header(const char *mime, const char *header); - extern char *mime_get_header_field(const char *mime, const char *name, const char *prefix); -@@ -1417,4 +1420,53 @@ - void storeLocateVary(StoreEntry * e, int offset, const char *vary_data, String accept_encoding, STLVCB * callback, void *cbdata); - void storeAddVary(const char *url, const char *log_url, const method_t method, const cache_key * key, const char *etag, const char *vary, const char *vary_headers, const char *accept_encoding); - -+#ifdef HS_FEAT_ICAP -+/* -+ * icap_common.c -+ */ -+void icapInit(void); -+void icapClose(void); -+void icapParseEncapsulated(IcapStateData *, const char *, const char *); -+icap_service *icapService(icap_service_t, request_t *); -+int icapConnect(IcapStateData *, CNCB *); -+IcapStateData *icapAllocate(void); -+PF icapStateFree; -+PF icapConnectTimeout; -+PF icapReadTimeout; -+icap_service_t icapServiceToType(const char *); -+const char *icapServiceToStr(const icap_service_t); -+int icapCheckAcl(clientHttpRequest *); -+size_t icapLineLength(const char *, int); -+int icapReadHeader(int, IcapStateData *, int *); -+int icapFindHeader(const char *, const char *, const char **, const char **); -+int icapParseKeepAlive(const IcapStateData *, const char *, const char *); -+void icapSetKeepAlive(IcapStateData * icap, const char *hdrs); -+size_t icapParseChunkedBody(IcapStateData *, STRCB *, void *); -+void icapAddAuthUserHeader(MemBuf *, auth_user_request_t *); -+int icapParseStatusLine(const char *, int, int *, int *, const char **); -+ -+/* -+ * icap_respmod.c -+ */ -+IcapStateData *icapRespModStart(icap_service_t, request_t *, StoreEntry *, http_state_flags); -+void icapSendRespMod(IcapStateData *, char *, int, int); -+CNCB icapConnectOver; -+ -+/* -+ * icap_reqmod.c -+ */ -+IcapStateData *icapReqModStart(icap_service*, const char *, request_t *, int, struct timeval, struct in_addr, void *); -+ -+/* icap_opt.c */ -+void icapOptInit(void); -+void icapOptShutdown(void); -+void icapOptSetUnreachable(icap_service * s); -+ -+/* X-Server-IP support */ -+void icapAddOriginIP(MemBuf *, const char *); -+ -+/* for debugging purposes only */ -+void dump_icap_config(IcapConfig * cfg); -+#endif -+ - #endif /* SQUID_PROTOS_H */ -Index: squid/src/squid.h -diff -u squid/src/squid.h:1.36 squid/src/squid.h:1.24.8.7 ---- squid/src/squid.h:1.36 Fri Sep 8 12:50:59 2006 -+++ squid/src/squid.h Tue Sep 26 15:47:38 2006 -@@ -38,6 +38,14 @@ - #include "config.h" - - /* -+ * experimental defines for ICAP -+ */ -+#ifdef HS_FEAT_ICAP -+#define ICAP_PREVIEW 1 -+#define SUPPORT_ICAP_204 0 -+#endif -+ -+/* - * On some systems, FD_SETSIZE is set to something lower than the - * actual number of files which can be opened. IRIX is one case, - * NetBSD is another. So here we increase FD_SETSIZE to our -Index: squid/src/stat.c -diff -u squid/src/stat.c:1.38 squid/src/stat.c:1.26.8.10 ---- squid/src/stat.c:1.38 Wed Nov 1 13:51:29 2006 -+++ squid/src/stat.c Fri Nov 3 10:47:14 2006 -@@ -804,6 +804,17 @@ - storeAppendPrintf(sentry, "server.other.kbytes_out = %f/sec\n", - XAVG(server.other.kbytes_out.kb)); - -+#ifdef HS_FEAT_ICAP -+ storeAppendPrintf(sentry, "icap.all.requests = %f/sec\n", -+ XAVG(icap.all.requests)); -+ storeAppendPrintf(sentry, "icap.all.errors = %f/sec\n", -+ XAVG(icap.all.errors)); -+ storeAppendPrintf(sentry, "icap.all.kbytes_in = %f/sec\n", -+ XAVG(icap.all.kbytes_in.kb)); -+ storeAppendPrintf(sentry, "icap.all.kbytes_out = %f/sec\n", -+ XAVG(icap.all.kbytes_out.kb)); -+#endif -+ - storeAppendPrintf(sentry, "icp.pkts_sent = %f/sec\n", - XAVG(icp.pkts_sent)); - storeAppendPrintf(sentry, "icp.pkts_recv = %f/sec\n", -@@ -1188,6 +1199,17 @@ - storeAppendPrintf(sentry, "server.other.kbytes_out = %d\n", - (int) f->server.other.kbytes_out.kb); - -+#if HS_FEAT_ICAP -+ storeAppendPrintf(sentry, "icap.all.requests = %d\n", -+ (int) f->icap.all.requests); -+ storeAppendPrintf(sentry, "icap.all.errors = %d\n", -+ (int) f->icap.all.errors); -+ storeAppendPrintf(sentry, "icap.all.kbytes_in = %d\n", -+ (int) f->icap.all.kbytes_in.kb); -+ storeAppendPrintf(sentry, "icap.all.kbytes_out = %d\n", -+ (int) f->icap.all.kbytes_out.kb); -+#endif -+ - storeAppendPrintf(sentry, "icp.pkts_sent = %d\n", - f->icp.pkts_sent); - storeAppendPrintf(sentry, "icp.pkts_recv = %d\n", -@@ -1488,8 +1510,6 @@ - storeAppendPrintf(s, "\tme: %s:%d\n", - inet_ntoa(conn->me.sin_addr), - ntohs(conn->me.sin_port)); -- storeAppendPrintf(s, "\tnrequests: %d\n", -- conn->nrequests); - storeAppendPrintf(s, "\tdefer: n %d, until %ld\n", - conn->defer.n, (long int) conn->defer.until); - } -Index: squid/src/store.c -diff -u squid/src/store.c:1.38 squid/src/store.c:1.21.10.9 ---- squid/src/store.c:1.38 Mon Oct 9 06:52:39 2006 -+++ squid/src/store.c Fri Nov 3 10:47:14 2006 -@@ -1105,8 +1105,17 @@ - MemObject *mem = e->mem_obj; - assert(mem != NULL); - assert(len >= 0); -- assert(e->store_status == STORE_PENDING); - mem->refresh_timestamp = squid_curtime; -+ debug(20, 3) ("storeAppend: '%s'\n", storeKeyText(e->hash.key)); -+ if (e->store_status != STORE_PENDING) { -+ /* -+ * if we're not STORE_PENDING, then probably we got aborted -+ * and there should be NO clients on this entry -+ */ -+ assert(EBIT_TEST(e->flags, ENTRY_ABORTED)); -+ assert(e->mem_obj->nclients == 0); -+ return; -+ } - if (len) { - debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", - len, -Index: squid/src/structs.h -diff -u squid/src/structs.h:1.134 squid/src/structs.h:1.81.4.11 ---- squid/src/structs.h:1.134 Mon Oct 23 04:52:56 2006 -+++ squid/src/structs.h Fri Nov 3 10:47:14 2006 -@@ -423,6 +423,23 @@ - wordlist *args; - }; - -+#if HS_FEAT_ICAP -+struct _IcapConfig { -+ int onoff; -+ int preview_enable; -+ icap_service *service_head; -+ icap_class *class_head; -+ icap_access *access_head; -+ int preview_size; -+ int check_interval; -+ int send_client_ip; -+ int send_server_ip; -+ int send_auth_user; -+ char *auth_scheme; -+}; -+ -+#endif /* HS_FEAT_ICAP */ -+ - struct _SquidConfig { - struct { - squid_off_t maxSize; -@@ -810,6 +827,9 @@ - #endif - time_t refresh_stale_window; - int umask; -+#ifdef HS_FEAT_ICAP -+ IcapConfig icapcfg; -+#endif - }; - - struct _SquidConfig2 { -@@ -891,6 +911,10 @@ - comm_pending write_pending; - squid_off_t bytes_read; - squid_off_t bytes_written; -+ struct { -+ int uses; -+ int type; -+ } pconn; - int uses; /* ie # req's over persistent conn */ - struct _fde_disk { - DWCB *wrt_handle; -@@ -1095,6 +1119,131 @@ - unsigned int originpeer:1; - }; - -+#ifdef HS_FEAT_ICAP -+struct _IcapStateData { -+ request_t *request; -+ http_state_flags http_flags; -+ HttpStateData *httpState; /* needed to parse HTTP headers only */ -+ int icap_fd; -+ int sc; -+ icap_service *current_service; -+ MemBuf icap_hdr; -+ struct { -+ int res_hdr; -+ int res_body; -+ int req_hdr; -+ int req_body; -+ int opt_body; -+ int null_body; -+ } enc; -+ int bytes_to_gobble; -+ int chunk_size; -+ MemBuf chunk_buf; -+ int preview_size; -+ squid_off_t fake_content_length; -+ int http_header_bytes_read_so_far; -+ struct { -+ const char *uri; /* URI for REQMODs */ -+ int client_fd; -+ struct timeval start; /* for logging */ -+ struct in_addr log_addr; /* for logging */ -+ int hdr_state; -+ MemBuf hdr_buf; -+ void *client_cookie; -+ struct { -+ MemBuf buf; -+ CBCB *callback; -+ void *callback_data; -+ char *callback_buf; -+ size_t callback_bufsize; -+ squid_off_t bytes_read; -+ } http_entity; -+ } reqmod; -+ struct { -+ StoreEntry *entry; -+ MemBuf buffer; -+ MemBuf req_hdr_copy; /* XXX barf */ -+ MemBuf resp_copy; /* XXX barf^max */ -+ squid_off_t res_body_sz; -+ } respmod; -+ struct { -+ unsigned int connect_requested:1; -+ unsigned int connect_pending:1; -+ unsigned int write_pending:1; -+ unsigned int keep_alive:1; -+ unsigned int http_server_eof:1; -+ unsigned int send_zero_chunk:1; -+ unsigned int got_reply:1; -+ unsigned int wait_for_reply:1; -+ unsigned int wait_for_preview_reply:1; -+ unsigned int preview_done:1; -+ unsigned int copy_response:1; -+ unsigned int no_content:1; -+ unsigned int reqmod_http_entity_eof:1; -+ } flags; -+}; -+ -+struct _icap_service { -+ icap_service *next; -+ char *name; /* name to be used when referencing ths service */ -+ char *uri; /* uri of server/service to use */ -+ char *type_name; /* {req|resp}mod_{pre|post}cache */ -+ -+ char *hostname; -+ unsigned short int port; -+ char *resource; -+ icap_service_t type; /* parsed type */ -+ icap_method_t method; -+ ushort bypass; /* flag: bypass allowed */ -+ ushort unreachable; /* flag: set to 1 if options request fails */ -+ IcapOptData *opt; /* temp data needed during opt request */ -+ struct { -+ unsigned int allow_204:1; -+ unsigned int need_x_client_ip:1; -+ unsigned int need_x_server_ip:1; -+ unsigned int need_x_authenticated_user:1; -+ } flags; -+ int preview; -+ String istag; -+ String transfer_preview; -+ String transfer_ignore; -+ String transfer_complete; -+ int max_connections; -+ int options_ttl; -+ int keep_alive; -+}; -+ -+struct _icap_service_list { -+ icap_service_list *next; -+ icap_service *services[16]; -+ int nservices; /* Number of services already used */ -+ int last_service_used; /* Last services used, use to do a round robin */ -+}; -+ -+struct _icap_class { -+ icap_class *next; -+ char *name; -+ wordlist *services; -+ icap_service_list *isl; -+ ushort hidden; /* for unnamed classes */ -+}; -+ -+struct _icap_access { -+ icap_access *next; -+ char *service_name; -+ icap_class *class; -+ acl_access *access; -+}; -+ -+struct _IcapOptData { -+ char *buf; -+ off_t offset; -+ size_t size; -+ off_t headlen; -+}; -+ -+#endif -+ - struct _HttpStateData { - StoreEntry *entry; - request_t *request; -@@ -1106,10 +1255,14 @@ - int fd; - http_state_flags flags; - FwdState *fwd; -+#ifdef HS_FEAT_ICAP -+ struct _IcapStateData *icap_writer; -+#endif - char *body_buf; - int body_buf_sz; - }; - -+ - struct _icpUdpData { - struct sockaddr_in address; - void *msg; -@@ -1218,6 +1371,7 @@ - unsigned int internal:1; - unsigned int done_copying:1; - unsigned int purging:1; -+ unsigned int did_icap_reqmod:1; - unsigned int hit:1; - } flags; - struct { -@@ -1226,6 +1380,9 @@ - } redirect; - dlink_node active; - squid_off_t maxBodySize; -+#if HS_FEAT_ICAP -+ IcapStateData *icap_reqmod; -+#endif - }; - - struct _ConnStateData { -@@ -1894,6 +2051,9 @@ - unsigned int done_etag:1; /* We have done clientProcessETag on this, don't attempt it again */ - char *urlgroup; /* urlgroup, returned by redirectors */ - char *peer_domain; /* Configured peer forceddomain */ -+#if HS_FEAT_ICAP -+ icap_class *class; -+#endif - BODY_HANDLER *body_reader; - void *body_reader_data; - String extacl_log; /* String to be used for access.log purposes */ -@@ -2001,7 +2161,11 @@ - kb_t kbytes_in; - kb_t kbytes_out; - } all , http, ftp, other; -- } server; -+ } -+#if HS_FEAT_ICAP -+ icap, -+#endif -+ server; - struct { - int pkts_sent; - int queries_sent; -Index: squid/src/typedefs.h -diff -u squid/src/typedefs.h:1.41 squid/src/typedefs.h:1.32.4.8 ---- squid/src/typedefs.h:1.41 Sat Sep 2 07:17:45 2006 -+++ squid/src/typedefs.h Tue Sep 26 15:47:39 2006 -@@ -136,6 +136,15 @@ - typedef struct _HttpBody HttpBody; - typedef struct _HttpReply HttpReply; - typedef struct _HttpStateData HttpStateData; -+#ifdef HS_FEAT_ICAP -+typedef struct _IcapStateData IcapStateData; -+typedef struct _IcapConfig IcapConfig; -+typedef struct _icap_service icap_service; -+typedef struct _icap_service_list icap_service_list; -+typedef struct _icap_class icap_class; -+typedef struct _icap_access icap_access; -+typedef struct _IcapOptData IcapOptData; -+#endif - typedef struct _icpUdpData icpUdpData; - typedef struct _clientHttpRequest clientHttpRequest; - typedef struct _ConnStateData ConnStateData; -Index: squid/src/url.c -diff -u squid/src/url.c:1.17 squid/src/url.c:1.14.10.4 ---- squid/src/url.c:1.17 Sat Jun 17 16:51:19 2006 -+++ squid/src/url.c Wed Jun 28 14:12:01 2006 -@@ -103,6 +103,9 @@ - "whois", - "internal", - "https", -+#ifdef HS_FEAT_ICAP -+ "icap", -+#endif - "TOTAL" - }; - -@@ -217,6 +220,10 @@ - return PROTO_WHOIS; - if (strcasecmp(s, "internal") == 0) - return PROTO_INTERNAL; -+#ifdef HS_FEAT_ICAP -+ if (strcasecmp(s, "icap") == 0) -+ return PROTO_ICAP; -+#endif - return PROTO_NONE; - } - -@@ -240,6 +247,10 @@ - return CACHE_HTTP_PORT; - case PROTO_WHOIS: - return 43; -+#ifdef HS_FEAT_ICAP -+ case PROTO_ICAP: -+ return 1344; -+#endif - default: - return 0; - } diff -ruN squid-2.6.STABLE5/.metadata/.log squid-icap-2.6.STABLE5/.metadata/.log --- squid-2.6.STABLE5/.metadata/.log 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.log 2006-12-10 16:14:36.000000000 +0200 @@ -0,0 +1,56 @@ +!SESSION 2006-12-10 16:09:49.33 ------------------------------------------------ +eclipse.buildId=M20050929-0840 +java.version=1.5.0_06 +java.vendor=Sun Microsystems Inc. +BootLoader constants: OS=linux, ARCH=x86, WS=gtk, NL=en_US +Command-line arguments: -os linux -ws gtk -arch x86 + +!ENTRY org.eclipse.ui 1 0 2006-12-10 16:10:14.151 +!MESSAGE Warnings while parsing the commands from the 'org.eclipse.ui.commands' and 'org.eclipse.ui.actionDefinitions' extension points. +!SUBENTRY 1 org.eclipse.ui 1 0 2006-12-10 16:10:14.153 +!MESSAGE Commands should really have a category, not categoryId='': plug-in='org.epic.perleditor', commandId='org.epic.perleditor.uncomment'. +!SUBENTRY 1 org.eclipse.ui 1 0 2006-12-10 16:10:14.154 +!MESSAGE Commands should really have a category, not categoryId='': plug-in='org.epic.perleditor', commandId='org.epic.perleditor.comment'. +!SUBENTRY 1 org.eclipse.ui 1 0 2006-12-10 16:10:14.154 +!MESSAGE Commands should really have a category, not categoryId='': plug-in='org.epic.perleditor', commandId='org.epic.perleditor.contentassist'. +!SUBENTRY 1 org.eclipse.ui 1 0 2006-12-10 16:10:14.155 +!MESSAGE Commands should really have a category, not categoryId='': plug-in='org.epic.perleditor', commandId='org.epic.perleditor.htmlexport'. +!SUBENTRY 1 org.eclipse.ui 1 0 2006-12-10 16:10:14.155 +!MESSAGE Commands should really have a category, not categoryId='': plug-in='org.epic.perleditor', commandId='org.epic.perleditor.formatsource'. + +!ENTRY org.eclipse.ui 2 0 2006-12-10 16:10:14.228 +!MESSAGE Warnings while parsing the key bindings from the 'org.eclipse.ui.commands' extension point +!SUBENTRY 1 org.eclipse.ui 2 0 2006-12-10 16:10:14.229 +!MESSAGE Cannot bind to an undefined command: plug-in='org.epic.perleditor', commandId='org.epic.eclipse.perleditor.uncomment'. +!SUBENTRY 1 org.eclipse.ui 2 0 2006-12-10 16:10:14.229 +!MESSAGE Cannot bind to an undefined command: plug-in='org.epic.perleditor', commandId='org.epic.eclipse.perleditor.comment'. + +!ENTRY org.eclipse.ui 4 4 2006-12-10 16:14:36.664 +!MESSAGE Unhandled event loop exception + +!ENTRY org.eclipse.ui 4 0 2006-12-10 16:14:36.692 +!MESSAGE Failed to execute runnable (java.lang.OutOfMemoryError: Java heap space) +!STACK 0 +org.eclipse.swt.SWTException: Failed to execute runnable (java.lang.OutOfMemoryError: Java heap space) + at org.eclipse.swt.SWT.error(SWT.java:2942) + at org.eclipse.swt.SWT.error(SWT.java:2865) + at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:126) + at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:2844) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2575) + at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:1699) + at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:1663) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:367) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:143) + at org.eclipse.ui.internal.ide.IDEApplication.run(IDEApplication.java:103) + at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:226) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:376) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:163) + at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) + at java.lang.reflect.Method.invoke(Unknown Source) + at org.eclipse.core.launcher.Main.invokeFramework(Main.java:334) + at org.eclipse.core.launcher.Main.basicRun(Main.java:278) + at org.eclipse.core.launcher.Main.run(Main.java:973) + at org.eclipse.core.launcher.Main.main(Main.java:948) +Caused by: java.lang.OutOfMemoryError: Java heap space diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.cdt.make.core/specs.c squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.cdt.make.core/specs.c --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.cdt.make.core/specs.c 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.cdt.make.core/specs.c 2006-12-10 16:10:49.000000000 +0200 @@ -0,0 +1 @@ + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.cdt.make.core/specs.cpp squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.cdt.make.core/specs.cpp --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.cdt.make.core/specs.cpp 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.cdt.make.core/specs.cpp 2006-12-10 16:10:49.000000000 +0200 @@ -0,0 +1 @@ + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/0/c0b716325c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/0/c0b716325c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/0/c0b716325c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/0/c0b716325c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,43 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +ROOT := .. + +-include $(ROOT)/makefile.init + +RM := rm -rf + +# All of the sources participating in the build are defined here +-include sources.mk +-include $(SUBDIRS:%=%/subdir.mk) +-include objects.mk +ifneq ($(strip $(DEPS)),) +-include $(DEPS) +endif + +-include $(ROOT)/makefile.defs + +# Add inputs and outputs from these tool invocations to the build variables + +# All Target +all: src + +# Tool invocations +src: $(OBJS) $(USER_OBJS) + @echo 'Building target: $@' + @echo 'Invoking: GCC C Linker' + @echo gcc -osrc $(OBJS) $(USER_OBJS) $(LIBS) + @gcc -osrc $(OBJS) $(USER_OBJS) $(LIBS) + @echo 'Finished building target: $@' + @echo ' ' + +# Other Targets +clean: + -$(RM) $(OBJS)$(DEPS)$(EXECUTABLES) src + -@echo ' ' + +.PHONY: all clean dependents +.SECONDARY: + +-include $(ROOT)/makefile.targets diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/16/0065afda5f88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/16/0065afda5f88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/16/0065afda5f88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/16/0065afda5f88001b16add8656805b17d 2006-12-10 16:09:19.000000000 +0200 @@ -0,0 +1,1058 @@ + +/* + * $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. + * + */ + +#include "squid.h" + +static CWCB icapSendRespModDone; +static PF icapRespModGobble; +extern PF icapReadReply; +static PF icapRespModReadReply; +static void icapRespModKeepAliveOrClose(IcapStateData * icap); +static int icapReadReply2(IcapStateData * icap); +static void icapReadReply3(IcapStateData * icap); + +#define EXPECTED_ICAP_HEADER_LEN 256 +const char *crlf = "\r\n"; + +static void +getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, + const char *client_addr, IcapStateData * icap, const icap_service * service) +{ + memBufPrintf(mb, "RESPMOD %s ICAP/1.0\r\nEncapsulated:", service->uri); + if (o1 >= 0) + memBufPrintf(mb, " req-hdr=%1d", o1); + if (o2 >= 0) + memBufPrintf(mb, ", res-hdr=%1d", o2); + if (o3 >= 0) + memBufPrintf(mb, ", res-body=%1d", o3); + else + memBufPrintf(mb, ", null-body=%1d", -o3); + memBufPrintf(mb, crlf); + + if (service->flags.need_x_client_ip && Config.icapcfg.send_client_ip) { + memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); + } + if (service->flags.need_x_server_ip && Config.icapcfg.send_server_ip) + icapAddOriginIP(mb, icap->request->host); + + if ((service->flags.need_x_authenticated_user + && Config.icapcfg.send_auth_user) + && (icap->request->auth_user_request != NULL)) { + icapAddAuthUserHeader(mb, icap->request->auth_user_request); + } +#if NOT_YET_FINISHED + if (Config.icapcfg.trailers) { + memBufPrintf(mb, "X-TE: trailers\r\n"); + } +#endif +} + +static int +buildRespModHeader(MemBuf * mb, IcapStateData * icap, char *buf, + ssize_t len, int theEnd) +{ + MemBuf mb_hdr; + char *client_addr; + int o2 = 0; + int o3 = 0; + int hlen; + int consumed; + icap_service *service; + HttpReply *r; + + if (memBufIsNull(&icap->respmod.req_hdr_copy)) + memBufDefInit(&icap->respmod.req_hdr_copy); + + memBufAppend(&icap->respmod.req_hdr_copy, buf, len); + + if (icap->respmod.req_hdr_copy.size > 4 && strncmp(icap->respmod.req_hdr_copy.buf, "HTTP/", 5)) { + debug(81, 3) ("buildRespModHeader: Non-HTTP-compliant header: '%s'\n", buf); + /* + *Possible we can consider that we did not have http responce headers + *(maybe HTTP 0.9 protocol), lets returning -1... + */ + consumed = -1; + o2 = -1; + memBufDefInit(&mb_hdr); + httpBuildRequestPrefix(icap->request, icap->request, + icap->respmod.entry, &mb_hdr, icap->http_flags); + o3 = mb_hdr.size; + } else { + + hlen = headersEnd(icap->respmod.req_hdr_copy.buf, + icap->respmod.req_hdr_copy.size); + debug(81, 3) ("buildRespModHeader: headersEnd = %d(%s)\n", hlen, buf); + if (0 == hlen) + return 0; + + /* + * calc how many bytes from this 'buf' went towards the + * reply header. + */ + consumed = hlen - (icap->respmod.req_hdr_copy.size - len); + debug(81, 3) ("buildRespModHeader: consumed = %d\n", consumed); + + + /* + * now, truncate our req_hdr_copy at the header end. + * this 'if' statement might be unncessary? + */ + if (hlen < icap->respmod.req_hdr_copy.size) + icap->respmod.req_hdr_copy.size = hlen; + + /* Copy request header */ + memBufDefInit(&mb_hdr); + httpBuildRequestPrefix(icap->request, icap->request, + icap->respmod.entry, &mb_hdr, icap->http_flags); + o2 = mb_hdr.size; + + /* Copy response header - Append to request header mbuffer */ + memBufAppend(&mb_hdr, + icap->respmod.req_hdr_copy.buf, icap->respmod.req_hdr_copy.size); + o3 = mb_hdr.size; + } + + service = icap->current_service; + assert(service); + client_addr = inet_ntoa(icap->request->client_addr); + + r = httpReplyCreate(); + httpReplyParse(r, icap->respmod.req_hdr_copy.buf, + icap->respmod.req_hdr_copy.size); + icap->respmod.res_body_sz = httpReplyBodySize(icap->request->method, r); + httpReplyDestroy(r); + if (icap->respmod.res_body_sz) + getICAPRespModString(mb, 0, o2, o3, client_addr, icap, service); + else + getICAPRespModString(mb, 0, o2, -o3, client_addr, icap, service); + if (Config.icapcfg.preview_enable) + if (icap->preview_size >= 0) { + memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size); + icap->flags.preview_done = 0; + } + if (service->keep_alive) { + icap->flags.keep_alive = 1; + memBufAppend(mb, "Connection: keep-alive\r\n", 24); + } else { + icap->flags.keep_alive = 0; + memBufAppend(mb, "Connection: close\r\n", 19); + } + memBufAppend(mb, crlf, 2); + memBufAppend(mb, mb_hdr.buf, mb_hdr.size); + memBufClean(&mb_hdr); + + + return consumed; +} + + +void +icapSendRespMod(IcapStateData * icap, char *buf, int len, int theEnd) +{ + MemBuf mb; +#if ICAP_PREVIEW + int size; + const int preview_size = icap->preview_size; +#endif + debug(81, 5) ("icapSendRespMod: FD %d, len %d, theEnd %d\n", + icap->icap_fd, len, theEnd); + + if (icap->flags.no_content) { + /* + * ICAP server said there are no modifications to make, so + * just append this data to the StoreEntry + */ + if (icap->respmod.resp_copy.size) { + /* + * first copy the data that we already sent to the ICAP server + */ + memBufAppend(&icap->chunk_buf, + icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); + icap->respmod.resp_copy.size = 0; + } + debug(81, 5) ("icapSendRepMod: len=%d theEnd=%d write_pending=%d\n", + len, theEnd, icap->flags.write_pending); + if (len) { + /* + * also copy any new data from the HTTP side + */ + memBufAppend(&icap->chunk_buf, buf, len); + } + (void) icapReadReply2(icap); + return; + } + if (theEnd) { + if (icap->respmod.res_body_sz) + icap->flags.send_zero_chunk = 1; + icap->flags.http_server_eof = 1; + } + /* + * httpReadReply is going to call us with a chunk and then + * right away again with an EOF if httpPconnTransferDone() is true. + * Since the first write is already dispatched, we'll have to + * hack this in somehow. + */ + if (icap->flags.write_pending) { + debug(81, 3) ("icapSendRespMod: oops, write_pending=1\n"); + assert(theEnd); + assert(len == 0); + return; + } + if (!cbdataValid(icap)) { + debug(81, 3) ("icapSendRespMod: failed to establish connection?\n"); + return; + } + memBufDefInit(&mb); + +#if SUPPORT_ICAP_204 || ICAP_PREVIEW + /* + * make a copy of the response in case ICAP server gives us a 204 + */ + /* + * This piece of code is problematic for 204 responces outside preview. + * The icap->respmod.resp_copy continues to filled until we had responce + * If the icap server waits to gets all data before sends its responce + * then we are puting all downloading object to the main system memory. + * My opinion is that 204 responces outside preview must be disabled ..... + * /chtsanti + */ + + if (len && icap->flags.copy_response) { + if (memBufIsNull(&icap->respmod.resp_copy)) + memBufDefInit(&icap->respmod.resp_copy); + memBufAppend(&icap->respmod.resp_copy, buf, len); + } +#endif + + if (icap->sc == 0) { + // http connection has been closed without sending us anything + if (len == 0 && theEnd == 1) { + ErrorState *err; + err = errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY, icap->request); + errorAppendEntry(icap->respmod.entry, err); + comm_close(icap->icap_fd); + return; + } + /* No data sent yet. Start with headers */ + if ((icap->sc = buildRespModHeader(&mb, icap, buf, len, theEnd)) > 0) { + buf += icap->sc; + len -= icap->sc; + } + /* + * Then we do not have http responce headers. All data (previous and those in buf) + * now are exist to icap->respmod.req_hdr_copy. Lets get them back....... + */ + if (icap->sc < 0) { + memBufAppend(&icap->respmod.buffer, + icap->respmod.req_hdr_copy.buf, + icap->respmod.req_hdr_copy.size); + icap->sc = icap->respmod.req_hdr_copy.size; + icap->respmod.req_hdr_copy.size = 0; + buf = NULL; + len = 0; + } + } + if (0 == icap->sc) { + /* check again; bail if we're not ready to send ICAP/HTTP hdrs */ + debug(81, 5) ("icapSendRespMod: dont have full HTTP response hdrs\n"); + memBufClean(&mb); + return; + } +#if ICAP_PREVIEW + if (preview_size < 0 || !Config.icapcfg.preview_enable) /* preview feature off */ + icap->flags.preview_done = 1; + + if (!icap->flags.preview_done) { + /* preview not yet sent */ + if (icap->sc > 0 && icap->respmod.buffer.size <= preview_size + && len > 0) { + /* Try to collect at least preview_size+1 bytes */ + /* By collecting one more byte than needed for preview we know best */ + /* whether we have to send the ieof chunk extension */ + size = icap->respmod.buffer.size + len; + if (size > preview_size + 1) + size = preview_size + 1; + size -= icap->respmod.buffer.size; + debug(81, + 3) + ("icapSendRespMod: FD %d: copy %d more bytes to preview buffer.\n", + icap->icap_fd, size); + memBufAppend(&icap->respmod.buffer, buf, size); + buf = ((char *) buf) + size; + len -= size; + } + if (icap->respmod.buffer.size > preview_size || theEnd) { + /* we got enough bytes for preview or this is the last call */ + /* add preview preview now */ + if (icap->respmod.buffer.size > 0) { + size = icap->respmod.buffer.size; + if (size > preview_size) + size = preview_size; + memBufPrintf(&mb, "%x\r\n", size); + memBufAppend(&mb, icap->respmod.buffer.buf, size); + memBufAppend(&mb, crlf, 2); + icap->sc += size; + } + if (icap->respmod.buffer.size <= preview_size) { + /* content length is less than preview size+1 */ + if (icap->respmod.res_body_sz) + memBufAppend(&mb, "0; ieof\r\n\r\n", 11); + memBufReset(&icap->respmod.buffer); /* will now be used for other data */ + } else { + char ch; + memBufAppend(&mb, "0\r\n\r\n", 5); + /* end of preview, wait for continue or 204 signal */ + /* copy the extra byte and all other data to the icap buffer */ + /* so that it can be handled next time */ + ch = icap->respmod.buffer.buf[preview_size]; + memBufReset(&icap->respmod.buffer); /* will now be used for other data */ + memBufAppend(&icap->respmod.buffer, &ch, 1); + debug(81, + 3) + ("icapSendRespMod: FD %d: sending preview and keeping %d bytes in internal buf.\n", + icap->icap_fd, len + 1); + if (len > 0) + memBufAppend(&icap->respmod.buffer, buf, len); + } + icap->flags.preview_done = 1; + icap->flags.wait_for_preview_reply = 1; + } + } else if (icap->flags.wait_for_preview_reply) { + /* received new data while waiting for preview response */ + /* add data to internal buffer and send later */ + debug(81, + 3) + ("icapSendRespMod: FD %d: add %d more bytes to internal buf while waiting for preview-response.\n", + icap->icap_fd, len); + if (len > 0) + memBufAppend(&icap->respmod.buffer, buf, len); + /* do not send any data now while waiting for preview response */ + /* but prepare for read more data on the HTTP connection */ + memBufClean(&mb); + return; + } else +#endif + { + /* after preview completed and ICAP preview response received */ + /* there may still be some data in the buffer */ + if (icap->respmod.buffer.size > 0) { + memBufPrintf(&mb, "%x\r\n", icap->respmod.buffer.size); + memBufAppend(&mb, icap->respmod.buffer.buf, + icap->respmod.buffer.size); + memBufAppend(&mb, crlf, 2); + icap->sc += icap->respmod.buffer.size; + memBufReset(&icap->respmod.buffer); + } + if (len > 0) { + memBufPrintf(&mb, "%x\r\n", len); + memBufAppend(&mb, buf, len); + memBufAppend(&mb, crlf, 2); + icap->sc += len; + } + if (icap->flags.send_zero_chunk) { + /* send zero end chunk */ + icap->flags.send_zero_chunk = 0; + icap->flags.http_server_eof = 1; + memBufAppend(&mb, "0\r\n\r\n", 5); + } + /* wait for data coming from ICAP server as soon as we sent something */ + /* but of course only until we got the response header */ + if (!icap->flags.got_reply) + icap->flags.wait_for_reply = 1; + } + commSetTimeout(icap->icap_fd, -1, NULL, NULL); + + if (!mb.size) { + memBufClean(&mb); + return; + } + debug(81, 5) ("icapSendRespMod: FD %d writing {%s}\n", icap->icap_fd, + mb.buf); + icap->flags.write_pending = 1; + comm_write_mbuf(icap->icap_fd, mb, icapSendRespModDone, icap); +} + +static void +icapRespModReadReply(int fd, void *data) +{ + IcapStateData *icap = data; + int version_major, version_minor; + const char *str_status; + int x; + int status = 0; + int isIcap = 0; + int directResponse = 0; + ErrorState *err; + const char *start; + const char *end; + + debug(81, 5) ("icapRespModReadReply: FD %d data = %p\n", fd, data); + statCounter.syscalls.sock.reads++; + + x = icapReadHeader(fd, icap, &isIcap); + if (x < 0) { + /* Did not find a proper ICAP response */ + debug(81, 3) ("ICAP : Error path!\n"); + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + } + if (x == 0) { + /* + * Waiting for more headers. Schedule new read hander, but + * don't reset timeout. + */ + commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); + return; + } + /* + * Parse the ICAP header + */ + assert(icap->icap_hdr.size); + debug(81, 3) ("Parse icap header : <%s>\n", icap->icap_hdr.buf); + if ((status = + icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, + &version_major, &version_minor, &str_status)) < 0) { + debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); + /* is this correct in case of ICAP protocol error? */ + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + }; + /* OK here we have responce. Lets stop filling the + * icap->respmod.resp_copy buffer .... + */ + icap->flags.copy_response = 0; + + icapSetKeepAlive(icap, icap->icap_hdr.buf); +#if ICAP_PREVIEW + if (icap->flags.wait_for_preview_reply) { + if (100 == status) { + debug(81, 5) ("icapRespModReadReply: 100 Continue received\n"); + icap->flags.wait_for_preview_reply = 0; + /* if http_server_eof + * call again icapSendRespMod to handle data that + * was received while waiting for this ICAP response + * else let http to call icapSendRespMod when new data arrived + */ + if (icap->flags.http_server_eof) + icapSendRespMod(icap, NULL, 0, 0); + /* + * reset the header to send the rest of the preview + */ + if (!memBufIsNull(&icap->icap_hdr)) + memBufReset(&icap->icap_hdr); + + /*We do n't need it any more ....... */ + if (!memBufIsNull(&icap->respmod.resp_copy)) + memBufClean(&icap->respmod.resp_copy); + + return; + } + if (204 == status) { + debug(81, + 5) ("icapRespModReadReply: 204 No modification received\n"); + icap->flags.wait_for_preview_reply = 0; + } + } +#endif /*ICAP_PREVIEW */ + +#if SUPPORT_ICAP_204 || ICAP_PREVIEW + if (204 == status) { + debug(81, 3) ("got 204 status from ICAP server\n"); + icapRespModKeepAliveOrClose(icap); + + debug(81, 3) ("setting icap->flags.no_content\n"); + icap->flags.no_content = 1; + /* + * copy the response already written to the ICAP server + */ + debug(81, 3) ("copying %d bytes from resp_copy to chunk_buf\n", + icap->respmod.resp_copy.size); + memBufAppend(&icap->chunk_buf, + icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); + icap->respmod.resp_copy.size = 0; + if (icapReadReply2(icap) < 0) + icapStateFree(-1, icap); + + /* + * XXX ideally want to clean icap->respmod.resp_copy here + * XXX ideally want to "close" ICAP server connection here + * OK do it.... + */ + if (!memBufIsNull(&icap->respmod.resp_copy)) + memBufClean(&icap->respmod.resp_copy); + return; + } +#endif + if (200 != status && 201 != status) { + debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); + /* Did not find a proper ICAP response */ + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + } + if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { + icapParseEncapsulated(icap, start, end); + } else { + debug(81, + 1) + ("WARNING: icapRespModReadReply() did not find 'Encapsulated' header\n"); + } + if (icap->enc.res_hdr > -1) + directResponse = 1; + else if (icap->enc.res_body > -1) + directResponse = 1; + else + directResponse = 0; + + /* + * "directResponse" is the normal case here. If we don't have + * a response header or body, it is an error. + */ + if (!directResponse) { + /* Did not find a proper ICAP response */ + debug(81, 3) ("ICAP : Error path!\n"); + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + } + /* got the reply, no need to come here again */ + icap->flags.wait_for_reply = 0; + icap->flags.got_reply = 1; + /* Next, gobble any data before the HTTP response starts */ + if (icap->enc.res_hdr > -1) + icap->bytes_to_gobble = icap->enc.res_hdr; + commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); +} + + +/* + * Gobble up (read) some bytes until we get to the start of the body + */ +static void +icapRespModGobble(int fd, void *data) +{ + IcapStateData *icap = data; + int len; + LOCAL_ARRAY(char, junk, SQUID_TCP_SO_RCVBUF); + debug(81, 3) ("icapRespModGobble: FD %d gobbling %d bytes\n", fd, + icap->bytes_to_gobble); + len = FD_READ_METHOD(fd, junk, icap->bytes_to_gobble); + debug(81, 3) ("icapRespModGobble: gobbled %d bytes\n", len); + if (len < 0) { + /* XXX error */ + abort(); + } + icap->bytes_to_gobble -= len; + if (icap->bytes_to_gobble) + commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); + else + icapReadReply(fd, icap); +} + + +static void +icapSendRespModDone(int fd, char *bufnotused, size_t size, int errflag, + void *data) +{ + IcapStateData *icap = data; + ErrorState *err; + + icap->flags.write_pending = 0; + debug(81, 5) ("icapSendRespModDone: 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 == COMM_ERR_CLOSING) + return; + if (errflag) { + if (cbdataValid(icap)) + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + else + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, NULL); + err->xerrno = errno; + storeEntryReset(icap->respmod.entry); + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + } + if (EBIT_TEST(icap->respmod.entry->flags, ENTRY_ABORTED)) { + debug(81, 3) ("icapSendRespModDone: Entry Aborded\n"); + comm_close(fd); + return; + } + if (icap->flags.send_zero_chunk) { + debug(81, + 3) ("icapSendRespModDone: I'm supposed to send zero chunk now\n"); + icap->flags.send_zero_chunk = 0; + icapSendRespMod(icap, NULL, 0, 1); + return; + } + if (icap->flags.wait_for_preview_reply || icap->flags.wait_for_reply) { + /* Schedule reading the ICAP response */ + debug(81, + 3) + ("icapSendRespModDone: FD %d: commSetSelect on read icapRespModReadReply.\n", + fd); + commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); +#if 1 + commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); + commSetDefer(fd, fwdCheckDeferRead, icap->respmod.entry); +#else + if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { + /* + * Set the read timeout only after all data has been sent + * or we are waiting for a preview response + * If the ICAP server does not return any data till all data + * has been sent, we are likely to hit the timeout for large + * HTTP bodies + */ + commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); + } +#endif + } +} + +void +icapConnectOver(int fd, int status, void *data) +{ + ErrorState *err; + IcapStateData *icap = data; + debug(81, 3) ("icapConnectOver: FD %d, status=%d\n", fd, status); + icap->flags.connect_pending = 0; + if (status < 0) { + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + debug(81, 3) ("icapConnectOver: status < 0, unreachable=1\n"); + icapOptSetUnreachable(icap->current_service); + return; + } + fd_table[fd].pconn.uses++; + fd_table[fd].pconn.type = 2; + commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); +} + + + +IcapStateData * +icapRespModStart(icap_service_t type, request_t * request, StoreEntry * entry, + http_state_flags http_flags) +{ + IcapStateData *icap = NULL; + CNCB *theCallback = NULL; + icap_service *service = NULL; + + debug(81, 3) ("icapRespModStart: type=%d\n", (int) type); + assert(type >= 0 && type < ICAP_SERVICE_MAX); + + service = icapService(type, request); + if (!service) { + debug(81, 3) ("icapRespModStart: no service found\n"); + return NULL; /* no service found */ + } + if (service->unreachable) { + if (service->bypass) { + debug(81, + 5) + ("icapRespModStart: BYPASS because service unreachable: %s\n", + service->uri); + return NULL; + } else { + debug(81, + 5) + ("icapRespModStart: ERROR because service unreachable: %s\n", + service->uri); + return (IcapStateData *) - 1; + } + } + switch (type) { + /* TODO: When we support more than ICAP_SERVICE_RESPMOD_PRECACHE, we needs to change + * this switch, because callbacks isn't keep */ + case ICAP_SERVICE_RESPMOD_PRECACHE: + theCallback = icapConnectOver; + break; + default: + fatalf("icapRespModStart: unsupported service type '%s'\n", + icap_service_type_str[type]); + break; + } + + icap = icapAllocate(); + if (!icap) { + debug(81, 3) ("icapRespModStart: icapAllocate() failed\n"); + return NULL; + } + icap->request = requestLink(request); + icap->respmod.entry = entry; + if (entry) + storeLockObject(entry); + icap->http_flags = http_flags; + memBufDefInit(&icap->respmod.buffer); + memBufDefInit(&icap->chunk_buf); + + icap->current_service = service; + icap->preview_size = service->preview; + + /* + * Don't create socket to the icap server now, but only for the first + * packet receive from the http server. This will resolve all timeout + * between the web server and icap server. + */ + debug(81, 3) ("icapRespModStart: setting connect_requested to 0\n"); + icap->flags.connect_requested = 0; + + /* + * make a copy the HTTP response that we send to the ICAP server in + * case it turns out to be a 204 + */ +#ifdef SUPPORT_ICAP_204 + icap->flags.copy_response = 1; +#elif ICAP_PREVIEW + if (preview_size < 0 || !Config.icapcfg.preview_enable) + icap->flags.copy_response = 0; + else + icap->flags.copy_response = 1; +#else + icap->flags.copy_response = 0; +#endif + + statCounter.icap.all.requests++; + debug(81, 3) ("icapRespModStart: returning %p\n", icap); + return icap; +} + +static int +icapHttpReplyHdrState(IcapStateData * icap) +{ + assert(icap); + if (NULL == icap->httpState) + return 0; + return icap->httpState->reply_hdr_state; +} + +static void +icapProcessHttpReplyHeader(IcapStateData * icap, const char *buf, int size) +{ + if (NULL == icap->httpState) { + icap->httpState = cbdataAlloc(HttpStateData); + icap->httpState->request = requestLink(icap->request); + icap->httpState->orig_request = requestLink(icap->request); + icap->httpState->entry = icap->respmod.entry; + storeLockObject(icap->httpState->entry); /* lock it */ + } + httpProcessReplyHeader(icap->httpState, buf, size); + if (2 == icap->httpState->reply_hdr_state) + EBIT_CLR(icap->httpState->entry->flags, ENTRY_FWD_HDR_WAIT); +} + +/* + * icapRespModKeepAliveOrClose + * + * Called when we are done reading from the ICAP server. + * Either close the connection or keep it open for a future + * transaction. + */ +static void +icapRespModKeepAliveOrClose(IcapStateData * icap) +{ + int fd = icap->icap_fd; + if (fd < 0) + return; + debug(81, 3) ("%s:%d FD %d looks good, keeping alive\n", __FILE__, __LINE__, + fd); + commSetDefer(fd, NULL, NULL); + commSetTimeout(fd, -1, NULL, NULL); + commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); + comm_remove_close_handler(fd, icapStateFree, icap); + icap->icap_fd = -1; + if (!icap->flags.keep_alive) { + debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, + __LINE__); + comm_close(fd); + return; + } else { + pconnPush(fd, icap->current_service->hostname, icap->current_service->port, NULL, NULL, 0); + } +} + + + +/* + * copied from httpPconnTransferDone + * + */ +static int +icapPconnTransferDone(int fd, IcapStateData * icap) +{ + debug(81, 3) ("icapPconnTransferDone: FD %d\n", fd); + /* + * Be careful with 204 responses. Normally we are done when we + * see the zero-end chunk, but that won't happen for 204s, so we + * use an EOF indicator on the HTTP side instead. + */ + if (icap->flags.no_content && icap->flags.http_server_eof) { + debug(81, 5) ("icapPconnTransferDone: no content, ret 1\n"); + return 1; + } + if (icapHttpReplyHdrState(icap) != 2) { + debug(81, + 5) ("icapPconnTransferDone: didn't see end of HTTP hdrs, ret 0\n"); + return 0; + } + if (icap->enc.null_body > -1) { + debug(81, 5) ("icapPconnTransferDone: no message body, ret 1\n"); + return 1; + } + if (icap->chunk_size == -2) { //AI: was != -2 ; and change content with bottom + /* zero end chunk reached */ + debug(81, 5) ("icapPconnTransferDone: got zero end chunk\n"); + return 1; + } + debug(81, 5) ("icapPconnTransferDone: didnt get zero end chunk yet\n"); //AI: change with second top condition + + return 0; +} + +static int +icapExpectedHttpReplyHdrSize(IcapStateData * icap) +{ + if (icap->enc.res_body > -1 && icap->enc.res_hdr > -1) + return (icap->enc.res_body - icap->enc.res_hdr); + if (icap->enc.null_body > -1 && icap->enc.res_hdr > -1) + return icap->enc.null_body - icap->enc.res_hdr; + /*The case we did not get res_hdr ..... */ + if (icap->enc.res_body > -1) + return icap->enc.res_body; + if (icap->enc.null_body > -1) + return icap->enc.null_body; + return -1; +} + +/* + * copied from httpReadReply() + * + * by the time this is called, the ICAP headers have already + * been read. + */ +void +icapReadReply(int fd, void *data) +{ + IcapStateData *icap = data; + StoreEntry *entry = icap->respmod.entry; + const request_t *request = icap->request; + int len; + debug(81, 5) ("icapReadReply: FD %d: icap %p.\n", fd, data); + if (icap->flags.no_content && !icap->flags.http_server_eof) { //AI + + return; + } + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + comm_close(fd); + return; + } + errno = 0; + statCounter.syscalls.sock.reads++; + len = memBufRead(fd, &icap->chunk_buf); + debug(81, 5) ("icapReadReply: FD %d: len %d.\n", fd, len); + if (len > 0) { + fd_bytes(fd, len, FD_READ); + kb_incr(&statCounter.icap.all.kbytes_in, len); + commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); + if (icap->chunk_buf.size < icap->chunk_buf.capacity) { + *(icap->chunk_buf.buf + icap->chunk_buf.size) = '\0'; + debug(81, 9) ("{%s}\n", icap->chunk_buf.buf); + } + } + if (len <= 0) { + debug(81, 2) ("icapReadReply: FD %d: read failure: %s.\n", + fd, xstrerror()); + if (ignoreErrno(errno)) { + debug(81, 2) ("icapReadReply: FD %d: ignored errno\n", fd); + commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); + } else if (entry->mem_obj->inmem_hi == 0) { + ErrorState *err; + debug(81, 2) ("icapReadReply: FD %d: generating error page\n", fd); + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, (request_t *)request); + err->xerrno = errno; + errorAppendEntry(entry, err); + comm_close(fd); + } else { + debug(81, 2) ("icapReadReply: FD %d: just calling comm_close()\n", + fd); + comm_close(fd); + } + return; + } + if (icapReadReply2(icap) < 0) + comm_close(fd); +} + +static int +icapReadReply2(IcapStateData * icap) +{ + StoreEntry *entry = icap->respmod.entry; + const request_t *request = icap->request; + debug(81, 3) ("icapReadReply2\n"); + if (icap->chunk_buf.size == 0 && entry->mem_obj->inmem_hi == 0) { + ErrorState *err; + err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE, (request_t *)request); + err->xerrno = errno; + errorAppendEntry(entry, err); + icap->flags.http_server_eof = 1; + return -1; + } + if (icap->chunk_buf.size == 0) { + /* Retrieval done. */ + if (icapHttpReplyHdrState(icap) < 2) + icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, + icap->chunk_buf.size); + icap->flags.http_server_eof = 1; + icapReadReply3(icap); + return 0; + } + if (icapHttpReplyHdrState(icap) == 0) { + int expect = icapExpectedHttpReplyHdrSize(icap); + int so_far = icap->http_header_bytes_read_so_far; + int needed = expect - so_far; + debug(81, 3) ("expect=%d\n", expect); + debug(81, 3) ("so_far=%d\n", so_far); + debug(81, 3) ("needed=%d\n", needed); + assert(needed < 0 || needed >= 0); + if (0 > expect) { + icapProcessHttpReplyHeader(icap, + icap->chunk_buf.buf, icap->chunk_buf.size); + } else if (0 == expect) { + /* + * this icap reply doesn't give us new HTTP headers + * so we must copy them from our copy + */ + debug(81, 1) ("WARNING: untested code at %s:%d\n", __FILE__, + __LINE__); + if (icap->respmod.req_hdr_copy.size) { /*For HTTP 0.9 we do not have headers */ + storeAppend(entry, + icap->respmod.req_hdr_copy.buf, + icap->respmod.req_hdr_copy.size); + } + icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, + icap->chunk_buf.size); + assert(icapHttpReplyHdrState(icap) == 2); + icap->chunk_size = 0; /*we are ready to read chunks of data now.... */ + } else if (needed) { + icapProcessHttpReplyHeader(icap, + icap->chunk_buf.buf, icap->chunk_buf.size); + if (icap->chunk_buf.size >= needed) { + storeAppend(entry, icap->chunk_buf.buf, needed); + so_far += needed; + xmemmove(icap->chunk_buf.buf, + icap->chunk_buf.buf + needed, + icap->chunk_buf.size - needed); + icap->chunk_buf.size -= needed; + assert(icapHttpReplyHdrState(icap) == 2); + icap->chunk_size = 0; + } else { + /* + * We don't have the full HTTP reply headers yet, so keep + * the partial reply buffered in 'chunk_buf' and wait + * for more. + */ + debug(81, 3) ("We don't have full Http headers.Schedule a new read\n"); + commSetSelect(icap->icap_fd, COMM_SELECT_READ, icapReadReply, icap, 0); + } + } + icap->http_header_bytes_read_so_far = so_far; + } + debug(81, 3) ("%s:%d: icap->chunk_buf.size=%d\n", __FILE__, __LINE__, + (int) icap->chunk_buf.size); + debug(81, 3) ("%s:%d: flags.no_content=%d\n", __FILE__, __LINE__, + icap->flags.no_content); + if (icap->flags.no_content) { + /* data from http.c is not chunked */ + if (!EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + debug(81, 3) ("copying %d bytes from chunk_buf to entry\n", + icap->chunk_buf.size); + storeAppend(entry, icap->chunk_buf.buf, icap->chunk_buf.size); + icap->chunk_buf.size = 0; + } + } else if (2 == icapHttpReplyHdrState(icap)) { + if (icap->chunk_buf.size) + icapParseChunkedBody(icap, (STRCB *) storeAppend, entry); + } + icapReadReply3(icap); + return 0; +} + +static void +icapReadReply3(IcapStateData * icap) +{ + StoreEntry *entry = icap->respmod.entry; + int fd = icap->icap_fd; + debug(81, 3) ("icapReadReply3\n"); + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + debug(81, 3) ("icapReadReply3: Entry Aborded\n"); + if (icap->flags.no_content) + icapStateFree(-1, icap); + else + comm_close(fd); + } else if (icapPconnTransferDone(fd, icap)) { + storeComplete(entry); + if (icap->flags.no_content) + icapStateFree(-1, icap); + else { + icapRespModKeepAliveOrClose(icap); + icapStateFree(-1, icap); + } + } else if (!icap->flags.no_content) { + /* Wait for EOF condition */ + commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); + debug(81, + 3) + ("icapReadReply3: Going to read mode data throught icapReadReply\n"); + } else { + debug(81, 3) ("icapReadReply3: Nothing\n"); + } +} diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/28/105700105f88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/28/105700105f88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/28/105700105f88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/28/105700105f88001b16add8656805b17d 2006-12-10 16:09:19.000000000 +0200 @@ -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)); +} diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/2f/603c963c5888001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/2f/603c963c5888001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/2f/603c963c5888001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/2f/603c963c5888001b16add8656805b17d 2006-12-10 16:10:49.000000000 +0200 @@ -0,0 +1,12 @@ + + + src + + + + + + + org.eclipse.cdt.core.cnature + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/38/00d19a315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/38/00d19a315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/38/00d19a315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/38/00d19a315c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,365 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/CacheDigest.c \ +$(ROOT)/HttpBody.c \ +$(ROOT)/HttpHdrCc.c \ +$(ROOT)/HttpHdrContRange.c \ +$(ROOT)/HttpHdrRange.c \ +$(ROOT)/HttpHeader.c \ +$(ROOT)/HttpHeaderTools.c \ +$(ROOT)/HttpMsg.c \ +$(ROOT)/HttpReply.c \ +$(ROOT)/HttpRequest.c \ +$(ROOT)/HttpStatusLine.c \ +$(ROOT)/MemBuf.c \ +$(ROOT)/MemPool.c \ +$(ROOT)/Packer.c \ +$(ROOT)/StatHist.c \ +$(ROOT)/String.c \ +$(ROOT)/access_log.c \ +$(ROOT)/acl.c \ +$(ROOT)/asn.c \ +$(ROOT)/authenticate.c \ +$(ROOT)/cache_cf.c \ +$(ROOT)/cache_manager.c \ +$(ROOT)/carp.c \ +$(ROOT)/cbdata.c \ +$(ROOT)/cf_gen.c \ +$(ROOT)/client_db.c \ +$(ROOT)/client_side.c \ +$(ROOT)/comm.c \ +$(ROOT)/comm_epoll.c \ +$(ROOT)/comm_generic.c \ +$(ROOT)/comm_kqueue.c \ +$(ROOT)/comm_poll.c \ +$(ROOT)/comm_select.c \ +$(ROOT)/comm_select_simple.c \ +$(ROOT)/comm_select_win32.c \ +$(ROOT)/debug.c \ +$(ROOT)/delay_pools.c \ +$(ROOT)/disk.c \ +$(ROOT)/dns.c \ +$(ROOT)/dns_internal.c \ +$(ROOT)/dnsserver.c \ +$(ROOT)/errormap.c \ +$(ROOT)/errorpage.c \ +$(ROOT)/event.c \ +$(ROOT)/external_acl.c \ +$(ROOT)/fd.c \ +$(ROOT)/filemap.c \ +$(ROOT)/forward.c \ +$(ROOT)/fqdncache.c \ +$(ROOT)/ftp.c \ +$(ROOT)/gopher.c \ +$(ROOT)/helper.c \ +$(ROOT)/htcp.c \ +$(ROOT)/http.c \ +$(ROOT)/icap_common.c \ +$(ROOT)/icap_opt.c \ +$(ROOT)/icap_reqmod.c \ +$(ROOT)/icap_respmod.c \ +$(ROOT)/icmp.c \ +$(ROOT)/icp_v2.c \ +$(ROOT)/icp_v3.c \ +$(ROOT)/ident.c \ +$(ROOT)/internal.c \ +$(ROOT)/ipc.c \ +$(ROOT)/ipc_win32.c \ +$(ROOT)/ipcache.c \ +$(ROOT)/leakfinder.c \ +$(ROOT)/locrewrite.c \ +$(ROOT)/logfile.c \ +$(ROOT)/main.c \ +$(ROOT)/mem.c \ +$(ROOT)/mime.c \ +$(ROOT)/multicast.c \ +$(ROOT)/neighbors.c \ +$(ROOT)/net_db.c \ +$(ROOT)/pconn.c \ +$(ROOT)/peer_digest.c \ +$(ROOT)/peer_monitor.c \ +$(ROOT)/peer_select.c \ +$(ROOT)/peer_sourcehash.c \ +$(ROOT)/peer_userhash.c \ +$(ROOT)/pinger.c \ +$(ROOT)/redirect.c \ +$(ROOT)/referer.c \ +$(ROOT)/refresh.c \ +$(ROOT)/send-announce.c \ +$(ROOT)/snmp_agent.c \ +$(ROOT)/snmp_core.c \ +$(ROOT)/ssl.c \ +$(ROOT)/ssl_support.c \ +$(ROOT)/stat.c \ +$(ROOT)/stmem.c \ +$(ROOT)/store.c \ +$(ROOT)/store_client.c \ +$(ROOT)/store_digest.c \ +$(ROOT)/store_dir.c \ +$(ROOT)/store_io.c \ +$(ROOT)/store_key_md5.c \ +$(ROOT)/store_log.c \ +$(ROOT)/store_rebuild.c \ +$(ROOT)/store_swapin.c \ +$(ROOT)/store_swapmeta.c \ +$(ROOT)/store_swapout.c \ +$(ROOT)/tools.c \ +$(ROOT)/unlinkd.c \ +$(ROOT)/url.c \ +$(ROOT)/urn.c \ +$(ROOT)/useragent.c \ +$(ROOT)/wais.c \ +$(ROOT)/wccp.c \ +$(ROOT)/wccp2.c \ +$(ROOT)/whois.c \ +$(ROOT)/win32.c + +OBJS += \ +./CacheDigest.o \ +./HttpBody.o \ +./HttpHdrCc.o \ +./HttpHdrContRange.o \ +./HttpHdrRange.o \ +./HttpHeader.o \ +./HttpHeaderTools.o \ +./HttpMsg.o \ +./HttpReply.o \ +./HttpRequest.o \ +./HttpStatusLine.o \ +./MemBuf.o \ +./MemPool.o \ +./Packer.o \ +./StatHist.o \ +./String.o \ +./access_log.o \ +./acl.o \ +./asn.o \ +./authenticate.o \ +./cache_cf.o \ +./cache_manager.o \ +./carp.o \ +./cbdata.o \ +./cf_gen.o \ +./client_db.o \ +./client_side.o \ +./comm.o \ +./comm_epoll.o \ +./comm_generic.o \ +./comm_kqueue.o \ +./comm_poll.o \ +./comm_select.o \ +./comm_select_simple.o \ +./comm_select_win32.o \ +./debug.o \ +./delay_pools.o \ +./disk.o \ +./dns.o \ +./dns_internal.o \ +./dnsserver.o \ +./errormap.o \ +./errorpage.o \ +./event.o \ +./external_acl.o \ +./fd.o \ +./filemap.o \ +./forward.o \ +./fqdncache.o \ +./ftp.o \ +./gopher.o \ +./helper.o \ +./htcp.o \ +./http.o \ +./icap_common.o \ +./icap_opt.o \ +./icap_reqmod.o \ +./icap_respmod.o \ +./icmp.o \ +./icp_v2.o \ +./icp_v3.o \ +./ident.o \ +./internal.o \ +./ipc.o \ +./ipc_win32.o \ +./ipcache.o \ +./leakfinder.o \ +./locrewrite.o \ +./logfile.o \ +./main.o \ +./mem.o \ +./mime.o \ +./multicast.o \ +./neighbors.o \ +./net_db.o \ +./pconn.o \ +./peer_digest.o \ +./peer_monitor.o \ +./peer_select.o \ +./peer_sourcehash.o \ +./peer_userhash.o \ +./pinger.o \ +./redirect.o \ +./referer.o \ +./refresh.o \ +./send-announce.o \ +./snmp_agent.o \ +./snmp_core.o \ +./ssl.o \ +./ssl_support.o \ +./stat.o \ +./stmem.o \ +./store.o \ +./store_client.o \ +./store_digest.o \ +./store_dir.o \ +./store_io.o \ +./store_key_md5.o \ +./store_log.o \ +./store_rebuild.o \ +./store_swapin.o \ +./store_swapmeta.o \ +./store_swapout.o \ +./tools.o \ +./unlinkd.o \ +./url.o \ +./urn.o \ +./useragent.o \ +./wais.o \ +./wccp.o \ +./wccp2.o \ +./whois.o \ +./win32.o + +DEPS += \ +${addprefix ./, \ +CacheDigest.d \ +HttpBody.d \ +HttpHdrCc.d \ +HttpHdrContRange.d \ +HttpHdrRange.d \ +HttpHeader.d \ +HttpHeaderTools.d \ +HttpMsg.d \ +HttpReply.d \ +HttpRequest.d \ +HttpStatusLine.d \ +MemBuf.d \ +MemPool.d \ +Packer.d \ +StatHist.d \ +String.d \ +access_log.d \ +acl.d \ +asn.d \ +authenticate.d \ +cache_cf.d \ +cache_manager.d \ +carp.d \ +cbdata.d \ +cf_gen.d \ +client_db.d \ +client_side.d \ +comm.d \ +comm_epoll.d \ +comm_generic.d \ +comm_kqueue.d \ +comm_poll.d \ +comm_select.d \ +comm_select_simple.d \ +comm_select_win32.d \ +debug.d \ +delay_pools.d \ +disk.d \ +dns.d \ +dns_internal.d \ +dnsserver.d \ +errormap.d \ +errorpage.d \ +event.d \ +external_acl.d \ +fd.d \ +filemap.d \ +forward.d \ +fqdncache.d \ +ftp.d \ +gopher.d \ +helper.d \ +htcp.d \ +http.d \ +icap_common.d \ +icap_opt.d \ +icap_reqmod.d \ +icap_respmod.d \ +icmp.d \ +icp_v2.d \ +icp_v3.d \ +ident.d \ +internal.d \ +ipc.d \ +ipc_win32.d \ +ipcache.d \ +leakfinder.d \ +locrewrite.d \ +logfile.d \ +main.d \ +mem.d \ +mime.d \ +multicast.d \ +neighbors.d \ +net_db.d \ +pconn.d \ +peer_digest.d \ +peer_monitor.d \ +peer_select.d \ +peer_sourcehash.d \ +peer_userhash.d \ +pinger.d \ +redirect.d \ +referer.d \ +refresh.d \ +send-announce.d \ +snmp_agent.d \ +snmp_core.d \ +ssl.d \ +ssl_support.d \ +stat.d \ +stmem.d \ +store.d \ +store_client.d \ +store_digest.d \ +store_dir.d \ +store_io.d \ +store_key_md5.d \ +store_log.d \ +store_rebuild.d \ +store_swapin.d \ +store_swapmeta.d \ +store_swapout.d \ +tools.d \ +unlinkd.d \ +url.d \ +urn.d \ +useragent.d \ +wais.d \ +wccp.d \ +wccp2.d \ +whois.d \ +win32.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +%.o: $(ROOT)/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/3d/c0c5ea315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/3d/c0c5ea315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/3d/c0c5ea315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/3d/c0c5ea315c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,29 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/auth/digest/auth_digest.c + +OBJS += \ +./auth/digest/auth_digest.o + +DEPS += \ +${addprefix ./auth/digest/, \ +auth_digest.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +auth/digest/%.o: $(ROOT)/auth/digest/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/4d/5022ea3f5888001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/4d/5022ea3f5888001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/4d/5022ea3f5888001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/4d/5022ea3f5888001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,3 @@ +#Sun Dec 10 16:10:54 IST 2006 +eclipse.preferences.version=1 +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.exe.debug.1102957643=\n\n\n\n\n diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/5c/402a8c3c5888001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/5c/402a8c3c5888001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/5c/402a8c3c5888001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/5c/402a8c3c5888001b16add8656805b17d 2006-12-10 16:10:48.000000000 +0200 @@ -0,0 +1,11 @@ + + + src + + + + + + + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/62/9033cd365f88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/62/9033cd365f88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/62/9033cd365f88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/62/9033cd365f88001b16add8656805b17d 2006-12-10 16:59:40.000000000 +0200 @@ -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); + cap->respmod.icap_server_session_context = NULL; + 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)); +} diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/67/d04d973c5888001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/67/d04d973c5888001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/67/d04d973c5888001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/67/d04d973c5888001b16add8656805b17d 2006-12-10 16:10:49.000000000 +0200 @@ -0,0 +1,13 @@ + + + src + + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/7c/b06f2d606088001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/7c/b06f2d606088001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/7c/b06f2d606088001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/7c/b06f2d606088001b16add8656805b17d 2006-12-10 17:05:21.000000000 +0200 @@ -0,0 +1,1066 @@ + +/* + * $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. + * + */ + +#include "squid.h" + +static CWCB icapSendRespModDone; +static PF icapRespModGobble; +extern PF icapReadReply; +static PF icapRespModReadReply; +static void icapRespModKeepAliveOrClose(IcapStateData * icap); +static int icapReadReply2(IcapStateData * icap); +static void icapReadReply3(IcapStateData * icap); + +#define EXPECTED_ICAP_HEADER_LEN 256 +const char *crlf = "\r\n"; + +static void +getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, + const char *client_addr, IcapStateData * icap, const icap_service * service) +{ + memBufPrintf(mb, "RESPMOD %s ICAP/1.0\r\nEncapsulated:", service->uri); + if (o1 >= 0) + memBufPrintf(mb, " req-hdr=%1d", o1); + if (o2 >= 0) + memBufPrintf(mb, ", res-hdr=%1d", o2); + if (o3 >= 0) + memBufPrintf(mb, ", res-body=%1d", o3); + else + memBufPrintf(mb, ", null-body=%1d", -o3); + memBufPrintf(mb, crlf); + + if (service->flags.need_x_client_ip && Config.icapcfg.send_client_ip) { + memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); + } + if (icap->respmod.icap_server_session_context != NULL ) + { + debug(81, 5)( "sending icap context to server %s: %s\r\n", + Config.icapcfg.icap_session_context_tag_name, + icap->respmod.icap_server_session_context); + memBufPrintf(mb, "%s: %s\r\n", Config.icapcfg.icap_session_context_tag_name, + icap->respmod.icap_server_session_context); + } + if (service->flags.need_x_server_ip && Config.icapcfg.send_server_ip) + icapAddOriginIP(mb, icap->request->host); + + if ((service->flags.need_x_authenticated_user + && Config.icapcfg.send_auth_user) + && (icap->request->auth_user_request != NULL)) { + icapAddAuthUserHeader(mb, icap->request->auth_user_request); + } +#if NOT_YET_FINISHED + if (Config.icapcfg.trailers) { + memBufPrintf(mb, "X-TE: trailers\r\n"); + } +#endif +} + +static int +buildRespModHeader(MemBuf * mb, IcapStateData * icap, char *buf, + ssize_t len, int theEnd) +{ + MemBuf mb_hdr; + char *client_addr; + int o2 = 0; + int o3 = 0; + int hlen; + int consumed; + icap_service *service; + HttpReply *r; + + if (memBufIsNull(&icap->respmod.req_hdr_copy)) + memBufDefInit(&icap->respmod.req_hdr_copy); + + memBufAppend(&icap->respmod.req_hdr_copy, buf, len); + + if (icap->respmod.req_hdr_copy.size > 4 && strncmp(icap->respmod.req_hdr_copy.buf, "HTTP/", 5)) { + debug(81, 3) ("buildRespModHeader: Non-HTTP-compliant header: '%s'\n", buf); + /* + *Possible we can consider that we did not have http responce headers + *(maybe HTTP 0.9 protocol), lets returning -1... + */ + consumed = -1; + o2 = -1; + memBufDefInit(&mb_hdr); + httpBuildRequestPrefix(icap->request, icap->request, + icap->respmod.entry, &mb_hdr, icap->http_flags); + o3 = mb_hdr.size; + } else { + + hlen = headersEnd(icap->respmod.req_hdr_copy.buf, + icap->respmod.req_hdr_copy.size); + debug(81, 3) ("buildRespModHeader: headersEnd = %d(%s)\n", hlen, buf); + if (0 == hlen) + return 0; + + /* + * calc how many bytes from this 'buf' went towards the + * reply header. + */ + consumed = hlen - (icap->respmod.req_hdr_copy.size - len); + debug(81, 3) ("buildRespModHeader: consumed = %d\n", consumed); + + + /* + * now, truncate our req_hdr_copy at the header end. + * this 'if' statement might be unncessary? + */ + if (hlen < icap->respmod.req_hdr_copy.size) + icap->respmod.req_hdr_copy.size = hlen; + + /* Copy request header */ + memBufDefInit(&mb_hdr); + httpBuildRequestPrefix(icap->request, icap->request, + icap->respmod.entry, &mb_hdr, icap->http_flags); + o2 = mb_hdr.size; + + /* Copy response header - Append to request header mbuffer */ + memBufAppend(&mb_hdr, + icap->respmod.req_hdr_copy.buf, icap->respmod.req_hdr_copy.size); + o3 = mb_hdr.size; + } + + service = icap->current_service; + assert(service); + client_addr = inet_ntoa(icap->request->client_addr); + + r = httpReplyCreate(); + httpReplyParse(r, icap->respmod.req_hdr_copy.buf, + icap->respmod.req_hdr_copy.size); + icap->respmod.res_body_sz = httpReplyBodySize(icap->request->method, r); + httpReplyDestroy(r); + if (icap->respmod.res_body_sz) + getICAPRespModString(mb, 0, o2, o3, client_addr, icap, service); + else + getICAPRespModString(mb, 0, o2, -o3, client_addr, icap, service); + if (Config.icapcfg.preview_enable) + if (icap->preview_size >= 0) { + memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size); + icap->flags.preview_done = 0; + } + if (service->keep_alive) { + icap->flags.keep_alive = 1; + memBufAppend(mb, "Connection: keep-alive\r\n", 24); + } else { + icap->flags.keep_alive = 0; + memBufAppend(mb, "Connection: close\r\n", 19); + } + memBufAppend(mb, crlf, 2); + memBufAppend(mb, mb_hdr.buf, mb_hdr.size); + memBufClean(&mb_hdr); + + + return consumed; +} + + +void +icapSendRespMod(IcapStateData * icap, char *buf, int len, int theEnd) +{ + MemBuf mb; +#if ICAP_PREVIEW + int size; + const int preview_size = icap->preview_size; +#endif + debug(81, 5) ("icapSendRespMod: FD %d, len %d, theEnd %d\n", + icap->icap_fd, len, theEnd); + + if (icap->flags.no_content) { + /* + * ICAP server said there are no modifications to make, so + * just append this data to the StoreEntry + */ + if (icap->respmod.resp_copy.size) { + /* + * first copy the data that we already sent to the ICAP server + */ + memBufAppend(&icap->chunk_buf, + icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); + icap->respmod.resp_copy.size = 0; + } + debug(81, 5) ("icapSendRepMod: len=%d theEnd=%d write_pending=%d\n", + len, theEnd, icap->flags.write_pending); + if (len) { + /* + * also copy any new data from the HTTP side + */ + memBufAppend(&icap->chunk_buf, buf, len); + } + (void) icapReadReply2(icap); + return; + } + if (theEnd) { + if (icap->respmod.res_body_sz) + icap->flags.send_zero_chunk = 1; + icap->flags.http_server_eof = 1; + } + /* + * httpReadReply is going to call us with a chunk and then + * right away again with an EOF if httpPconnTransferDone() is true. + * Since the first write is already dispatched, we'll have to + * hack this in somehow. + */ + if (icap->flags.write_pending) { + debug(81, 3) ("icapSendRespMod: oops, write_pending=1\n"); + assert(theEnd); + assert(len == 0); + return; + } + if (!cbdataValid(icap)) { + debug(81, 3) ("icapSendRespMod: failed to establish connection?\n"); + return; + } + memBufDefInit(&mb); + +#if SUPPORT_ICAP_204 || ICAP_PREVIEW + /* + * make a copy of the response in case ICAP server gives us a 204 + */ + /* + * This piece of code is problematic for 204 responces outside preview. + * The icap->respmod.resp_copy continues to filled until we had responce + * If the icap server waits to gets all data before sends its responce + * then we are puting all downloading object to the main system memory. + * My opinion is that 204 responces outside preview must be disabled ..... + * /chtsanti + */ + + if (len && icap->flags.copy_response) { + if (memBufIsNull(&icap->respmod.resp_copy)) + memBufDefInit(&icap->respmod.resp_copy); + memBufAppend(&icap->respmod.resp_copy, buf, len); + } +#endif + + if (icap->sc == 0) { + // http connection has been closed without sending us anything + if (len == 0 && theEnd == 1) { + ErrorState *err; + err = errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY, icap->request); + errorAppendEntry(icap->respmod.entry, err); + comm_close(icap->icap_fd); + return; + } + /* No data sent yet. Start with headers */ + if ((icap->sc = buildRespModHeader(&mb, icap, buf, len, theEnd)) > 0) { + buf += icap->sc; + len -= icap->sc; + } + /* + * Then we do not have http responce headers. All data (previous and those in buf) + * now are exist to icap->respmod.req_hdr_copy. Lets get them back....... + */ + if (icap->sc < 0) { + memBufAppend(&icap->respmod.buffer, + icap->respmod.req_hdr_copy.buf, + icap->respmod.req_hdr_copy.size); + icap->sc = icap->respmod.req_hdr_copy.size; + icap->respmod.req_hdr_copy.size = 0; + buf = NULL; + len = 0; + } + } + if (0 == icap->sc) { + /* check again; bail if we're not ready to send ICAP/HTTP hdrs */ + debug(81, 5) ("icapSendRespMod: dont have full HTTP response hdrs\n"); + memBufClean(&mb); + return; + } +#if ICAP_PREVIEW + if (preview_size < 0 || !Config.icapcfg.preview_enable) /* preview feature off */ + icap->flags.preview_done = 1; + + if (!icap->flags.preview_done) { + /* preview not yet sent */ + if (icap->sc > 0 && icap->respmod.buffer.size <= preview_size + && len > 0) { + /* Try to collect at least preview_size+1 bytes */ + /* By collecting one more byte than needed for preview we know best */ + /* whether we have to send the ieof chunk extension */ + size = icap->respmod.buffer.size + len; + if (size > preview_size + 1) + size = preview_size + 1; + size -= icap->respmod.buffer.size; + debug(81, + 3) + ("icapSendRespMod: FD %d: copy %d more bytes to preview buffer.\n", + icap->icap_fd, size); + memBufAppend(&icap->respmod.buffer, buf, size); + buf = ((char *) buf) + size; + len -= size; + } + if (icap->respmod.buffer.size > preview_size || theEnd) { + /* we got enough bytes for preview or this is the last call */ + /* add preview preview now */ + if (icap->respmod.buffer.size > 0) { + size = icap->respmod.buffer.size; + if (size > preview_size) + size = preview_size; + memBufPrintf(&mb, "%x\r\n", size); + memBufAppend(&mb, icap->respmod.buffer.buf, size); + memBufAppend(&mb, crlf, 2); + icap->sc += size; + } + if (icap->respmod.buffer.size <= preview_size) { + /* content length is less than preview size+1 */ + if (icap->respmod.res_body_sz) + memBufAppend(&mb, "0; ieof\r\n\r\n", 11); + memBufReset(&icap->respmod.buffer); /* will now be used for other data */ + } else { + char ch; + memBufAppend(&mb, "0\r\n\r\n", 5); + /* end of preview, wait for continue or 204 signal */ + /* copy the extra byte and all other data to the icap buffer */ + /* so that it can be handled next time */ + ch = icap->respmod.buffer.buf[preview_size]; + memBufReset(&icap->respmod.buffer); /* will now be used for other data */ + memBufAppend(&icap->respmod.buffer, &ch, 1); + debug(81, + 3) + ("icapSendRespMod: FD %d: sending preview and keeping %d bytes in internal buf.\n", + icap->icap_fd, len + 1); + if (len > 0) + memBufAppend(&icap->respmod.buffer, buf, len); + } + icap->flags.preview_done = 1; + icap->flags.wait_for_preview_reply = 1; + } + } else if (icap->flags.wait_for_preview_reply) { + /* received new data while waiting for preview response */ + /* add data to internal buffer and send later */ + debug(81, + 3) + ("icapSendRespMod: FD %d: add %d more bytes to internal buf while waiting for preview-response.\n", + icap->icap_fd, len); + if (len > 0) + memBufAppend(&icap->respmod.buffer, buf, len); + /* do not send any data now while waiting for preview response */ + /* but prepare for read more data on the HTTP connection */ + memBufClean(&mb); + return; + } else +#endif + { + /* after preview completed and ICAP preview response received */ + /* there may still be some data in the buffer */ + if (icap->respmod.buffer.size > 0) { + memBufPrintf(&mb, "%x\r\n", icap->respmod.buffer.size); + memBufAppend(&mb, icap->respmod.buffer.buf, + icap->respmod.buffer.size); + memBufAppend(&mb, crlf, 2); + icap->sc += icap->respmod.buffer.size; + memBufReset(&icap->respmod.buffer); + } + if (len > 0) { + memBufPrintf(&mb, "%x\r\n", len); + memBufAppend(&mb, buf, len); + memBufAppend(&mb, crlf, 2); + icap->sc += len; + } + if (icap->flags.send_zero_chunk) { + /* send zero end chunk */ + icap->flags.send_zero_chunk = 0; + icap->flags.http_server_eof = 1; + memBufAppend(&mb, "0\r\n\r\n", 5); + } + /* wait for data coming from ICAP server as soon as we sent something */ + /* but of course only until we got the response header */ + if (!icap->flags.got_reply) + icap->flags.wait_for_reply = 1; + } + commSetTimeout(icap->icap_fd, -1, NULL, NULL); + + if (!mb.size) { + memBufClean(&mb); + return; + } + debug(81, 5) ("icapSendRespMod: FD %d writing {%s}\n", icap->icap_fd, + mb.buf); + icap->flags.write_pending = 1; + comm_write_mbuf(icap->icap_fd, mb, icapSendRespModDone, icap); +} + +static void +icapRespModReadReply(int fd, void *data) +{ + IcapStateData *icap = data; + int version_major, version_minor; + const char *str_status; + int x; + int status = 0; + int isIcap = 0; + int directResponse = 0; + ErrorState *err; + const char *start; + const char *end; + + debug(81, 5) ("icapRespModReadReply: FD %d data = %p\n", fd, data); + statCounter.syscalls.sock.reads++; + + x = icapReadHeader(fd, icap, &isIcap); + if (x < 0) { + /* Did not find a proper ICAP response */ + debug(81, 3) ("ICAP : Error path!\n"); + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + } + if (x == 0) { + /* + * Waiting for more headers. Schedule new read hander, but + * don't reset timeout. + */ + commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); + return; + } + /* + * Parse the ICAP header + */ + assert(icap->icap_hdr.size); + debug(81, 3) ("Parse icap header : <%s>\n", icap->icap_hdr.buf); + if ((status = + icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, + &version_major, &version_minor, &str_status)) < 0) { + debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); + /* is this correct in case of ICAP protocol error? */ + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + }; + /* OK here we have responce. Lets stop filling the + * icap->respmod.resp_copy buffer .... + */ + icap->flags.copy_response = 0; + + icapSetKeepAlive(icap, icap->icap_hdr.buf); +#if ICAP_PREVIEW + if (icap->flags.wait_for_preview_reply) { + if (100 == status) { + debug(81, 5) ("icapRespModReadReply: 100 Continue received\n"); + icap->flags.wait_for_preview_reply = 0; + /* if http_server_eof + * call again icapSendRespMod to handle data that + * was received while waiting for this ICAP response + * else let http to call icapSendRespMod when new data arrived + */ + if (icap->flags.http_server_eof) + icapSendRespMod(icap, NULL, 0, 0); + /* + * reset the header to send the rest of the preview + */ + if (!memBufIsNull(&icap->icap_hdr)) + memBufReset(&icap->icap_hdr); + + /*We do n't need it any more ....... */ + if (!memBufIsNull(&icap->respmod.resp_copy)) + memBufClean(&icap->respmod.resp_copy); + + return; + } + if (204 == status) { + debug(81, + 5) ("icapRespModReadReply: 204 No modification received\n"); + icap->flags.wait_for_preview_reply = 0; + } + } +#endif /*ICAP_PREVIEW */ + +#if SUPPORT_ICAP_204 || ICAP_PREVIEW + if (204 == status) { + debug(81, 3) ("got 204 status from ICAP server\n"); + icapRespModKeepAliveOrClose(icap); + + debug(81, 3) ("setting icap->flags.no_content\n"); + icap->flags.no_content = 1; + /* + * copy the response already written to the ICAP server + */ + debug(81, 3) ("copying %d bytes from resp_copy to chunk_buf\n", + icap->respmod.resp_copy.size); + memBufAppend(&icap->chunk_buf, + icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); + icap->respmod.resp_copy.size = 0; + if (icapReadReply2(icap) < 0) + icapStateFree(-1, icap); + + /* + * XXX ideally want to clean icap->respmod.resp_copy here + * XXX ideally want to "close" ICAP server connection here + * OK do it.... + */ + if (!memBufIsNull(&icap->respmod.resp_copy)) + memBufClean(&icap->respmod.resp_copy); + return; + } +#endif + if (200 != status && 201 != status) { + debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); + /* Did not find a proper ICAP response */ + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + } + if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { + icapParseEncapsulated(icap, start, end); + } else { + debug(81, + 1) + ("WARNING: icapRespModReadReply() did not find 'Encapsulated' header\n"); + } + if (icap->enc.res_hdr > -1) + directResponse = 1; + else if (icap->enc.res_body > -1) + directResponse = 1; + else + directResponse = 0; + + /* + * "directResponse" is the normal case here. If we don't have + * a response header or body, it is an error. + */ + if (!directResponse) { + /* Did not find a proper ICAP response */ + debug(81, 3) ("ICAP : Error path!\n"); + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + } + /* got the reply, no need to come here again */ + icap->flags.wait_for_reply = 0; + icap->flags.got_reply = 1; + /* Next, gobble any data before the HTTP response starts */ + if (icap->enc.res_hdr > -1) + icap->bytes_to_gobble = icap->enc.res_hdr; + commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); +} + + +/* + * Gobble up (read) some bytes until we get to the start of the body + */ +static void +icapRespModGobble(int fd, void *data) +{ + IcapStateData *icap = data; + int len; + LOCAL_ARRAY(char, junk, SQUID_TCP_SO_RCVBUF); + debug(81, 3) ("icapRespModGobble: FD %d gobbling %d bytes\n", fd, + icap->bytes_to_gobble); + len = FD_READ_METHOD(fd, junk, icap->bytes_to_gobble); + debug(81, 3) ("icapRespModGobble: gobbled %d bytes\n", len); + if (len < 0) { + /* XXX error */ + abort(); + } + icap->bytes_to_gobble -= len; + if (icap->bytes_to_gobble) + commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); + else + icapReadReply(fd, icap); +} + + +static void +icapSendRespModDone(int fd, char *bufnotused, size_t size, int errflag, + void *data) +{ + IcapStateData *icap = data; + ErrorState *err; + + icap->flags.write_pending = 0; + debug(81, 5) ("icapSendRespModDone: 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 == COMM_ERR_CLOSING) + return; + if (errflag) { + if (cbdataValid(icap)) + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + else + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, NULL); + err->xerrno = errno; + storeEntryReset(icap->respmod.entry); + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + } + if (EBIT_TEST(icap->respmod.entry->flags, ENTRY_ABORTED)) { + debug(81, 3) ("icapSendRespModDone: Entry Aborded\n"); + comm_close(fd); + return; + } + if (icap->flags.send_zero_chunk) { + debug(81, + 3) ("icapSendRespModDone: I'm supposed to send zero chunk now\n"); + icap->flags.send_zero_chunk = 0; + icapSendRespMod(icap, NULL, 0, 1); + return; + } + if (icap->flags.wait_for_preview_reply || icap->flags.wait_for_reply) { + /* Schedule reading the ICAP response */ + debug(81, + 3) + ("icapSendRespModDone: FD %d: commSetSelect on read icapRespModReadReply.\n", + fd); + commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); +#if 1 + commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); + commSetDefer(fd, fwdCheckDeferRead, icap->respmod.entry); +#else + if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { + /* + * Set the read timeout only after all data has been sent + * or we are waiting for a preview response + * If the ICAP server does not return any data till all data + * has been sent, we are likely to hit the timeout for large + * HTTP bodies + */ + commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); + } +#endif + } +} + +void +icapConnectOver(int fd, int status, void *data) +{ + ErrorState *err; + IcapStateData *icap = data; + debug(81, 3) ("icapConnectOver: FD %d, status=%d\n", fd, status); + icap->flags.connect_pending = 0; + if (status < 0) { + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + debug(81, 3) ("icapConnectOver: status < 0, unreachable=1\n"); + icapOptSetUnreachable(icap->current_service); + return; + } + fd_table[fd].pconn.uses++; + fd_table[fd].pconn.type = 2; + commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); +} + + + +IcapStateData * +icapRespModStart(icap_service_t type, request_t * request, StoreEntry * entry, + http_state_flags http_flags) +{ + IcapStateData *icap = NULL; + CNCB *theCallback = NULL; + icap_service *service = NULL; + + debug(81, 3) ("icapRespModStart: type=%d\n", (int) type); + assert(type >= 0 && type < ICAP_SERVICE_MAX); + + service = icapService(type, request); + if (!service) { + debug(81, 3) ("icapRespModStart: no service found\n"); + return NULL; /* no service found */ + } + if (service->unreachable) { + if (service->bypass) { + debug(81, + 5) + ("icapRespModStart: BYPASS because service unreachable: %s\n", + service->uri); + return NULL; + } else { + debug(81, + 5) + ("icapRespModStart: ERROR because service unreachable: %s\n", + service->uri); + return (IcapStateData *) - 1; + } + } + switch (type) { + /* TODO: When we support more than ICAP_SERVICE_RESPMOD_PRECACHE, we needs to change + * this switch, because callbacks isn't keep */ + case ICAP_SERVICE_RESPMOD_PRECACHE: + theCallback = icapConnectOver; + break; + default: + fatalf("icapRespModStart: unsupported service type '%s'\n", + icap_service_type_str[type]); + break; + } + + icap = icapAllocate(); + if (!icap) { + debug(81, 3) ("icapRespModStart: icapAllocate() failed\n"); + return NULL; + } + icap->request = requestLink(request); + icap->respmod.entry = entry; + if (entry) + storeLockObject(entry); + icap->http_flags = http_flags; + memBufDefInit(&icap->respmod.buffer); + memBufDefInit(&icap->chunk_buf); + + icap->current_service = service; + icap->preview_size = service->preview; + + /* + * Don't create socket to the icap server now, but only for the first + * packet receive from the http server. This will resolve all timeout + * between the web server and icap server. + */ + debug(81, 3) ("icapRespModStart: setting connect_requested to 0\n"); + icap->flags.connect_requested = 0; + + /* + * make a copy the HTTP response that we send to the ICAP server in + * case it turns out to be a 204 + */ +#ifdef SUPPORT_ICAP_204 + icap->flags.copy_response = 1; +#elif ICAP_PREVIEW + if (preview_size < 0 || !Config.icapcfg.preview_enable) + icap->flags.copy_response = 0; + else + icap->flags.copy_response = 1; +#else + icap->flags.copy_response = 0; +#endif + + statCounter.icap.all.requests++; + debug(81, 3) ("icapRespModStart: returning %p\n", icap); + return icap; +} + +static int +icapHttpReplyHdrState(IcapStateData * icap) +{ + assert(icap); + if (NULL == icap->httpState) + return 0; + return icap->httpState->reply_hdr_state; +} + +static void +icapProcessHttpReplyHeader(IcapStateData * icap, const char *buf, int size) +{ + if (NULL == icap->httpState) { + icap->httpState = cbdataAlloc(HttpStateData); + icap->httpState->request = requestLink(icap->request); + icap->httpState->orig_request = requestLink(icap->request); + icap->httpState->entry = icap->respmod.entry; + storeLockObject(icap->httpState->entry); /* lock it */ + } + httpProcessReplyHeader(icap->httpState, buf, size); + if (2 == icap->httpState->reply_hdr_state) + EBIT_CLR(icap->httpState->entry->flags, ENTRY_FWD_HDR_WAIT); +} + +/* + * icapRespModKeepAliveOrClose + * + * Called when we are done reading from the ICAP server. + * Either close the connection or keep it open for a future + * transaction. + */ +static void +icapRespModKeepAliveOrClose(IcapStateData * icap) +{ + int fd = icap->icap_fd; + if (fd < 0) + return; + debug(81, 3) ("%s:%d FD %d looks good, keeping alive\n", __FILE__, __LINE__, + fd); + commSetDefer(fd, NULL, NULL); + commSetTimeout(fd, -1, NULL, NULL); + commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); + comm_remove_close_handler(fd, icapStateFree, icap); + icap->icap_fd = -1; + if (!icap->flags.keep_alive) { + debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, + __LINE__); + comm_close(fd); + return; + } else { + pconnPush(fd, icap->current_service->hostname, icap->current_service->port, NULL, NULL, 0); + } +} + + + +/* + * copied from httpPconnTransferDone + * + */ +static int +icapPconnTransferDone(int fd, IcapStateData * icap) +{ + debug(81, 3) ("icapPconnTransferDone: FD %d\n", fd); + /* + * Be careful with 204 responses. Normally we are done when we + * see the zero-end chunk, but that won't happen for 204s, so we + * use an EOF indicator on the HTTP side instead. + */ + if (icap->flags.no_content && icap->flags.http_server_eof) { + debug(81, 5) ("icapPconnTransferDone: no content, ret 1\n"); + return 1; + } + if (icapHttpReplyHdrState(icap) != 2) { + debug(81, + 5) ("icapPconnTransferDone: didn't see end of HTTP hdrs, ret 0\n"); + return 0; + } + if (icap->enc.null_body > -1) { + debug(81, 5) ("icapPconnTransferDone: no message body, ret 1\n"); + return 1; + } + if (icap->chunk_size == -2) { //AI: was != -2 ; and change content with bottom + /* zero end chunk reached */ + debug(81, 5) ("icapPconnTransferDone: got zero end chunk\n"); + return 1; + } + debug(81, 5) ("icapPconnTransferDone: didnt get zero end chunk yet\n"); //AI: change with second top condition + + return 0; +} + +static int +icapExpectedHttpReplyHdrSize(IcapStateData * icap) +{ + if (icap->enc.res_body > -1 && icap->enc.res_hdr > -1) + return (icap->enc.res_body - icap->enc.res_hdr); + if (icap->enc.null_body > -1 && icap->enc.res_hdr > -1) + return icap->enc.null_body - icap->enc.res_hdr; + /*The case we did not get res_hdr ..... */ + if (icap->enc.res_body > -1) + return icap->enc.res_body; + if (icap->enc.null_body > -1) + return icap->enc.null_body; + return -1; +} + +/* + * copied from httpReadReply() + * + * by the time this is called, the ICAP headers have already + * been read. + */ +void +icapReadReply(int fd, void *data) +{ + IcapStateData *icap = data; + StoreEntry *entry = icap->respmod.entry; + const request_t *request = icap->request; + int len; + debug(81, 5) ("icapReadReply: FD %d: icap %p.\n", fd, data); + if (icap->flags.no_content && !icap->flags.http_server_eof) { //AI + + return; + } + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + comm_close(fd); + return; + } + errno = 0; + statCounter.syscalls.sock.reads++; + len = memBufRead(fd, &icap->chunk_buf); + debug(81, 5) ("icapReadReply: FD %d: len %d.\n", fd, len); + if (len > 0) { + fd_bytes(fd, len, FD_READ); + kb_incr(&statCounter.icap.all.kbytes_in, len); + commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); + if (icap->chunk_buf.size < icap->chunk_buf.capacity) { + *(icap->chunk_buf.buf + icap->chunk_buf.size) = '\0'; + debug(81, 9) ("{%s}\n", icap->chunk_buf.buf); + } + } + if (len <= 0) { + debug(81, 2) ("icapReadReply: FD %d: read failure: %s.\n", + fd, xstrerror()); + if (ignoreErrno(errno)) { + debug(81, 2) ("icapReadReply: FD %d: ignored errno\n", fd); + commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); + } else if (entry->mem_obj->inmem_hi == 0) { + ErrorState *err; + debug(81, 2) ("icapReadReply: FD %d: generating error page\n", fd); + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, (request_t *)request); + err->xerrno = errno; + errorAppendEntry(entry, err); + comm_close(fd); + } else { + debug(81, 2) ("icapReadReply: FD %d: just calling comm_close()\n", + fd); + comm_close(fd); + } + return; + } + if (icapReadReply2(icap) < 0) + comm_close(fd); +} + +static int +icapReadReply2(IcapStateData * icap) +{ + StoreEntry *entry = icap->respmod.entry; + const request_t *request = icap->request; + debug(81, 3) ("icapReadReply2\n"); + if (icap->chunk_buf.size == 0 && entry->mem_obj->inmem_hi == 0) { + ErrorState *err; + err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE, (request_t *)request); + err->xerrno = errno; + errorAppendEntry(entry, err); + icap->flags.http_server_eof = 1; + return -1; + } + if (icap->chunk_buf.size == 0) { + /* Retrieval done. */ + if (icapHttpReplyHdrState(icap) < 2) + icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, + icap->chunk_buf.size); + icap->flags.http_server_eof = 1; + icapReadReply3(icap); + return 0; + } + if (icapHttpReplyHdrState(icap) == 0) { + int expect = icapExpectedHttpReplyHdrSize(icap); + int so_far = icap->http_header_bytes_read_so_far; + int needed = expect - so_far; + debug(81, 3) ("expect=%d\n", expect); + debug(81, 3) ("so_far=%d\n", so_far); + debug(81, 3) ("needed=%d\n", needed); + assert(needed < 0 || needed >= 0); + if (0 > expect) { + icapProcessHttpReplyHeader(icap, + icap->chunk_buf.buf, icap->chunk_buf.size); + } else if (0 == expect) { + /* + * this icap reply doesn't give us new HTTP headers + * so we must copy them from our copy + */ + debug(81, 1) ("WARNING: untested code at %s:%d\n", __FILE__, + __LINE__); + if (icap->respmod.req_hdr_copy.size) { /*For HTTP 0.9 we do not have headers */ + storeAppend(entry, + icap->respmod.req_hdr_copy.buf, + icap->respmod.req_hdr_copy.size); + } + icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, + icap->chunk_buf.size); + assert(icapHttpReplyHdrState(icap) == 2); + icap->chunk_size = 0; /*we are ready to read chunks of data now.... */ + } else if (needed) { + icapProcessHttpReplyHeader(icap, + icap->chunk_buf.buf, icap->chunk_buf.size); + if (icap->chunk_buf.size >= needed) { + storeAppend(entry, icap->chunk_buf.buf, needed); + so_far += needed; + xmemmove(icap->chunk_buf.buf, + icap->chunk_buf.buf + needed, + icap->chunk_buf.size - needed); + icap->chunk_buf.size -= needed; + assert(icapHttpReplyHdrState(icap) == 2); + icap->chunk_size = 0; + } else { + /* + * We don't have the full HTTP reply headers yet, so keep + * the partial reply buffered in 'chunk_buf' and wait + * for more. + */ + debug(81, 3) ("We don't have full Http headers.Schedule a new read\n"); + commSetSelect(icap->icap_fd, COMM_SELECT_READ, icapReadReply, icap, 0); + } + } + icap->http_header_bytes_read_so_far = so_far; + } + debug(81, 3) ("%s:%d: icap->chunk_buf.size=%d\n", __FILE__, __LINE__, + (int) icap->chunk_buf.size); + debug(81, 3) ("%s:%d: flags.no_content=%d\n", __FILE__, __LINE__, + icap->flags.no_content); + if (icap->flags.no_content) { + /* data from http.c is not chunked */ + if (!EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + debug(81, 3) ("copying %d bytes from chunk_buf to entry\n", + icap->chunk_buf.size); + storeAppend(entry, icap->chunk_buf.buf, icap->chunk_buf.size); + icap->chunk_buf.size = 0; + } + } else if (2 == icapHttpReplyHdrState(icap)) { + if (icap->chunk_buf.size) + icapParseChunkedBody(icap, (STRCB *) storeAppend, entry); + } + icapReadReply3(icap); + return 0; +} + +static void +icapReadReply3(IcapStateData * icap) +{ + StoreEntry *entry = icap->respmod.entry; + int fd = icap->icap_fd; + debug(81, 3) ("icapReadReply3\n"); + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + debug(81, 3) ("icapReadReply3: Entry Aborded\n"); + if (icap->flags.no_content) + icapStateFree(-1, icap); + else + comm_close(fd); + } else if (icapPconnTransferDone(fd, icap)) { + storeComplete(entry); + if (icap->flags.no_content) + icapStateFree(-1, icap); + else { + icapRespModKeepAliveOrClose(icap); + icapStateFree(-1, icap); + } + } else if (!icap->flags.no_content) { + /* Wait for EOF condition */ + commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); + debug(81, + 3) + ("icapReadReply3: Going to read mode data throught icapReadReply\n"); + } else { + debug(81, 3) ("icapReadReply3: Nothing\n"); + } +} diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/80/7079fecb6888001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/80/7079fecb6888001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/80/7079fecb6888001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/80/7079fecb6888001b16add8656805b17d 2006-12-10 16:09:19.000000000 +0200 @@ -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); + } +} diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/8d/2036ac315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/8d/2036ac315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/8d/2036ac315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/8d/2036ac315c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,32 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/repl/heap/store_heap_replacement.c \ +$(ROOT)/repl/heap/store_repl_heap.c + +OBJS += \ +./repl/heap/store_heap_replacement.o \ +./repl/heap/store_repl_heap.o + +DEPS += \ +${addprefix ./repl/heap/, \ +store_heap_replacement.d \ +store_repl_heap.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +repl/heap/%.o: $(ROOT)/repl/heap/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/8f/4005d6315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/8f/4005d6315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/8f/4005d6315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/8f/4005d6315c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,41 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/fs/aufs/aiops.c \ +$(ROOT)/fs/aufs/aiops_win32.c \ +$(ROOT)/fs/aufs/async_io.c \ +$(ROOT)/fs/aufs/store_dir_aufs.c \ +$(ROOT)/fs/aufs/store_io_aufs.c + +OBJS += \ +./fs/aufs/aiops.o \ +./fs/aufs/aiops_win32.o \ +./fs/aufs/async_io.o \ +./fs/aufs/store_dir_aufs.o \ +./fs/aufs/store_io_aufs.o + +DEPS += \ +${addprefix ./fs/aufs/, \ +aiops.d \ +aiops_win32.d \ +async_io.d \ +store_dir_aufs.d \ +store_io_aufs.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +fs/aufs/%.o: $(ROOT)/fs/aufs/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/95/4058dd315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/95/4058dd315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/95/4058dd315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/95/4058dd315c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,29 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/auth/ntlm/auth_ntlm.c + +OBJS += \ +./auth/ntlm/auth_ntlm.o + +DEPS += \ +${addprefix ./auth/ntlm/, \ +auth_ntlm.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +auth/ntlm/%.o: $(ROOT)/auth/ntlm/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/9b/80a8c0315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/9b/80a8c0315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/9b/80a8c0315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/9b/80a8c0315c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,29 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/fs/null/store_null.c + +OBJS += \ +./fs/null/store_null.o + +DEPS += \ +${addprefix ./fs/null/, \ +store_null.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +fs/null/%.o: $(ROOT)/fs/null/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/9c/40b3b6286888001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/9c/40b3b6286888001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/9c/40b3b6286888001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/9c/40b3b6286888001b16add8656805b17d 2006-12-10 16:09:19.000000000 +0200 @@ -0,0 +1,1169 @@ + +/* + * $Id: main.c,v 1.392 2006/10/23 11:22:21 hno Exp $ + * + * DEBUG: section 1 Startup and Main Loop + * AUTHOR: Harvest Derived + * + * 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" + +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) +#include +#include +static int opt_install_service = FALSE; +static int opt_remove_service = FALSE; +static int opt_signal_service = FALSE; +static int opt_command_line = FALSE; +extern void WIN32_svcstatusupdate(DWORD, DWORD); +void WINAPI WIN32_svcHandler(DWORD); +#endif + +/* for error reporting from xmalloc and friends */ +extern void (*failure_notify) (const char *); + +static char *opt_syslog_facility = NULL; +static int icpPortNumOverride = 1; /* Want to detect "-u 0" */ +static int configured_once = 0; +#if MALLOC_DBG +static int malloc_debug_level = 0; +#endif +static volatile int do_reconfigure = 0; +static volatile int do_rotate = 0; +static volatile int do_shutdown = 0; + +static void mainRotate(void); +static void mainReconfigure(void); +static void mainInitialize(void); +static void usage(void); +static void mainParseOptions(int, char **); +static void sendSignal(void); +static void serverConnectionsOpen(void); +static void watch_child(char **); +static void setEffectiveUser(void); +#if MEM_GEN_TRACE +extern void log_trace_done(); +extern void log_trace_init(char *); +#endif +static EVH SquidShutdown; +static void mainSetCwd(void); +static int checkRunningPid(void); + +#ifndef _SQUID_MSWIN_ +static const char *squid_start_script = "squid_start"; +#endif + +#if TEST_ACCESS +#include "test_access.c" +#endif + +static void +usage(void) +{ + fprintf(stderr, +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + "Usage: %s [-hirvzCDFRYX] [-d level] [-s | -l facility] [-f config-file] [-u port] [-k signal] [-n name] [-O command-line]\n" +#else + "Usage: %s [-hvzCDFNRYX] [-d level] [-s | -l facility] [-f config-file] [-u port] [-k signal]\n" +#endif + " -d level Write debugging to stderr also.\n" + " -f file Use given config-file instead of\n" + " %s\n" + " -h Print help message.\n" +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + " -i Installs as a Windows Service (see -n option).\n" +#endif + " -k reconfigure|rotate|shutdown|interrupt|kill|debug|check|parse\n" + " Parse configuration file, then send signal to \n" + " running copy (except -k parse) and exit.\n" +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + " -n name Specify Windows Service name to use for service operations\n" + " default is: " _WIN_SQUID_DEFAULT_SERVICE_NAME ".\n" + " -r Removes a Windows Service (see -n option).\n" +#endif + " -s | -l facility\n" + " Enable logging to syslog.\n" + " -u port Specify ICP port number (default: %d), disable with 0.\n" + " -v Print version.\n" + " -z Create swap directories\n" + " -C Do not catch fatal signals.\n" + " -D Disable initial DNS tests.\n" + " -F Don't serve any requests until store is rebuilt.\n" + " -N No daemon mode.\n" +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + " -O options\n" + " Set Windows Service Command line options in Registry.\n" +#endif + " -R Do not set REUSEADDR on port.\n" + " -S Double-check swap during rebuild.\n" + " -X Force full debugging.\n" + " -Y Only return UDP_HIT or UDP_MISS_NOFETCH during fast reload.\n", + appname, DefaultConfigFile, CACHE_ICP_PORT); + exit(1); +} + +static void +mainParseOptions(int argc, char *argv[]) +{ + extern char *optarg; + int c; + +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + while ((c = getopt(argc, argv, "CDFO:RSYXd:f:hik:m::n:rsl:u:vz?")) != -1) { +#else + while ((c = getopt(argc, argv, "CDFNRSYXd:f:hk:m::sl:u:vz?")) != -1) { +#endif + switch (c) { + case 'C': + opt_catch_signals = 0; + break; + case 'D': + opt_dns_tests = 0; + break; + case 'F': + opt_foreground_rebuild = 1; + break; + case 'N': + opt_no_daemon = 1; + break; +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + case 'O': + opt_command_line = 1; + WIN32_Command_Line = xstrdup(optarg); + break; +#endif + case 'R': + opt_reuseaddr = 0; + break; + case 'S': + opt_store_doublecheck = 1; + break; + case 'X': + /* force full debugging */ + sigusr2_handle(SIGUSR2); + break; + case 'Y': + opt_reload_hit_only = 1; + break; + case 'd': + opt_debug_stderr = atoi(optarg); + break; + case 'f': + xfree(ConfigFile); + ConfigFile = xstrdup(optarg); + break; + case 'h': + usage(); + break; +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + case 'i': + opt_install_service = TRUE; + break; +#endif + case 'k': + if ((int) strlen(optarg) < 1) + usage(); + if (!strncmp(optarg, "reconfigure", strlen(optarg))) + opt_send_signal = SIGHUP; + else if (!strncmp(optarg, "rotate", strlen(optarg))) +#ifdef _SQUID_LINUX_THREADS_ + opt_send_signal = SIGQUIT; +#else + opt_send_signal = SIGUSR1; +#endif + else if (!strncmp(optarg, "debug", strlen(optarg))) +#ifdef _SQUID_LINUX_THREADS_ + opt_send_signal = SIGTRAP; +#else + opt_send_signal = SIGUSR2; +#endif + else if (!strncmp(optarg, "shutdown", strlen(optarg))) + opt_send_signal = SIGTERM; + else if (!strncmp(optarg, "interrupt", strlen(optarg))) + opt_send_signal = SIGINT; + else if (!strncmp(optarg, "kill", strlen(optarg))) + opt_send_signal = SIGKILL; + else if (!strncmp(optarg, "check", strlen(optarg))) + opt_send_signal = 0; /* SIGNULL */ + else if (!strncmp(optarg, "parse", strlen(optarg))) + opt_parse_cfg_only = 1; /* parse cfg file only */ + else + usage(); + break; + case 'm': + if (optarg) { +#if MALLOC_DBG + malloc_debug_level = atoi(optarg); + /* NOTREACHED */ + break; +#else + fatal("Need to add -DMALLOC_DBG when compiling to use -mX option"); + /* NOTREACHED */ +#endif + } else { +#if XMALLOC_TRACE + xmalloc_trace = !xmalloc_trace; +#else + fatal("Need to configure --enable-xmalloc-debug-trace to use -m option"); +#endif + } +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + case 'n': + xfree(WIN32_Service_name); + WIN32_Service_name = xstrdup(optarg); + opt_signal_service = TRUE; + break; + case 'r': + opt_remove_service = TRUE; + break; +#endif + case 'l': + opt_syslog_facility = xstrdup(optarg); + case 's': +#if HAVE_SYSLOG + _db_set_syslog(opt_syslog_facility); + break; +#else + fatal("Logging to syslog not available on this platform"); + /* NOTREACHED */ +#endif + case 'u': + icpPortNumOverride = atoi(optarg); + if (icpPortNumOverride < 0) + icpPortNumOverride = 0; + break; + case 'v': + printf("Squid Cache: Version %s\nconfigure options: %s\n", version_string, SQUID_CONFIGURE_OPTIONS); +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + printf("Compiled as Windows System Service.\n"); +#endif + exit(0); + /* NOTREACHED */ + case 'z': + opt_create_swap_dirs = 1; + break; + case '?': + default: + usage(); + break; + } + } +} + +/* ARGSUSED */ +void +rotate_logs(int sig) +{ + do_rotate = 1; +#ifndef _SQUID_MSWIN_ +#if !HAVE_SIGACTION + signal(sig, rotate_logs); +#endif +#endif +} + +/* ARGSUSED */ +void +reconfigure(int sig) +{ + do_reconfigure = 1; +#ifndef _SQUID_MSWIN_ +#if !HAVE_SIGACTION + signal(sig, reconfigure); +#endif +#endif +} + +void +shut_down(int sig) +{ + do_shutdown = sig == SIGINT ? -1 : 1; +#ifndef _SQUID_MSWIN_ +#ifdef KILL_PARENT_OPT + if (getppid() > 1) { + debug(1, 1) ("Killing RunCache, pid %ld\n", (long) getppid()); + if (kill(getppid(), sig) < 0) + debug(1, 1) ("kill %ld: %s\n", (long) getppid(), xstrerror()); + } +#endif +#if SA_RESETHAND == 0 + signal(SIGTERM, SIG_DFL); + signal(SIGINT, SIG_DFL); +#endif +#endif +} + +static void +serverConnectionsOpen(void) +{ + clientOpenListenSockets(); + icpConnectionsOpen(); +#if USE_HTCP + htcpInit(); +#endif +#ifdef SQUID_SNMP + snmpConnectionOpen(); +#endif +#if USE_WCCP + wccpConnectionOpen(); +#endif +#if USE_WCCPv2 + wccp2ConnectionOpen(); +#endif + clientdbInit(); + icmpOpen(); + netdbInit(); + asnInit(); + peerSelectInit(); +#if USE_CARP + carpInit(); +#endif + peerSourceHashInit(); + peerUserHashInit(); + peerMonitorInit(); +} + +void +serverConnectionsClose(void) +{ + assert(shutting_down || reconfiguring); + clientHttpConnectionsClose(); + icpConnectionShutdown(); +#if USE_HTCP + htcpSocketShutdown(); +#endif + icmpClose(); +#ifdef SQUID_SNMP + snmpConnectionShutdown(); +#endif +#if USE_WCCP + wccpConnectionClose(); +#endif +#if USE_WCCPv2 + wccp2ConnectionClose(); +#endif + asnFreeMemory(); +} + +static void +mainReconfigure(void) +{ + debug(1, 1) ("Reconfiguring Squid Cache (version %s)...\n", version_string); + reconfiguring = 1; + /* Already called serverConnectionsClose and ipcacheShutdownServers() */ + serverConnectionsClose(); + icpConnectionClose(); +#if USE_HTCP + htcpSocketClose(); +#endif +#ifdef SQUID_SNMP + snmpConnectionClose(); +#endif +#if USE_DNSSERVERS + dnsShutdown(); +#else + idnsShutdown(); +#endif +#ifdef HS_FEAT_ICAP + icapClose(); +#endif + redirectShutdown(); + locationRewriteShutdown(); + authenticateShutdown(); + externalAclShutdown(); + storeDirCloseSwapLogs(); + storeLogClose(); + accessLogClose(); + useragentLogClose(); + refererCloseLog(); + errorClean(); + enter_suid(); /* root to read config file */ + parseConfigFile(ConfigFile); + setUmask(Config.umask); + setEffectiveUser(); + _db_init(Config.Log.log, Config.debugOptions); + ipcache_restart(); /* clear stuck entries */ + authenticateUserCacheRestart(); /* clear stuck ACL entries */ + fqdncache_restart(); /* sigh, fqdncache too */ + parseEtcHosts(); + errorInitialize(); /* reload error pages */ + accessLogInit(); + storeLogOpen(); + useragentOpenLog(); + refererOpenLog(); +#if USE_DNSSERVERS + dnsInit(); +#else + idnsInit(); +#endif + redirectInit(); + locationRewriteInit(); +#ifdef HS_FEAT_ICAP + icapInit(); +#endif + authenticateInit(&Config.authConfig); + externalAclInit(); +#if USE_WCCP + wccpInit(); +#endif +#if USE_WCCPv2 + wccp2Init(); +#endif + serverConnectionsOpen(); + neighbors_init(); + storeDirOpenSwapLogs(); + mimeInit(Config.mimeTablePathname); + if (Config.onoff.announce) { + if (!eventFind(start_announce, NULL)) + eventAdd("start_announce", start_announce, NULL, 3600.0, 1); + } else { + if (eventFind(start_announce, NULL)) + eventDelete(start_announce, NULL); + } + eventCleanup(); + writePidFile(); /* write PID file */ + debug(1, 1) ("Ready to serve requests.\n"); + reconfiguring = 0; + peerMonitorInit(); +} + +static void +mainRotate(void) +{ + icmpClose(); +#if USE_DNSSERVERS + dnsShutdown(); +#endif + redirectShutdown(); + locationRewriteShutdown(); + authenticateShutdown(); + externalAclShutdown(); + _db_rotate_log(); /* cache.log */ + storeDirWriteCleanLogs(1); + storeDirSync(); /* Flush pending I/O ops */ + storeLogRotate(); /* store.log */ + accessLogRotate(); /* access.log */ + useragentRotateLog(); /* useragent.log */ + refererRotateLog(); /* referer.log */ +#if WIP_FWD_LOG + fwdLogRotate(); +#endif + icmpOpen(); +#if USE_DNSSERVERS + dnsInit(); +#endif + redirectInit(); + locationRewriteInit(); + authenticateInit(&Config.authConfig); + externalAclInit(); +} + +static void +setEffectiveUser(void) +{ + keepCapabilities(); + leave_suid(); /* Run as non privilegied user */ +#ifdef _SQUID_OS2_ + return; +#endif + if (geteuid() == 0) { + debug(0, 0) ("Squid is not safe to run as root! If you must\n"); + debug(0, 0) ("start Squid as root, then you must configure\n"); + debug(0, 0) ("it to run as a non-priveledged user with the\n"); + debug(0, 0) ("'cache_effective_user' option in the config file.\n"); + fatal("Don't run Squid as root, set 'cache_effective_user'!"); + } +} + +static void +mainSetCwd(void) +{ + char pathbuf[MAXPATHLEN]; + if (Config.coredump_dir) { + if (0 == strcmp("none", Config.coredump_dir)) { + (void) 0; + } else if (chdir(Config.coredump_dir) == 0) { + debug(0, 1) ("Set Current Directory to %s\n", Config.coredump_dir); + return; + } else { + debug(50, 0) ("chdir: %s: %s\n", Config.coredump_dir, xstrerror()); + } + } + /* If we don't have coredump_dir or couldn't cd there, report current dir */ + if (getcwd(pathbuf, MAXPATHLEN)) { + debug(0, 1) ("Current Directory is %s\n", pathbuf); + } else { + debug(50, 0) ("WARNING: Can't find current directory, getcwd: %s\n", xstrerror()); + } +} + +static void +mainInitialize(void) +{ + /* chroot if configured to run inside chroot */ + if (Config.chroot_dir && chroot(Config.chroot_dir)) { + fatal("failed to chroot"); + } + if (opt_catch_signals) { + squid_signal(SIGSEGV, death, SA_NODEFER | SA_RESETHAND); + squid_signal(SIGBUS, death, SA_NODEFER | SA_RESETHAND); + } + squid_signal(SIGPIPE, SIG_IGN, SA_RESTART); + squid_signal(SIGCHLD, sig_child, SA_NODEFER | SA_RESTART); + + setEffectiveUser(); + if (icpPortNumOverride != 1) + Config.Port.icp = (u_short) icpPortNumOverride; + + _db_init(Config.Log.log, Config.debugOptions); + fd_open(fileno(debug_log), FD_LOG, Config.Log.log); +#if MEM_GEN_TRACE + log_trace_init("/tmp/squid.alloc"); +#endif + debug(1, 0) ("Starting Squid Cache version %s for %s...\n", + version_string, + CONFIG_HOST_TYPE); +#ifdef _SQUID_WIN32_ + if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) { + debug(1, 0) ("Running as %s Windows System Service on %s\n", WIN32_Service_name, WIN32_OS_string); + debug(1, 0) ("Service command line is: %s\n", WIN32_Service_Command_Line); + } else + debug(1, 0) ("Running on %s\n", WIN32_OS_string); +#endif + debug(1, 1) ("Process ID %d\n", (int) getpid()); + debug(1, 1) ("With %d file descriptors available\n", Squid_MaxFD); +#ifdef _SQUID_MSWIN_ + debug(1, 1) ("With %d CRT stdio descriptors available\n", _getmaxstdio()); + if (WIN32_Socks_initialized) + debug(1, 1) ("Windows sockets initialized\n"); +#endif + + comm_select_postinit(); + if (!configured_once) + disk_init(); /* disk_init must go before ipcache_init() */ + ipcache_init(); + fqdncache_init(); + parseEtcHosts(); +#if USE_DNSSERVERS + dnsInit(); +#else + idnsInit(); +#endif + redirectInit(); + locationRewriteInit(); + errorMapInit(); +#ifdef HS_FEAT_ICAP + icapInit(); +#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + useragentOpenLog(); + refererOpenLog(); + httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */ + httpReplyInitModule(); /* must go before accepting replies */ + errorInitialize(); + accessLogInit(); +#if USE_IDENT + identInit(); +#endif +#ifdef SQUID_SNMP + snmpInit(); +#endif +#if MALLOC_DBG + malloc_debug(0, malloc_debug_level); +#endif + + if (!configured_once) { +#if USE_UNLINKD + unlinkdInit(); +#endif + urlInitialize(); + cachemgrInit(); + statInit(); + storeInit(); + mainSetCwd(); + /* after this point we want to see the mallinfo() output */ + do_mallinfo = 1; + mimeInit(Config.mimeTablePathname); + pconnInit(); + refreshInit(); +#if DELAY_POOLS + delayPoolsInit(); +#endif + fwdInit(); + } +#if USE_WCCP + wccpInit(); +#endif +#if USE_WCCPv2 + wccp2Init(); +#endif + serverConnectionsOpen(); + neighbors_init(); + if (Config.chroot_dir) + no_suid(); + if (!configured_once) + writePidFile(); /* write PID file */ + +#ifdef _SQUID_LINUX_THREADS_ + squid_signal(SIGQUIT, rotate_logs, SA_RESTART); + squid_signal(SIGTRAP, sigusr2_handle, SA_RESTART); +#else + squid_signal(SIGUSR1, rotate_logs, SA_RESTART); + squid_signal(SIGUSR2, sigusr2_handle, SA_RESTART); +#endif + squid_signal(SIGHUP, reconfigure, SA_RESTART); + squid_signal(SIGTERM, shut_down, SA_NODEFER | SA_RESETHAND | SA_RESTART); + squid_signal(SIGINT, shut_down, SA_NODEFER | SA_RESETHAND | SA_RESTART); + memCheckInit(); + debug(1, 1) ("Ready to serve requests.\n"); + if (!configured_once) { + eventAdd("storeMaintain", storeMaintainSwapSpace, NULL, 1.0, 1); + if (Config.onoff.announce) + eventAdd("start_announce", start_announce, NULL, 3600.0, 1); + eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1); + eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 15.0, 1); + } + configured_once = 1; +} + +#if USE_WIN32_SERVICE +/* When USE_WIN32_SERVICE is defined, the main function is placed in win32.c */ +void WINAPI +SquidWinSvcMain(int argc, char **argv) +{ + SquidMain(argc, argv); +} + +int +SquidMain(int argc, char **argv) +#else +int +main(int argc, char **argv) +#endif +{ + int errcount = 0; + int loop_delay; +#ifdef _SQUID_WIN32_ + int WIN32_init_err; +#endif + +#if HAVE_SBRK + sbrk_start = sbrk(0); +#endif + + debug_log = stderr; + +#ifdef _SQUID_WIN32_ + if ((WIN32_init_err = WIN32_Subsystem_Init(&argc, &argv))) + return WIN32_init_err; +#endif + + /* call mallopt() before anything else */ +#if HAVE_MALLOPT +#ifdef M_GRAIN + /* Round up all sizes to a multiple of this */ + mallopt(M_GRAIN, 16); +#endif +#ifdef M_MXFAST + /* biggest size that is considered a small block */ + mallopt(M_MXFAST, 256); +#endif +#ifdef M_NBLKS + /* allocate this many small blocks at once */ + mallopt(M_NLBLKS, 32); +#endif +#endif /* HAVE_MALLOPT */ + + memset(&local_addr, '\0', sizeof(struct in_addr)); + safe_inet_addr(localhost, &local_addr); + memset(&any_addr, '\0', sizeof(struct in_addr)); + safe_inet_addr("0.0.0.0", &any_addr); + memset(&no_addr, '\0', sizeof(struct in_addr)); + safe_inet_addr("255.255.255.255", &no_addr); + squid_srandom(time(NULL)); + + getCurrentTime(); + squid_start = current_time; + failure_notify = fatal_dump; + +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + WIN32_svcstatusupdate(SERVICE_START_PENDING, 10000); +#endif + mainParseOptions(argc, argv); + +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + if (opt_install_service) { + WIN32_InstallService(); + return 0; + } + if (opt_remove_service) { + WIN32_RemoveService(); + return 0; + } + if (opt_command_line) { + WIN32_SetServiceCommandLine(); + return 0; + } +#endif + + /* parse configuration file + * note: in "normal" case this used to be called from mainInitialize() */ + { + int parse_err; + if (!ConfigFile) + ConfigFile = xstrdup(DefaultConfigFile); + assert(!configured_once); +#if USE_LEAKFINDER + leakInit(); +#endif + memInit(); + cbdataInit(); + eventInit(); /* eventInit() is required for config parsing */ + storeFsInit(); /* required for config parsing */ + authenticateSchemeInit(); /* required for config parsing */ + parse_err = parseConfigFile(ConfigFile); + + if (opt_parse_cfg_only) + return parse_err; + } + setUmask(Config.umask); + if (-1 == opt_send_signal) + if (checkRunningPid()) + exit(1); + + /* Make sure the OS allows core dumps if enabled in squid.conf */ + enableCoredumps(); + +#if TEST_ACCESS + comm_init(); + comm_select_init(); + mainInitialize(); + test_access(); + return 0; +#endif + + /* send signal to running copy and exit */ + if (opt_send_signal != -1) { + /* chroot if configured to run inside chroot */ + if (Config.chroot_dir) { + if (chroot(Config.chroot_dir)) + fatal("failed to chroot"); + no_suid(); + } else { + leave_suid(); + } + sendSignal(); + /* NOTREACHED */ + } + if (opt_create_swap_dirs) { + /* chroot if configured to run inside chroot */ + if (Config.chroot_dir && chroot(Config.chroot_dir)) { + fatal("failed to chroot"); + } + setEffectiveUser(); + debug(0, 0) ("Creating Swap Directories\n"); + storeCreateSwapDirectories(); + return 0; + } + if (!opt_no_daemon) + watch_child(argv); + setMaxFD(); + + /* init comm module */ + comm_init(); + comm_select_init(); + + if (opt_no_daemon) { + /* we have to init fdstat here. */ + fd_open(0, FD_LOG, "stdin"); + fd_open(1, FD_LOG, "stdout"); + fd_open(2, FD_LOG, "stderr"); + } +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + WIN32_svcstatusupdate(SERVICE_START_PENDING, 10000); +#endif + mainInitialize(); + +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + WIN32_svcstatusupdate(SERVICE_RUNNING, 0); +#endif + + /* main loop */ + for (;;) { + if (do_reconfigure) { + mainReconfigure(); + do_reconfigure = 0; + } else if (do_rotate) { + mainRotate(); + do_rotate = 0; + } else if (do_shutdown) { + time_t wait = do_shutdown > 0 ? (int) Config.shutdownLifetime : 0; + debug(1, 1) ("Preparing for shutdown after %d requests\n", + statCounter.client_http.requests); + debug(1, 1) ("Waiting %d seconds for active connections to finish\n", + (int) wait); + do_shutdown = 0; + shutting_down = 1; +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + WIN32_svcstatusupdate(SERVICE_STOP_PENDING, (wait + 1) * 1000); +#endif + serverConnectionsClose(); + eventAdd("SquidShutdown", SquidShutdown, NULL, (double) (wait + 1), 1); + } + eventRun(); + if ((loop_delay = eventNextTime()) < 0) + loop_delay = 0; + if (debug_log_flush() && loop_delay > 1000) + loop_delay = 1000; + switch (comm_select(loop_delay)) { + case COMM_OK: + errcount = 0; /* reset if successful */ + break; + case COMM_ERROR: + errcount++; + debug(1, 0) ("Select loop Error. Retry %d\n", errcount); + if (errcount == 10) + fatal_dump("Select Loop failed!"); + break; + case COMM_TIMEOUT: + break; + case COMM_SHUTDOWN: + SquidShutdown(NULL); + break; + default: + fatal_dump("MAIN: Internal error -- this should never happen."); + break; + } + } + /* NOTREACHED */ + return 0; +} + +static void +sendSignal(void) +{ + pid_t pid; + debug_log = stderr; + pid = readPidFile(); + if (pid > 1) { +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + if (opt_signal_service) + WIN32_sendSignal(opt_send_signal); + else { +#endif +#if defined(_SQUID_MSWIN_) && defined(USE_WIN32_SERVICE) + fprintf(stderr, "%s: ERROR: Could not send ", appname); + fprintf(stderr, "signal to Squid Service:\n"); + fprintf(stderr, "missing -n command line switch.\n"); +#else + if (kill(pid, opt_send_signal) && + /* ignore permissions if just running check */ + !(opt_send_signal == 0 && errno == EPERM)) { + fprintf(stderr, "%s: ERROR: Could not send ", appname); + fprintf(stderr, "signal %d to process %d: %s\n", + opt_send_signal, (int) pid, xstrerror()); +#endif + exit(1); + } +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_CYGWIN_) + } +#endif + } else { + fprintf(stderr, "%s: ERROR: No running copy\n", appname); + exit(1); + } + /* signal successfully sent */ + exit(0); +} + +#ifndef _SQUID_MSWIN_ +/* + * This function is run when Squid is in daemon mode, just + * before the parent forks and starts up the child process. + * It can be used for admin-specific tasks, such as notifying + * someone that Squid is (re)started. + */ +static void +mainStartScript(const char *prog) +{ + char script[SQUID_MAXPATHLEN]; + char *t; + size_t sl = 0; + pid_t cpid; + pid_t rpid; + xstrncpy(script, prog, MAXPATHLEN); + if ((t = strrchr(script, '/'))) { + *(++t) = '\0'; + sl = strlen(script); + } + xstrncpy(&script[sl], squid_start_script, MAXPATHLEN - sl); + if ((cpid = fork()) == 0) { + /* child */ + execl(script, squid_start_script, NULL); + _exit(0); + } else { + do { +#ifdef _SQUID_NEXT_ + union wait status; + rpid = wait3(&status, 0, NULL); +#else + int status; + rpid = waitpid(-1, &status, 0); +#endif + } while (rpid != cpid); + } +} +#endif + +static int +checkRunningPid(void) +{ + pid_t pid; + debug_log = stderr; + if (strcmp(Config.pidFilename, "none") == 0) { + debug(0, 1) ("No pid_filename specified. Trusting you know what you are doing.\n"); + return 0; + } + pid = readPidFile(); + if (pid < 2) + return 0; + if (kill(pid, 0) < 0) + return 0; + debug(0, 0) ("Squid is already running! Process ID %ld\n", (long int) pid); + return 1; +} + +static void +watch_child(char *argv[]) +{ +#ifndef _SQUID_MSWIN_ + char *prog; + int failcount = 0; + time_t start; + time_t stop; +#ifdef _SQUID_NEXT_ + union wait status; +#else + int status; +#endif + pid_t pid; +#ifdef TIOCNOTTY + int i; +#endif + int nullfd; + if (*(argv[0]) == '(') + return; + openlog(appname, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4); + if ((pid = fork()) < 0) + syslog(LOG_ALERT, "fork failed: %s", xstrerror()); + else if (pid > 0) + exit(0); + if (setsid() < 0) + syslog(LOG_ALERT, "setsid failed: %s", xstrerror()); + closelog(); +#ifdef TIOCNOTTY + if ((i = open("/dev/tty", O_RDWR | O_TEXT)) >= 0) { + ioctl(i, TIOCNOTTY, NULL); + close(i); + } +#endif + + + /* + * RBCOLLINS - if cygwin stackdumps when squid is run without + * -N, check the cygwin1.dll version, it needs to be AT LEAST + * 1.1.3. execvp had a bit overflow error in a loop.. + */ + /* Connect stdio to /dev/null in daemon mode */ + nullfd = open(_PATH_DEVNULL, O_RDWR | O_TEXT); + if (nullfd < 0) + fatalf(_PATH_DEVNULL " %s\n", xstrerror()); + dup2(nullfd, 0); + if (opt_debug_stderr < 0) { + dup2(nullfd, 1); + dup2(nullfd, 2); + } + if (nullfd > 2) + close(nullfd); + for (;;) { + mainStartScript(argv[0]); + if ((pid = fork()) == 0) { + /* child */ + openlog(appname, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4); + prog = xstrdup(argv[0]); + argv[0] = xstrdup("(squid)"); + execvp(prog, argv); + syslog(LOG_ALERT, "execvp failed: %s", xstrerror()); + } + /* parent */ + openlog(appname, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4); + syslog(LOG_NOTICE, "Squid Parent: child process %d started", pid); + time(&start); + squid_signal(SIGINT, SIG_IGN, SA_RESTART); +#ifdef _SQUID_NEXT_ + pid = wait3(&status, 0, NULL); +#else + pid = waitpid(-1, &status, 0); +#endif + time(&stop); + if (WIFEXITED(status)) { + syslog(LOG_NOTICE, + "Squid Parent: child process %d exited with status %d", + pid, WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + syslog(LOG_NOTICE, + "Squid Parent: child process %d exited due to signal %d", + pid, WTERMSIG(status)); + } else { + syslog(LOG_NOTICE, "Squid Parent: child process %d exited", pid); + } + if (stop - start < 10) + failcount++; + else + failcount = 0; + if (failcount == 5) { + syslog(LOG_ALERT, "Exiting due to repeated, frequent failures"); + exit(1); + } + if (WIFEXITED(status)) + if (WEXITSTATUS(status) == 0) + exit(0); + if (WIFSIGNALED(status)) { + switch (WTERMSIG(status)) { + case SIGKILL: + exit(0); + break; + default: + break; + } + } + squid_signal(SIGINT, SIG_DFL, SA_RESTART); + sleep(3); + } + /* NOTREACHED */ +#endif +} + +static void +SquidShutdown(void *unused) +{ +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + WIN32_svcstatusupdate(SERVICE_STOP_PENDING, 10000); +#endif + debug(1, 1) ("Shutting down...\n"); +#if USE_DNSSERVERS + dnsShutdown(); +#else + idnsShutdown(); +#endif + redirectShutdown(); + externalAclShutdown(); + locationRewriteShutdown(); + icpConnectionClose(); +#if USE_HTCP + htcpSocketClose(); +#endif +#ifdef SQUID_SNMP + snmpConnectionClose(); +#endif +#if USE_WCCP + wccpConnectionClose(); +#endif +#if USE_WCCPv2 + wccp2ConnectionClose(); +#endif + releaseServerSockets(); + commCloseAllSockets(); + authenticateShutdown(); +#if USE_UNLINKD + unlinkdClose(); +#endif +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + WIN32_svcstatusupdate(SERVICE_STOP_PENDING, 10000); +#endif + storeDirSync(); /* Flush pending object writes/unlinks */ + storeDirWriteCleanLogs(0); + PrintRusage(); + dumpMallocStats(); + storeDirSync(); /* Flush log writes */ + storeLogClose(); + accessLogClose(); + useragentLogClose(); + refererCloseLog(); +#if WIP_FWD_LOG + fwdUninit(); +#endif + storeDirSync(); /* Flush log close */ + storeFsDone(); +#if LEAK_CHECK_MODE + configFreeMemory(); + storeFreeMemory(); + /*stmemFreeMemory(); */ + netdbFreeMemory(); + ipcacheFreeMemory(); + fqdncacheFreeMemory(); + asnFreeMemory(); + clientdbFreeMemory(); + httpHeaderCleanModule(); + statFreeMemory(); + eventFreeMemory(); + mimeFreeMemory(); + errorClean(); +#endif +#if !XMALLOC_TRACE + if (opt_no_daemon) { + fd_close(0); + fd_close(1); + fd_close(2); + } +#endif + comm_select_shutdown(); + fdDumpOpen(); + fdFreeMemory(); + memClean(); +#if XMALLOC_TRACE + xmalloc_find_leaks(); + debug(1, 0) ("Memory used after shutdown: %d\n", xmalloc_total); +#endif +#if MEM_GEN_TRACE + log_trace_done(); +#endif + if (Config.pidFilename && strcmp(Config.pidFilename, "none") != 0) { + enter_suid(); + safeunlink(Config.pidFilename, 0); + leave_suid(); + } + debug(1, 1) ("Squid Cache (Version %s): Exiting normally.\n", + version_string); + if (debug_log) + fclose(debug_log); + exit(0); +} diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/a6/3073a52f6888001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/a6/3073a52f6888001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/a6/3073a52f6888001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/a6/3073a52f6888001b16add8656805b17d 2006-12-10 18:04:47.000000000 +0200 @@ -0,0 +1,1169 @@ + +/* + * $Id: main.c,v 1.392 2006/10/23 11:22:21 hno Exp $ + * + * DEBUG: section 1 Startup and Main Loop + * AUTHOR: Harvest Derived + * + * 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" + +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) +#include +#include +static int opt_install_service = FALSE; +static int opt_remove_service = FALSE; +static int opt_signal_service = FALSE; +static int opt_command_line = FALSE; +extern void WIN32_svcstatusupdate(DWORD, DWORD); +void WINAPI WIN32_svcHandler(DWORD); +#endif + +/* for error reporting from xmalloc and friends */ +extern void (*failure_notify) (const char *); + +static char *opt_syslog_facility = NULL; +static int icpPortNumOverride = 1; /* Want to detect "-u 0" */ +static int configured_once = 0; +#if MALLOC_DBG +static int malloc_debug_level = 0; +#endif +static volatile int do_reconfigure = 0; +static volatile int do_rotate = 0; +static volatile int do_shutdown = 0; + +static void mainRotate(void); +static void mainReconfigure(void); +static void mainInitialize(void); +static void usage(void); +static void mainParseOptions(int, char **); +static void sendSignal(void); +static void serverConnectionsOpen(void); +static void watch_child(char **); +static void setEffectiveUser(void); +#if MEM_GEN_TRACE +extern void log_trace_done(); +extern void log_trace_init(char *); +#endif +static EVH SquidShutdown; +static void mainSetCwd(void); +static int checkRunningPid(void); + +#ifndef _SQUID_MSWIN_ +static const char *squid_start_script = "squid_start"; +#endif + +#if TEST_ACCESS +#include "test_access.c" +#endif + +static void +usage(void) +{ + fprintf(stderr, +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + "Usage: %s [-hirvzCDFRYX] [-d level] [-s | -l facility] [-f config-file] [-u port] [-k signal] [-n name] [-O command-line]\n" +#else + "Usage: %s [-hvzCDFNRYX] [-d level] [-s | -l facility] [-f config-file] [-u port] [-k signal]\n" +#endif + " -d level Write debugging to stderr also.\n" + " -f file Use given config-file instead of\n" + " %s\n" + " -h Print help message.\n" +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + " -i Installs as a Windows Service (see -n option).\n" +#endif + " -k reconfigure|rotate|shutdown|interrupt|kill|debug|check|parse\n" + " Parse configuration file, then send signal to \n" + " running copy (except -k parse) and exit.\n" +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + " -n name Specify Windows Service name to use for service operations\n" + " default is: " _WIN_SQUID_DEFAULT_SERVICE_NAME ".\n" + " -r Removes a Windows Service (see -n option).\n" +#endif + " -s | -l facility\n" + " Enable logging to syslog.\n" + " -u port Specify ICP port number (default: %d), disable with 0.\n" + " -v Print version.\n" + " -z Create swap directories\n" + " -C Do not catch fatal signals.\n" + " -D Disable initial DNS tests.\n" + " -F Don't serve any requests until store is rebuilt.\n" + " -N No daemon mode.\n" +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + " -O options\n" + " Set Windows Service Command line options in Registry.\n" +#endif + " -R Do not set REUSEADDR on port.\n" + " -S Double-check swap during rebuild.\n" + " -X Force full debugging.\n" + " -Y Only return UDP_HIT or UDP_MISS_NOFETCH during fast reload.\n", + appname, DefaultConfigFile, CACHE_ICP_PORT); + exit(1); +} + +static void +mainParseOptions(int argc, char *argv[]) +{ + extern char *optarg; + int c; + +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + while ((c = getopt(argc, argv, "CDFO:RSYXd:f:hik:m::n:rsl:u:vz?")) != -1) { +#else + while ((c = getopt(argc, argv, "CDFNRSYXd:f:hk:m::sl:u:vz?")) != -1) { +#endif + switch (c) { + case 'C': + opt_catch_signals = 0; + break; + case 'D': + opt_dns_tests = 0; + break; + case 'F': + opt_foreground_rebuild = 1; + break; + case 'N': + opt_no_daemon = 1; + break; +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + case 'O': + opt_command_line = 1; + WIN32_Command_Line = xstrdup(optarg); + break; +#endif + case 'R': + opt_reuseaddr = 0; + break; + case 'S': + opt_store_doublecheck = 1; + break; + case 'X': + /* force full debugging */ + sigusr2_handle(SIGUSR2); + break; + case 'Y': + opt_reload_hit_only = 1; + break; + case 'd': + opt_debug_stderr = atoi(optarg); + break; + case 'f': + xfree(ConfigFile); + ConfigFile = xstrdup(optarg); + break; + case 'h': + usage(); + break; +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + case 'i': + opt_install_service = TRUE; + break; +#endif + case 'k': + if ((int) strlen(optarg) < 1) + usage(); + if (!strncmp(optarg, "reconfigure", strlen(optarg))) + opt_send_signal = SIGHUP; + else if (!strncmp(optarg, "rotate", strlen(optarg))) +#ifdef _SQUID_LINUX_THREADS_ + opt_send_signal = SIGQUIT; +#else + opt_send_signal = SIGUSR1; +#endif + else if (!strncmp(optarg, "debug", strlen(optarg))) +#ifdef _SQUID_LINUX_THREADS_ + opt_send_signal = SIGTRAP; +#else + opt_send_signal = SIGUSR2; +#endif + else if (!strncmp(optarg, "shutdown", strlen(optarg))) + opt_send_signal = SIGTERM; + else if (!strncmp(optarg, "interrupt", strlen(optarg))) + opt_send_signal = SIGINT; + else if (!strncmp(optarg, "kill", strlen(optarg))) + opt_send_signal = SIGKILL; + else if (!strncmp(optarg, "check", strlen(optarg))) + opt_send_signal = 0; /* SIGNULL */ + else if (!strncmp(optarg, "parse", strlen(optarg))) + opt_parse_cfg_only = 1; /* parse cfg file only */ + else + usage(); + break; + case 'm': + if (optarg) { +#if MALLOC_DBG + malloc_debug_level = atoi(optarg); + /* NOTREACHED */ + break; +#else + fatal("Need to add -DMALLOC_DBG when compiling to use -mX option"); + /* NOTREACHED */ +#endif + } else { +#if XMALLOC_TRACE + xmalloc_trace = !xmalloc_trace; +#else + fatal("Need to configure --enable-xmalloc-debug-trace to use -m option"); +#endif + } +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + case 'n': + xfree(WIN32_Service_name); + WIN32_Service_name = xstrdup(optarg); + opt_signal_service = TRUE; + break; + case 'r': + opt_remove_service = TRUE; + break; +#endif + case 'l': + opt_syslog_facility = xstrdup(optarg); + case 's': +#if HAVE_SYSLOG + _db_set_syslog(opt_syslog_facility); + break; +#else + fatal("Logging to syslog not available on this platform"); + /* NOTREACHED */ +#endif + case 'u': + icpPortNumOverride = atoi(optarg); + if (icpPortNumOverride < 0) + icpPortNumOverride = 0; + break; + case 'v': + printf("Squid Cache: Version %s\nPureSight: ICAP Enabled v 1.50\nconfigure options: %s\n", version_string, SQUID_CONFIGURE_OPTIONS); +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + printf("Compiled as Windows System Service.\n"); +#endif + exit(0); + /* NOTREACHED */ + case 'z': + opt_create_swap_dirs = 1; + break; + case '?': + default: + usage(); + break; + } + } +} + +/* ARGSUSED */ +void +rotate_logs(int sig) +{ + do_rotate = 1; +#ifndef _SQUID_MSWIN_ +#if !HAVE_SIGACTION + signal(sig, rotate_logs); +#endif +#endif +} + +/* ARGSUSED */ +void +reconfigure(int sig) +{ + do_reconfigure = 1; +#ifndef _SQUID_MSWIN_ +#if !HAVE_SIGACTION + signal(sig, reconfigure); +#endif +#endif +} + +void +shut_down(int sig) +{ + do_shutdown = sig == SIGINT ? -1 : 1; +#ifndef _SQUID_MSWIN_ +#ifdef KILL_PARENT_OPT + if (getppid() > 1) { + debug(1, 1) ("Killing RunCache, pid %ld\n", (long) getppid()); + if (kill(getppid(), sig) < 0) + debug(1, 1) ("kill %ld: %s\n", (long) getppid(), xstrerror()); + } +#endif +#if SA_RESETHAND == 0 + signal(SIGTERM, SIG_DFL); + signal(SIGINT, SIG_DFL); +#endif +#endif +} + +static void +serverConnectionsOpen(void) +{ + clientOpenListenSockets(); + icpConnectionsOpen(); +#if USE_HTCP + htcpInit(); +#endif +#ifdef SQUID_SNMP + snmpConnectionOpen(); +#endif +#if USE_WCCP + wccpConnectionOpen(); +#endif +#if USE_WCCPv2 + wccp2ConnectionOpen(); +#endif + clientdbInit(); + icmpOpen(); + netdbInit(); + asnInit(); + peerSelectInit(); +#if USE_CARP + carpInit(); +#endif + peerSourceHashInit(); + peerUserHashInit(); + peerMonitorInit(); +} + +void +serverConnectionsClose(void) +{ + assert(shutting_down || reconfiguring); + clientHttpConnectionsClose(); + icpConnectionShutdown(); +#if USE_HTCP + htcpSocketShutdown(); +#endif + icmpClose(); +#ifdef SQUID_SNMP + snmpConnectionShutdown(); +#endif +#if USE_WCCP + wccpConnectionClose(); +#endif +#if USE_WCCPv2 + wccp2ConnectionClose(); +#endif + asnFreeMemory(); +} + +static void +mainReconfigure(void) +{ + debug(1, 1) ("Reconfiguring Squid Cache (version %s)...\n", version_string); + reconfiguring = 1; + /* Already called serverConnectionsClose and ipcacheShutdownServers() */ + serverConnectionsClose(); + icpConnectionClose(); +#if USE_HTCP + htcpSocketClose(); +#endif +#ifdef SQUID_SNMP + snmpConnectionClose(); +#endif +#if USE_DNSSERVERS + dnsShutdown(); +#else + idnsShutdown(); +#endif +#ifdef HS_FEAT_ICAP + icapClose(); +#endif + redirectShutdown(); + locationRewriteShutdown(); + authenticateShutdown(); + externalAclShutdown(); + storeDirCloseSwapLogs(); + storeLogClose(); + accessLogClose(); + useragentLogClose(); + refererCloseLog(); + errorClean(); + enter_suid(); /* root to read config file */ + parseConfigFile(ConfigFile); + setUmask(Config.umask); + setEffectiveUser(); + _db_init(Config.Log.log, Config.debugOptions); + ipcache_restart(); /* clear stuck entries */ + authenticateUserCacheRestart(); /* clear stuck ACL entries */ + fqdncache_restart(); /* sigh, fqdncache too */ + parseEtcHosts(); + errorInitialize(); /* reload error pages */ + accessLogInit(); + storeLogOpen(); + useragentOpenLog(); + refererOpenLog(); +#if USE_DNSSERVERS + dnsInit(); +#else + idnsInit(); +#endif + redirectInit(); + locationRewriteInit(); +#ifdef HS_FEAT_ICAP + icapInit(); +#endif + authenticateInit(&Config.authConfig); + externalAclInit(); +#if USE_WCCP + wccpInit(); +#endif +#if USE_WCCPv2 + wccp2Init(); +#endif + serverConnectionsOpen(); + neighbors_init(); + storeDirOpenSwapLogs(); + mimeInit(Config.mimeTablePathname); + if (Config.onoff.announce) { + if (!eventFind(start_announce, NULL)) + eventAdd("start_announce", start_announce, NULL, 3600.0, 1); + } else { + if (eventFind(start_announce, NULL)) + eventDelete(start_announce, NULL); + } + eventCleanup(); + writePidFile(); /* write PID file */ + debug(1, 1) ("Ready to serve requests.\n"); + reconfiguring = 0; + peerMonitorInit(); +} + +static void +mainRotate(void) +{ + icmpClose(); +#if USE_DNSSERVERS + dnsShutdown(); +#endif + redirectShutdown(); + locationRewriteShutdown(); + authenticateShutdown(); + externalAclShutdown(); + _db_rotate_log(); /* cache.log */ + storeDirWriteCleanLogs(1); + storeDirSync(); /* Flush pending I/O ops */ + storeLogRotate(); /* store.log */ + accessLogRotate(); /* access.log */ + useragentRotateLog(); /* useragent.log */ + refererRotateLog(); /* referer.log */ +#if WIP_FWD_LOG + fwdLogRotate(); +#endif + icmpOpen(); +#if USE_DNSSERVERS + dnsInit(); +#endif + redirectInit(); + locationRewriteInit(); + authenticateInit(&Config.authConfig); + externalAclInit(); +} + +static void +setEffectiveUser(void) +{ + keepCapabilities(); + leave_suid(); /* Run as non privilegied user */ +#ifdef _SQUID_OS2_ + return; +#endif + if (geteuid() == 0) { + debug(0, 0) ("Squid is not safe to run as root! If you must\n"); + debug(0, 0) ("start Squid as root, then you must configure\n"); + debug(0, 0) ("it to run as a non-priveledged user with the\n"); + debug(0, 0) ("'cache_effective_user' option in the config file.\n"); + fatal("Don't run Squid as root, set 'cache_effective_user'!"); + } +} + +static void +mainSetCwd(void) +{ + char pathbuf[MAXPATHLEN]; + if (Config.coredump_dir) { + if (0 == strcmp("none", Config.coredump_dir)) { + (void) 0; + } else if (chdir(Config.coredump_dir) == 0) { + debug(0, 1) ("Set Current Directory to %s\n", Config.coredump_dir); + return; + } else { + debug(50, 0) ("chdir: %s: %s\n", Config.coredump_dir, xstrerror()); + } + } + /* If we don't have coredump_dir or couldn't cd there, report current dir */ + if (getcwd(pathbuf, MAXPATHLEN)) { + debug(0, 1) ("Current Directory is %s\n", pathbuf); + } else { + debug(50, 0) ("WARNING: Can't find current directory, getcwd: %s\n", xstrerror()); + } +} + +static void +mainInitialize(void) +{ + /* chroot if configured to run inside chroot */ + if (Config.chroot_dir && chroot(Config.chroot_dir)) { + fatal("failed to chroot"); + } + if (opt_catch_signals) { + squid_signal(SIGSEGV, death, SA_NODEFER | SA_RESETHAND); + squid_signal(SIGBUS, death, SA_NODEFER | SA_RESETHAND); + } + squid_signal(SIGPIPE, SIG_IGN, SA_RESTART); + squid_signal(SIGCHLD, sig_child, SA_NODEFER | SA_RESTART); + + setEffectiveUser(); + if (icpPortNumOverride != 1) + Config.Port.icp = (u_short) icpPortNumOverride; + + _db_init(Config.Log.log, Config.debugOptions); + fd_open(fileno(debug_log), FD_LOG, Config.Log.log); +#if MEM_GEN_TRACE + log_trace_init("/tmp/squid.alloc"); +#endif + debug(1, 0) ("Starting Squid Cache version %s for %s...\n", + version_string, + CONFIG_HOST_TYPE); +#ifdef _SQUID_WIN32_ + if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) { + debug(1, 0) ("Running as %s Windows System Service on %s\n", WIN32_Service_name, WIN32_OS_string); + debug(1, 0) ("Service command line is: %s\n", WIN32_Service_Command_Line); + } else + debug(1, 0) ("Running on %s\n", WIN32_OS_string); +#endif + debug(1, 1) ("Process ID %d\n", (int) getpid()); + debug(1, 1) ("With %d file descriptors available\n", Squid_MaxFD); +#ifdef _SQUID_MSWIN_ + debug(1, 1) ("With %d CRT stdio descriptors available\n", _getmaxstdio()); + if (WIN32_Socks_initialized) + debug(1, 1) ("Windows sockets initialized\n"); +#endif + + comm_select_postinit(); + if (!configured_once) + disk_init(); /* disk_init must go before ipcache_init() */ + ipcache_init(); + fqdncache_init(); + parseEtcHosts(); +#if USE_DNSSERVERS + dnsInit(); +#else + idnsInit(); +#endif + redirectInit(); + locationRewriteInit(); + errorMapInit(); +#ifdef HS_FEAT_ICAP + icapInit(); +#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + useragentOpenLog(); + refererOpenLog(); + httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */ + httpReplyInitModule(); /* must go before accepting replies */ + errorInitialize(); + accessLogInit(); +#if USE_IDENT + identInit(); +#endif +#ifdef SQUID_SNMP + snmpInit(); +#endif +#if MALLOC_DBG + malloc_debug(0, malloc_debug_level); +#endif + + if (!configured_once) { +#if USE_UNLINKD + unlinkdInit(); +#endif + urlInitialize(); + cachemgrInit(); + statInit(); + storeInit(); + mainSetCwd(); + /* after this point we want to see the mallinfo() output */ + do_mallinfo = 1; + mimeInit(Config.mimeTablePathname); + pconnInit(); + refreshInit(); +#if DELAY_POOLS + delayPoolsInit(); +#endif + fwdInit(); + } +#if USE_WCCP + wccpInit(); +#endif +#if USE_WCCPv2 + wccp2Init(); +#endif + serverConnectionsOpen(); + neighbors_init(); + if (Config.chroot_dir) + no_suid(); + if (!configured_once) + writePidFile(); /* write PID file */ + +#ifdef _SQUID_LINUX_THREADS_ + squid_signal(SIGQUIT, rotate_logs, SA_RESTART); + squid_signal(SIGTRAP, sigusr2_handle, SA_RESTART); +#else + squid_signal(SIGUSR1, rotate_logs, SA_RESTART); + squid_signal(SIGUSR2, sigusr2_handle, SA_RESTART); +#endif + squid_signal(SIGHUP, reconfigure, SA_RESTART); + squid_signal(SIGTERM, shut_down, SA_NODEFER | SA_RESETHAND | SA_RESTART); + squid_signal(SIGINT, shut_down, SA_NODEFER | SA_RESETHAND | SA_RESTART); + memCheckInit(); + debug(1, 1) ("Ready to serve requests.\n"); + if (!configured_once) { + eventAdd("storeMaintain", storeMaintainSwapSpace, NULL, 1.0, 1); + if (Config.onoff.announce) + eventAdd("start_announce", start_announce, NULL, 3600.0, 1); + eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1); + eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 15.0, 1); + } + configured_once = 1; +} + +#if USE_WIN32_SERVICE +/* When USE_WIN32_SERVICE is defined, the main function is placed in win32.c */ +void WINAPI +SquidWinSvcMain(int argc, char **argv) +{ + SquidMain(argc, argv); +} + +int +SquidMain(int argc, char **argv) +#else +int +main(int argc, char **argv) +#endif +{ + int errcount = 0; + int loop_delay; +#ifdef _SQUID_WIN32_ + int WIN32_init_err; +#endif + +#if HAVE_SBRK + sbrk_start = sbrk(0); +#endif + + debug_log = stderr; + +#ifdef _SQUID_WIN32_ + if ((WIN32_init_err = WIN32_Subsystem_Init(&argc, &argv))) + return WIN32_init_err; +#endif + + /* call mallopt() before anything else */ +#if HAVE_MALLOPT +#ifdef M_GRAIN + /* Round up all sizes to a multiple of this */ + mallopt(M_GRAIN, 16); +#endif +#ifdef M_MXFAST + /* biggest size that is considered a small block */ + mallopt(M_MXFAST, 256); +#endif +#ifdef M_NBLKS + /* allocate this many small blocks at once */ + mallopt(M_NLBLKS, 32); +#endif +#endif /* HAVE_MALLOPT */ + + memset(&local_addr, '\0', sizeof(struct in_addr)); + safe_inet_addr(localhost, &local_addr); + memset(&any_addr, '\0', sizeof(struct in_addr)); + safe_inet_addr("0.0.0.0", &any_addr); + memset(&no_addr, '\0', sizeof(struct in_addr)); + safe_inet_addr("255.255.255.255", &no_addr); + squid_srandom(time(NULL)); + + getCurrentTime(); + squid_start = current_time; + failure_notify = fatal_dump; + +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + WIN32_svcstatusupdate(SERVICE_START_PENDING, 10000); +#endif + mainParseOptions(argc, argv); + +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + if (opt_install_service) { + WIN32_InstallService(); + return 0; + } + if (opt_remove_service) { + WIN32_RemoveService(); + return 0; + } + if (opt_command_line) { + WIN32_SetServiceCommandLine(); + return 0; + } +#endif + + /* parse configuration file + * note: in "normal" case this used to be called from mainInitialize() */ + { + int parse_err; + if (!ConfigFile) + ConfigFile = xstrdup(DefaultConfigFile); + assert(!configured_once); +#if USE_LEAKFINDER + leakInit(); +#endif + memInit(); + cbdataInit(); + eventInit(); /* eventInit() is required for config parsing */ + storeFsInit(); /* required for config parsing */ + authenticateSchemeInit(); /* required for config parsing */ + parse_err = parseConfigFile(ConfigFile); + + if (opt_parse_cfg_only) + return parse_err; + } + setUmask(Config.umask); + if (-1 == opt_send_signal) + if (checkRunningPid()) + exit(1); + + /* Make sure the OS allows core dumps if enabled in squid.conf */ + enableCoredumps(); + +#if TEST_ACCESS + comm_init(); + comm_select_init(); + mainInitialize(); + test_access(); + return 0; +#endif + + /* send signal to running copy and exit */ + if (opt_send_signal != -1) { + /* chroot if configured to run inside chroot */ + if (Config.chroot_dir) { + if (chroot(Config.chroot_dir)) + fatal("failed to chroot"); + no_suid(); + } else { + leave_suid(); + } + sendSignal(); + /* NOTREACHED */ + } + if (opt_create_swap_dirs) { + /* chroot if configured to run inside chroot */ + if (Config.chroot_dir && chroot(Config.chroot_dir)) { + fatal("failed to chroot"); + } + setEffectiveUser(); + debug(0, 0) ("Creating Swap Directories\n"); + storeCreateSwapDirectories(); + return 0; + } + if (!opt_no_daemon) + watch_child(argv); + setMaxFD(); + + /* init comm module */ + comm_init(); + comm_select_init(); + + if (opt_no_daemon) { + /* we have to init fdstat here. */ + fd_open(0, FD_LOG, "stdin"); + fd_open(1, FD_LOG, "stdout"); + fd_open(2, FD_LOG, "stderr"); + } +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + WIN32_svcstatusupdate(SERVICE_START_PENDING, 10000); +#endif + mainInitialize(); + +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + WIN32_svcstatusupdate(SERVICE_RUNNING, 0); +#endif + + /* main loop */ + for (;;) { + if (do_reconfigure) { + mainReconfigure(); + do_reconfigure = 0; + } else if (do_rotate) { + mainRotate(); + do_rotate = 0; + } else if (do_shutdown) { + time_t wait = do_shutdown > 0 ? (int) Config.shutdownLifetime : 0; + debug(1, 1) ("Preparing for shutdown after %d requests\n", + statCounter.client_http.requests); + debug(1, 1) ("Waiting %d seconds for active connections to finish\n", + (int) wait); + do_shutdown = 0; + shutting_down = 1; +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + WIN32_svcstatusupdate(SERVICE_STOP_PENDING, (wait + 1) * 1000); +#endif + serverConnectionsClose(); + eventAdd("SquidShutdown", SquidShutdown, NULL, (double) (wait + 1), 1); + } + eventRun(); + if ((loop_delay = eventNextTime()) < 0) + loop_delay = 0; + if (debug_log_flush() && loop_delay > 1000) + loop_delay = 1000; + switch (comm_select(loop_delay)) { + case COMM_OK: + errcount = 0; /* reset if successful */ + break; + case COMM_ERROR: + errcount++; + debug(1, 0) ("Select loop Error. Retry %d\n", errcount); + if (errcount == 10) + fatal_dump("Select Loop failed!"); + break; + case COMM_TIMEOUT: + break; + case COMM_SHUTDOWN: + SquidShutdown(NULL); + break; + default: + fatal_dump("MAIN: Internal error -- this should never happen."); + break; + } + } + /* NOTREACHED */ + return 0; +} + +static void +sendSignal(void) +{ + pid_t pid; + debug_log = stderr; + pid = readPidFile(); + if (pid > 1) { +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + if (opt_signal_service) + WIN32_sendSignal(opt_send_signal); + else { +#endif +#if defined(_SQUID_MSWIN_) && defined(USE_WIN32_SERVICE) + fprintf(stderr, "%s: ERROR: Could not send ", appname); + fprintf(stderr, "signal to Squid Service:\n"); + fprintf(stderr, "missing -n command line switch.\n"); +#else + if (kill(pid, opt_send_signal) && + /* ignore permissions if just running check */ + !(opt_send_signal == 0 && errno == EPERM)) { + fprintf(stderr, "%s: ERROR: Could not send ", appname); + fprintf(stderr, "signal %d to process %d: %s\n", + opt_send_signal, (int) pid, xstrerror()); +#endif + exit(1); + } +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_CYGWIN_) + } +#endif + } else { + fprintf(stderr, "%s: ERROR: No running copy\n", appname); + exit(1); + } + /* signal successfully sent */ + exit(0); +} + +#ifndef _SQUID_MSWIN_ +/* + * This function is run when Squid is in daemon mode, just + * before the parent forks and starts up the child process. + * It can be used for admin-specific tasks, such as notifying + * someone that Squid is (re)started. + */ +static void +mainStartScript(const char *prog) +{ + char script[SQUID_MAXPATHLEN]; + char *t; + size_t sl = 0; + pid_t cpid; + pid_t rpid; + xstrncpy(script, prog, MAXPATHLEN); + if ((t = strrchr(script, '/'))) { + *(++t) = '\0'; + sl = strlen(script); + } + xstrncpy(&script[sl], squid_start_script, MAXPATHLEN - sl); + if ((cpid = fork()) == 0) { + /* child */ + execl(script, squid_start_script, NULL); + _exit(0); + } else { + do { +#ifdef _SQUID_NEXT_ + union wait status; + rpid = wait3(&status, 0, NULL); +#else + int status; + rpid = waitpid(-1, &status, 0); +#endif + } while (rpid != cpid); + } +} +#endif + +static int +checkRunningPid(void) +{ + pid_t pid; + debug_log = stderr; + if (strcmp(Config.pidFilename, "none") == 0) { + debug(0, 1) ("No pid_filename specified. Trusting you know what you are doing.\n"); + return 0; + } + pid = readPidFile(); + if (pid < 2) + return 0; + if (kill(pid, 0) < 0) + return 0; + debug(0, 0) ("Squid is already running! Process ID %ld\n", (long int) pid); + return 1; +} + +static void +watch_child(char *argv[]) +{ +#ifndef _SQUID_MSWIN_ + char *prog; + int failcount = 0; + time_t start; + time_t stop; +#ifdef _SQUID_NEXT_ + union wait status; +#else + int status; +#endif + pid_t pid; +#ifdef TIOCNOTTY + int i; +#endif + int nullfd; + if (*(argv[0]) == '(') + return; + openlog(appname, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4); + if ((pid = fork()) < 0) + syslog(LOG_ALERT, "fork failed: %s", xstrerror()); + else if (pid > 0) + exit(0); + if (setsid() < 0) + syslog(LOG_ALERT, "setsid failed: %s", xstrerror()); + closelog(); +#ifdef TIOCNOTTY + if ((i = open("/dev/tty", O_RDWR | O_TEXT)) >= 0) { + ioctl(i, TIOCNOTTY, NULL); + close(i); + } +#endif + + + /* + * RBCOLLINS - if cygwin stackdumps when squid is run without + * -N, check the cygwin1.dll version, it needs to be AT LEAST + * 1.1.3. execvp had a bit overflow error in a loop.. + */ + /* Connect stdio to /dev/null in daemon mode */ + nullfd = open(_PATH_DEVNULL, O_RDWR | O_TEXT); + if (nullfd < 0) + fatalf(_PATH_DEVNULL " %s\n", xstrerror()); + dup2(nullfd, 0); + if (opt_debug_stderr < 0) { + dup2(nullfd, 1); + dup2(nullfd, 2); + } + if (nullfd > 2) + close(nullfd); + for (;;) { + mainStartScript(argv[0]); + if ((pid = fork()) == 0) { + /* child */ + openlog(appname, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4); + prog = xstrdup(argv[0]); + argv[0] = xstrdup("(squid)"); + execvp(prog, argv); + syslog(LOG_ALERT, "execvp failed: %s", xstrerror()); + } + /* parent */ + openlog(appname, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4); + syslog(LOG_NOTICE, "Squid Parent: child process %d started", pid); + time(&start); + squid_signal(SIGINT, SIG_IGN, SA_RESTART); +#ifdef _SQUID_NEXT_ + pid = wait3(&status, 0, NULL); +#else + pid = waitpid(-1, &status, 0); +#endif + time(&stop); + if (WIFEXITED(status)) { + syslog(LOG_NOTICE, + "Squid Parent: child process %d exited with status %d", + pid, WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + syslog(LOG_NOTICE, + "Squid Parent: child process %d exited due to signal %d", + pid, WTERMSIG(status)); + } else { + syslog(LOG_NOTICE, "Squid Parent: child process %d exited", pid); + } + if (stop - start < 10) + failcount++; + else + failcount = 0; + if (failcount == 5) { + syslog(LOG_ALERT, "Exiting due to repeated, frequent failures"); + exit(1); + } + if (WIFEXITED(status)) + if (WEXITSTATUS(status) == 0) + exit(0); + if (WIFSIGNALED(status)) { + switch (WTERMSIG(status)) { + case SIGKILL: + exit(0); + break; + default: + break; + } + } + squid_signal(SIGINT, SIG_DFL, SA_RESTART); + sleep(3); + } + /* NOTREACHED */ +#endif +} + +static void +SquidShutdown(void *unused) +{ +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + WIN32_svcstatusupdate(SERVICE_STOP_PENDING, 10000); +#endif + debug(1, 1) ("Shutting down...\n"); +#if USE_DNSSERVERS + dnsShutdown(); +#else + idnsShutdown(); +#endif + redirectShutdown(); + externalAclShutdown(); + locationRewriteShutdown(); + icpConnectionClose(); +#if USE_HTCP + htcpSocketClose(); +#endif +#ifdef SQUID_SNMP + snmpConnectionClose(); +#endif +#if USE_WCCP + wccpConnectionClose(); +#endif +#if USE_WCCPv2 + wccp2ConnectionClose(); +#endif + releaseServerSockets(); + commCloseAllSockets(); + authenticateShutdown(); +#if USE_UNLINKD + unlinkdClose(); +#endif +#if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) + WIN32_svcstatusupdate(SERVICE_STOP_PENDING, 10000); +#endif + storeDirSync(); /* Flush pending object writes/unlinks */ + storeDirWriteCleanLogs(0); + PrintRusage(); + dumpMallocStats(); + storeDirSync(); /* Flush log writes */ + storeLogClose(); + accessLogClose(); + useragentLogClose(); + refererCloseLog(); +#if WIP_FWD_LOG + fwdUninit(); +#endif + storeDirSync(); /* Flush log close */ + storeFsDone(); +#if LEAK_CHECK_MODE + configFreeMemory(); + storeFreeMemory(); + /*stmemFreeMemory(); */ + netdbFreeMemory(); + ipcacheFreeMemory(); + fqdncacheFreeMemory(); + asnFreeMemory(); + clientdbFreeMemory(); + httpHeaderCleanModule(); + statFreeMemory(); + eventFreeMemory(); + mimeFreeMemory(); + errorClean(); +#endif +#if !XMALLOC_TRACE + if (opt_no_daemon) { + fd_close(0); + fd_close(1); + fd_close(2); + } +#endif + comm_select_shutdown(); + fdDumpOpen(); + fdFreeMemory(); + memClean(); +#if XMALLOC_TRACE + xmalloc_find_leaks(); + debug(1, 0) ("Memory used after shutdown: %d\n", xmalloc_total); +#endif +#if MEM_GEN_TRACE + log_trace_done(); +#endif + if (Config.pidFilename && strcmp(Config.pidFilename, "none") != 0) { + enter_suid(); + safeunlink(Config.pidFilename, 0); + leave_suid(); + } + debug(1, 1) ("Squid Cache (Version %s): Exiting normally.\n", + version_string); + if (debug_log) + fclose(debug_log); + exit(0); +} diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/a8/d0ceef315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/a8/d0ceef315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/a8/d0ceef315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/a8/d0ceef315c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,29 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/auth/basic/auth_basic.c + +OBJS += \ +./auth/basic/auth_basic.o + +DEPS += \ +${addprefix ./auth/basic/, \ +auth_basic.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +auth/basic/%.o: $(ROOT)/auth/basic/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/c/80d6e2315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/c/80d6e2315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/c/80d6e2315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/c/80d6e2315c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,29 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/auth/negotiate/auth_negotiate.c + +OBJS += \ +./auth/negotiate/auth_negotiate.o + +DEPS += \ +${addprefix ./auth/negotiate/, \ +auth_negotiate.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +auth/negotiate/%.o: $(ROOT)/auth/negotiate/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/cd/a049c8315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/cd/a049c8315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/cd/a049c8315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/cd/a049c8315c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,35 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/fs/diskd/diskd.c \ +$(ROOT)/fs/diskd/store_dir_diskd.c \ +$(ROOT)/fs/diskd/store_io_diskd.c + +OBJS += \ +./fs/diskd/diskd.o \ +./fs/diskd/store_dir_diskd.o \ +./fs/diskd/store_io_diskd.o + +DEPS += \ +${addprefix ./fs/diskd/, \ +diskd.d \ +store_dir_diskd.d \ +store_io_diskd.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +fs/diskd/%.o: $(ROOT)/fs/diskd/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/d5/60c68ed15e88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/d5/60c68ed15e88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/d5/60c68ed15e88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/d5/60c68ed15e88001b16add8656805b17d 2006-12-10 16:09:19.000000000 +0200 @@ -0,0 +1,1689 @@ + +/* + * $Id: http.c,v 1.418 2006/10/23 21:34:17 hno Exp $ + * + * DEBUG: section 11 Hypertext Transfer Protocol (HTTP) + * AUTHOR: Harvest Derived + * + * 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. + * + */ + +/* + * Anonymizing patch by lutz@as-node.jena.thur.de + * have a look into http-anon.c to get more informations. + */ + +#include "squid.h" + +static const char *const crlf = "\r\n"; + +static CWCB httpSendComplete; +static CWCB httpSendRequestEntry; + +static PF httpReadReply; +static void httpSendRequest(HttpStateData *); +PF httpStateFree; +static PF httpTimeout; +static void httpCacheNegatively(StoreEntry *); +static void httpMakePrivate(StoreEntry *); +static void httpMakePublic(StoreEntry *); +static int httpCachableReply(HttpStateData *); +static void httpMaybeRemovePublic(StoreEntry *, http_status); +static int peer_supports_connection_pinning(HttpStateData * httpState); + +void +httpStateFree(int fd, void *data) +{ + HttpStateData *httpState = data; +#if DELAY_POOLS + if (fd >= 0) + delayClearNoDelay(fd); +#endif + if (httpState == NULL) + return; + if (httpState->body_buf) { + requestAbortBody(httpState->orig_request); + if (httpState->body_buf) { + memFree(httpState->body_buf, MEM_8K_BUF); + httpState->body_buf = NULL; + } + } + storeUnlockObject(httpState->entry); + if (!memBufIsNull(&httpState->reply_hdr)) { + memBufClean(&httpState->reply_hdr); + } + requestUnlink(httpState->request); + requestUnlink(httpState->orig_request); + httpState->request = NULL; + httpState->orig_request = NULL; +#if HS_FEAT_ICAP + cbdataUnlock(httpState->icap_writer); +#endif + cbdataFree(httpState); +} + +int +httpCachable(method_t method) +{ + /* GET and HEAD are cachable. Others are not. */ + if (method != METHOD_GET && method != METHOD_HEAD) + return 0; + /* else cachable */ + return 1; +} + +static void +httpTimeout(int fd, void *data) +{ + HttpStateData *httpState = data; + StoreEntry *entry = httpState->entry; + debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, storeUrl(entry)); + if (entry->store_status == STORE_PENDING) { + fwdFail(httpState->fwd, + errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT, httpState->fwd->request)); + } + comm_close(fd); +} + +/* This object can be cached for a long time */ +static void +httpMakePublic(StoreEntry * entry) +{ + if (EBIT_TEST(entry->flags, ENTRY_CACHABLE)) + storeSetPublicKey(entry); +} + +/* This object should never be cached at all */ +static void +httpMakePrivate(StoreEntry * entry) +{ + storeExpireNow(entry); + storeReleaseRequest(entry); /* delete object when not used */ + /* storeReleaseRequest clears ENTRY_CACHABLE flag */ +} + +/* This object may be negatively cached */ +static void +httpCacheNegatively(StoreEntry * entry) +{ + storeNegativeCache(entry); + if (EBIT_TEST(entry->flags, ENTRY_CACHABLE)) + storeSetPublicKey(entry); +} + +static void +httpMaybeRemovePublic(StoreEntry * e, http_status status) +{ + int remove = 0; + int forbidden = 0; + StoreEntry *pe; + switch (status) { + case HTTP_OK: + case HTTP_NON_AUTHORITATIVE_INFORMATION: + case HTTP_MULTIPLE_CHOICES: + case HTTP_MOVED_PERMANENTLY: + case HTTP_MOVED_TEMPORARILY: + case HTTP_GONE: + case HTTP_NOT_FOUND: + remove = 1; + break; + case HTTP_FORBIDDEN: + case HTTP_METHOD_NOT_ALLOWED: + forbidden = 1; + break; +#if WORK_IN_PROGRESS + case HTTP_UNAUTHORIZED: + forbidden = 1; + break; +#endif + default: +#if QUESTIONABLE + /* + * Any 2xx response should eject previously cached entities... + */ + if (status >= 200 && status < 300) + remove = 1; +#endif + break; + } + if (!remove && !forbidden) + return; + if (EBIT_TEST(e->flags, KEY_PRIVATE)) { + assert(e->mem_obj); + if (e->mem_obj->request) + pe = storeGetPublicByRequest(e->mem_obj->request); + else + pe = storeGetPublic(e->mem_obj->url, e->mem_obj->method); + if (pe != NULL) { + assert(e != pe); + storeRelease(pe); + } + } + /* + * Also remove any cached HEAD response in case the object has + * changed. + */ + if (e->mem_obj->request) + pe = storeGetPublicByRequestMethod(e->mem_obj->request, METHOD_HEAD); + else + pe = storeGetPublic(e->mem_obj->url, METHOD_HEAD); + if (pe != NULL && e != pe) { + storeRelease(pe); + } + if (forbidden) + return; + switch (e->mem_obj->method) { + case METHOD_PUT: + case METHOD_DELETE: + case METHOD_PROPPATCH: + case METHOD_MKCOL: + case METHOD_MOVE: + case METHOD_BMOVE: + case METHOD_BDELETE: + /* + * Remove any cached GET object if it is beleived that the + * object may have changed as a result of other methods + */ + if (e->mem_obj->request) + pe = storeGetPublicByRequestMethod(e->mem_obj->request, METHOD_GET); + else + pe = storeGetPublic(e->mem_obj->url, METHOD_GET); + if (pe != NULL) { + assert(e != pe); + storeRelease(pe); + } + break; + } +} + +static int +httpCachableReply(HttpStateData * httpState) +{ + HttpReply *rep = httpState->entry->mem_obj->reply; + HttpHeader *hdr = &rep->header; + const int cc_mask = (rep->cache_control) ? rep->cache_control->mask : 0; + const char *v; +#if HTTP_VIOLATIONS + const refresh_t *R = NULL; + /* This strange looking define first looks up the frefresh pattern + * and then checks if the specified flag is set. The main purpose + * of this is to simplify the refresh pattern lookup + */ +#define REFRESH_OVERRIDE(flag) \ + ((R = (R ? R : refreshLimits(httpState->entry->mem_obj->url))) , \ + (R && R->flags.flag)) +#else +#define REFRESH_OVERRIDE(field) 0 +#endif + if (EBIT_TEST(cc_mask, CC_PRIVATE) && !REFRESH_OVERRIDE(ignore_private)) + return 0; + if (EBIT_TEST(cc_mask, CC_NO_CACHE) && !REFRESH_OVERRIDE(ignore_no_cache)) + return 0; + if (EBIT_TEST(cc_mask, CC_NO_STORE)) + return 0; + if (httpState->request->flags.auth_sent) { + /* + * Responses to requests with authorization may be cached + * only if a Cache-Control: public reply header is present. + * RFC 2068, sec 14.9.4 + */ + if (!EBIT_TEST(cc_mask, CC_PUBLIC) && !REFRESH_OVERRIDE(ignore_auth)) + return 0; + } + /* Pragma: no-cache in _replies_ is not documented in HTTP, + * but servers like "Active Imaging Webcast/2.0" sure do use it */ + if (httpHeaderHas(hdr, HDR_PRAGMA)) { + String s = httpHeaderGetList(hdr, HDR_PRAGMA); + const int no_cache = strListIsMember(&s, "no-cache", ','); + stringClean(&s); + if (no_cache && !REFRESH_OVERRIDE(ignore_no_cache)) + return 0; + } + /* + * The "multipart/x-mixed-replace" content type is used for + * continuous push replies. These are generally dynamic and + * probably should not be cachable + */ + if ((v = httpHeaderGetStr(hdr, HDR_CONTENT_TYPE))) + if (!strncasecmp(v, "multipart/x-mixed-replace", 25)) + return 0; + switch (httpState->entry->mem_obj->reply->sline.status) { + /* Responses that are cacheable */ + case HTTP_OK: + case HTTP_NON_AUTHORITATIVE_INFORMATION: + case HTTP_MULTIPLE_CHOICES: + case HTTP_MOVED_PERMANENTLY: + case HTTP_GONE: + /* + * Don't cache objects that need to be refreshed on next request, + * unless we know how to refresh it. + */ + if (!refreshIsCachable(httpState->entry)) + return 0; + /* don't cache objects from peers w/o LMT, Date, or Expires */ + /* check that is it enough to check headers @?@ */ + if (rep->date > -1) + return 1; + else if (rep->last_modified > -1) + return 1; + else if (!httpState->peer) + return 1; + /* @?@ (here and 302): invalid expires header compiles to squid_curtime */ + else if (rep->expires > -1) + return 1; + else + return 0; + /* NOTREACHED */ + break; + /* Responses that only are cacheable if the server says so */ + case HTTP_MOVED_TEMPORARILY: + if (rep->expires > -1) + return 1; + else + return 0; + /* NOTREACHED */ + break; + /* Errors can be negatively cached */ + case HTTP_NO_CONTENT: + case HTTP_USE_PROXY: + case HTTP_BAD_REQUEST: + case HTTP_FORBIDDEN: + case HTTP_NOT_FOUND: + case HTTP_METHOD_NOT_ALLOWED: + case HTTP_REQUEST_URI_TOO_LARGE: + case HTTP_INTERNAL_SERVER_ERROR: + case HTTP_NOT_IMPLEMENTED: + case HTTP_BAD_GATEWAY: + case HTTP_SERVICE_UNAVAILABLE: + case HTTP_GATEWAY_TIMEOUT: + return -1; + /* NOTREACHED */ + break; + /* Some responses can never be cached */ + case HTTP_PARTIAL_CONTENT: /* Not yet supported */ + case HTTP_SEE_OTHER: + case HTTP_NOT_MODIFIED: + case HTTP_UNAUTHORIZED: + case HTTP_PROXY_AUTHENTICATION_REQUIRED: + case HTTP_INVALID_HEADER: /* Squid header parsing error */ + case HTTP_HEADER_TOO_LARGE: + default: /* Unknown status code */ + return 0; + /* NOTREACHED */ + break; + } + /* NOTREACHED */ +} + +/* + * For Vary, store the relevant request headers as + * virtual headers in the reply + * Returns false if the variance cannot be stored + */ +const char * +httpMakeVaryMark(request_t * request, HttpReply * reply) +{ + String vary = StringNull, hdr; + const char *pos = NULL; + const char *item; + const char *value; + int ilen; + String vstr = StringNull; + + stringClean(&vstr); + hdr = httpHeaderGetList(&reply->header, HDR_VARY); + if (strBuf(hdr)) + strListAdd(&vary, strBuf(hdr), ','); + stringClean(&hdr); +#if X_ACCELERATOR_VARY + hdr = httpHeaderGetList(&reply->header, HDR_X_ACCELERATOR_VARY); + if (strBuf(hdr)) + strListAdd(&vary, strBuf(hdr), ','); + stringClean(&hdr); +#endif + while (strListGetItem(&vary, ',', &item, &ilen, &pos)) { + char *name = xmalloc(ilen + 1); + xstrncpy(name, item, ilen + 1); + Tolower(name); + if (strcmp(name, "accept-encoding") == 0) { + aclCheck_t checklist; + memset(&checklist, 0, sizeof(checklist)); + checklist.request = request; + checklist.reply = reply; + if (Config.accessList.vary_encoding && aclCheckFast(Config.accessList.vary_encoding, &checklist)) { + stringClean(&request->vary_encoding); + request->vary_encoding = httpHeaderGetStrOrList(&request->header, HDR_ACCEPT_ENCODING); + strCat(request->vary_encoding, ""); + } + } + if (strcmp(name, "*") == 0) { + /* Can not handle "Vary: *" efficiently, bail out making the response not cached */ + safe_free(name); + stringClean(&vary); + stringClean(&vstr); + break; + } + strListAdd(&vstr, name, ','); + hdr = httpHeaderGetByName(&request->header, name); + safe_free(name); + value = strBuf(hdr); + if (value) { + value = rfc1738_escape_part(value); + stringAppend(&vstr, "=\"", 2); + stringAppend(&vstr, value, strlen(value)); + stringAppend(&vstr, "\"", 1); + } + stringClean(&hdr); + } + safe_free(request->vary_hdr); + safe_free(request->vary_headers); + if (strBuf(vary) && strBuf(vstr)) { + request->vary_hdr = xstrdup(strBuf(vary)); + request->vary_headers = xstrdup(strBuf(vstr)); + } + debug(11, 3) ("httpMakeVaryMark: %s\n", strBuf(vstr)); + stringClean(&vary); + stringClean(&vstr); + return request->vary_headers; +} + +/* rewrite this later using new interfaces @?@ */ +void +httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) +{ + StoreEntry *entry = httpState->entry; + size_t hdr_len; + size_t hdr_size; + HttpReply *reply = entry->mem_obj->reply; + Ctx ctx = ctx_enter(entry->mem_obj->url); + debug(11, 3) ("httpProcessReplyHeader: key '%s'\n", + storeKeyText(entry->hash.key)); + if (memBufIsNull(&httpState->reply_hdr)) + memBufDefInit(&httpState->reply_hdr); + assert(httpState->reply_hdr_state == 0); + memBufAppend(&httpState->reply_hdr, buf, size); + hdr_len = httpState->reply_hdr.size; + if (hdr_len > 4 && strncmp(httpState->reply_hdr.buf, "HTTP/", 5)) { + debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState->reply_hdr.buf); + httpState->reply_hdr_state += 2; + memBufClean(&httpState->reply_hdr); + httpBuildVersion(&reply->sline.version, 0, 9); + reply->sline.status = HTTP_INVALID_HEADER; + ctx_exit(ctx); + return; + } + hdr_size = headersEnd(httpState->reply_hdr.buf, hdr_len); + if (hdr_size) + hdr_len = hdr_size; + if (hdr_len > Config.maxReplyHeaderSize) { + debug(11, 1) ("httpProcessReplyHeader: Too large reply header\n"); + if (!memBufIsNull(&httpState->reply_hdr)) + memBufClean(&httpState->reply_hdr); + reply->sline.status = HTTP_HEADER_TOO_LARGE; + httpState->reply_hdr_state += 2; + ctx_exit(ctx); + return; + } + /* headers can be incomplete only if object still arriving */ + if (!hdr_size) { + if (httpState->eof) + hdr_size = hdr_len; + else { + ctx_exit(ctx); + return; /* headers not complete */ + } + } + safe_free(entry->mem_obj->vary_headers); + safe_free(entry->mem_obj->vary_encoding); + /* Cut away any excess body data (only needed for debug?) */ + memBufAppend(&httpState->reply_hdr, "\0", 1); + httpState->reply_hdr.buf[hdr_size] = '\0'; + httpState->reply_hdr_state++; + assert(httpState->reply_hdr_state == 1); + httpState->reply_hdr_state++; + debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n", + httpState->reply_hdr.buf); + /* Parse headers into reply structure */ + /* what happens if we fail to parse here? */ + httpReplyParse(reply, httpState->reply_hdr.buf, hdr_size); + if (reply->sline.status >= HTTP_INVALID_HEADER) { + debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState->reply_hdr.buf); + memBufClean(&httpState->reply_hdr); + ctx_exit(ctx); + return; + } + if (!peer_supports_connection_pinning(httpState)) + httpState->orig_request->flags.no_connection_auth = 1; + storeTimestampsSet(entry); + /* Check if object is cacheable or not based on reply code */ + debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status); + if (httpHeaderHas(&reply->header, HDR_VARY) +#if X_ACCELERATOR_VARY + || httpHeaderHas(&reply->header, HDR_X_ACCELERATOR_VARY) +#endif + ) { + const char *vary = NULL; + if (Config.onoff.cache_vary) + vary = httpMakeVaryMark(httpState->orig_request, reply); + if (!vary) { + httpMakePrivate(entry); + goto no_cache; + } + entry->mem_obj->vary_headers = xstrdup(vary); + if (strBuf(httpState->orig_request->vary_encoding)) + entry->mem_obj->vary_encoding = xstrdup(strBuf(httpState->orig_request->vary_encoding)); + } + switch (httpCachableReply(httpState)) { + case 1: + httpMakePublic(entry); + break; + case 0: + httpMakePrivate(entry); + break; + case -1: + if (Config.negativeTtl > 0) + httpCacheNegatively(entry); + else + httpMakePrivate(entry); + break; + default: + assert(0); + break; + } + no_cache: + if (reply->cache_control) { + if (EBIT_TEST(reply->cache_control->mask, CC_PROXY_REVALIDATE)) + EBIT_SET(entry->flags, ENTRY_REVALIDATE); + else if (EBIT_TEST(reply->cache_control->mask, CC_MUST_REVALIDATE)) + EBIT_SET(entry->flags, ENTRY_REVALIDATE); + } + if (neighbors_do_private_keys && !Config.onoff.collapsed_forwarding) + httpMaybeRemovePublic(entry, reply->sline.status); + if (httpState->flags.keepalive) + if (httpState->peer) + httpState->peer->stats.n_keepalives_sent++; + if (reply->keep_alive) { + if (httpState->peer) + httpState->peer->stats.n_keepalives_recv++; + if (Config.onoff.detect_broken_server_pconns && httpReplyBodySize(httpState->request->method, reply) == -1) { + debug(11, 1) ("httpProcessReplyHeader: Impossible keep-alive header from '%s'\n", storeUrl(entry)); + debug(11, 2) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n", + httpState->reply_hdr.buf); + httpState->flags.keepalive_broken = 1; + } + } + if (reply->date > -1 && !httpState->peer) { + int skew = abs(reply->date - squid_curtime); + if (skew > 86400) + debug(11, 3) ("%s's clock is skewed by %d seconds!\n", + httpState->request->host, skew); + } + ctx_exit(ctx); +#if HEADERS_LOG + headersLog(1, 0, httpState->request->method, reply); +#endif +} + +static int +httpPconnTransferDone(HttpStateData * httpState) +{ + /* return 1 if we got the last of the data on a persistent connection */ + MemObject *mem = httpState->entry->mem_obj; + HttpReply *reply = mem->reply; + squid_off_t clen; + squid_off_t content_bytes_read; + debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); + debug(11, 5) ("httpPconnTransferDone: content_length=%" PRINTF_OFF_T "\n", + reply->content_length); + /* If we haven't seen the end of reply headers, we are not done */ + if (httpState->reply_hdr_state < 2) { + debug(11, 3) ("httpPconnTransferDone: reply_hdr_state=%d, returning 0\n", + httpState->reply_hdr_state); + return 0; + } + clen = httpReplyBodySize(httpState->request->method, reply); +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) { + content_bytes_read = httpState->icap_writer->fake_content_length; + debug(11, 3) ("using fake conten length %" PRINTF_OFF_T "\n", content_bytes_read); + } else +#endif + content_bytes_read = mem->inmem_hi; + /* If the body size is unknown we must wait for EOF */ + if (clen < 0) + return 0; + /* Barf if we got more than we asked for */ + if (content_bytes_read > clen + reply->hdr_sz) + return -1; + /* If there is no message body, we can be persistent */ + if (0 == clen) + return 1; + /* If the body size is known, we must wait until we've gotten all of it. */ + if (content_bytes_read < clen + reply->hdr_sz) + return 0; + /* We got it all */ + return 1; +} + +/* Small helper function to verify if connection pinning is supported or not + */ +static int +peer_supports_connection_pinning(HttpStateData * httpState) +{ + const HttpReply *rep = httpState->entry->mem_obj->reply; + const HttpHeader *hdr = &rep->header; + const request_t *req = httpState->request; + int rc; + String header; + + if (!httpState->peer) + return 1; + + if (!httpState->peer->connection_auth) + return 0; + + if (rep->sline.status != HTTP_UNAUTHORIZED) + return 1; + + if (httpState->peer->connection_auth == 1) + return 1; + + if (httpState->peer->options.originserver) + return 1; + + if (req->flags.pinned) + return 1; + + if (!httpHeaderHas(hdr, HDR_PROXY_SUPPORT)) + return 0; + + header = httpHeaderGetStrOrList(hdr, HDR_PROXY_SUPPORT); + /* XXX This ought to be done in a case-insensitive manner */ + rc = (strStr(header, "Session-Based-Authentication") != NULL); + stringClean(&header); + + return rc; +} + +/* This will be called when data is ready to be read from fd. Read until + * error or connection closed. */ +/* XXX this function is too long! */ +static void +httpReadReply(int fd, void *data) +{ + HttpStateData *httpState = data; + LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF); + StoreEntry *entry = httpState->entry; + const request_t *request = httpState->request; + const request_t *orig_request = httpState->orig_request; + int len; + int bin; + int clen; + size_t read_sz = SQUID_TCP_SO_RCVBUF; + struct in_addr *client_addr = NULL; + u_short client_port = 0; +#if DELAY_POOLS + delay_id delay_id; +#endif + +#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; + } +#if DELAY_POOLS + /* special "if" only for http (for nodelay proxy conns) */ + if (delayIsNoDelay(fd)) + delay_id = 0; + 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); + debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, len); + if (len > 0) { + fd_bytes(fd, len, FD_READ); +#if DELAY_POOLS + delayBytesIn(delay_id, len); +#endif + kb_incr(&statCounter.server.all.kbytes_in, len); + kb_incr(&statCounter.server.http.kbytes_in, len); + IOStats.Http.reads++; + for (clen = len - 1, bin = 0; clen; bin++) + clen >>= 1; + IOStats.Http.read_hist[bin]++; + } +#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--); + if (len == 0) { + /* Continue to read... */ + /* Timeout NOT increased. This whitespace was from previous reply */ + commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); + return; + } + } + if (len < 0) { + debug(11, 2) ("httpReadReply: FD %d: read failure: %s.\n", + fd, xstrerror()); + if (ignoreErrno(errno)) { + commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); + } else { + ErrorState *err; + err = errorCon(ERR_READ_ERROR, HTTP_BAD_GATEWAY, httpState->fwd->request); + err->xerrno = errno; + fwdFail(httpState->fwd, err); + comm_close(fd); + } + } else if (len == 0 && entry->mem_obj->inmem_hi == 0) { + fwdFail(httpState->fwd, errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_BAD_GATEWAY, httpState->fwd->request)); + httpState->eof = 1; + comm_close(fd); + } else if (len == 0) { + /* Connection closed; retrieval done. */ + httpState->eof = 1; +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer && cbdataValid(httpState->icap_writer)) { + debug(81, 3) ("httpReadReply: EOF for ICAP writer\n"); + icapSendRespMod(httpState->icap_writer, buf, len, 1); + } +#endif + if (httpState->reply_hdr_state < 2) + /* + * Yes Henrik, there is a point to doing this. When we + * called httpProcessReplyHeader() before, we didn't find + * the end of headers, but now we are definately at EOF, so + * we want to process the reply headers. + */ + httpProcessReplyHeader(httpState, buf, len); + if (entry->mem_obj->reply->sline.status == HTTP_HEADER_TOO_LARGE) { + storeEntryReset(entry); + fwdFail(httpState->fwd, errorCon(ERR_TOO_BIG, HTTP_BAD_GATEWAY, httpState->fwd->request)); + httpState->fwd->flags.dont_retry = 1; + } else if (entry->mem_obj->reply->sline.status == HTTP_INVALID_HEADER && !(entry->mem_obj->reply->sline.version.major == 0 && entry->mem_obj->reply->sline.version.minor == 9)) { + storeEntryReset(entry); + fwdFail(httpState->fwd, errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY, httpState->fwd->request)); + httpState->fwd->flags.dont_retry = 1; + } else { + fwdComplete(httpState->fwd); + } + comm_close(fd); + return; + } else { + if (httpState->reply_hdr_state < 2) { + httpProcessReplyHeader(httpState, buf, len); + if (httpState->reply_hdr_state == 2) { + http_status s = entry->mem_obj->reply->sline.status; + if (s == HTTP_HEADER_TOO_LARGE) { + debug(11, 1) ("WARNING: %s:%d: HTTP header too large\n", __FILE__, __LINE__); + storeEntryReset(entry); + fwdFail(httpState->fwd, errorCon(ERR_TOO_BIG, HTTP_BAD_GATEWAY, httpState->fwd->request)); + httpState->fwd->flags.dont_retry = 1; + comm_close(fd); + return; + } + if (s == HTTP_INVALID_HEADER && !(entry->mem_obj->reply->sline.version.major == 0 && entry->mem_obj->reply->sline.version.minor == 9)) { + storeEntryReset(entry); + fwdFail(httpState->fwd, errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY, httpState->fwd->request)); + httpState->fwd->flags.dont_retry = 1; + comm_close(fd); + return; + } +#if WIP_FWD_LOG + fwdStatus(httpState->fwd, s); +#endif + /* + * If its not a reply that we will re-forward, then + * allow the client to get it. + */ + if (!fwdReforwardableStatus(s)) + EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); + } + } +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) { + debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); + if (cbdataValid(httpState->icap_writer)) { + icapSendRespMod(httpState->icap_writer, buf, len, 0); + httpState->icap_writer->fake_content_length += len; + } + } else +#endif + storeAppend(entry, buf, len); + + + debug(11, 5) ("httpReadReply: after storeAppend FD %d read %d\n", fd, len); +#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, + * in that case, the server FD should already be closed. + * there's nothing for us to do. + */ + return; + } + switch (httpPconnTransferDone(httpState)) { + case 1: + { + int keep_alive = 1; + /* + * If we didn't send a keep-alive request header, then this + * can not be a persistent connection. + */ + if (!httpState->flags.keepalive) + keep_alive = 0; + /* + * If we haven't sent the whole request then this can not be a persistent + * connection. + */ + if (!httpState->flags.request_sent) { + debug(11, 1) ("httpReadReply: Request not yet fully sent \"%s %s\"\n", + RequestMethodStr[orig_request->method], + storeUrl(entry)); + keep_alive = 0; + } + /* + * What does the reply have to say about keep-alive? + */ + if (!entry->mem_obj->reply->keep_alive) + keep_alive = 0; + /* + * Verify that the connection is clean + */ + if (len == read_sz) { + statCounter.syscalls.sock.reads++; + len = FD_READ_METHOD(fd, buf, SQUID_TCP_SO_RCVBUF); + if ((len < 0 && !ignoreErrno(errno)) || len == 0) { + keep_alive = 0; + } else if (len > 0) { + debug(11, Config.onoff.relaxed_header_parser <= 0 || keep_alive ? 1 : 2) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[orig_request->method], + storeUrl(entry)); +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) { + debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); + icapSendRespMod(httpState->icap_writer, buf, len, 0); + httpState->icap_writer->fake_content_length += len; + } else +#endif + storeAppend(entry, buf, len); + keep_alive = 0; + } + } +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) + icapSendRespMod(httpState->icap_writer, NULL, 0, 1); +#endif + if (keep_alive) { + int pinned = 0; +#if LINUX_TPROXY + if (orig_request->flags.tproxy) { + client_addr = &httpState->request->client_addr; + } +#endif + /* yes we have to clear all these! */ + commSetDefer(fd, NULL, NULL); + commSetTimeout(fd, -1, NULL, NULL); + commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); +#if DELAY_POOLS + delayClearNoDelay(fd); +#endif + comm_remove_close_handler(fd, httpStateFree, httpState); + fwdUnregister(fd, httpState->fwd); + if (request->flags.pinned) { + pinned = 1; + } else if (request->flags.connection_auth && request->flags.auth_sent) { + pinned = 1; + } + if (orig_request->pinned_connection && pinned) { + clientPinConnection(orig_request->pinned_connection, fd, orig_request, httpState->peer, request->flags.connection_auth); + } else if (httpState->peer) { + if (httpState->peer->options.originserver) + pconnPush(fd, httpState->peer->name, httpState->peer->http_port, httpState->orig_request->host, client_addr, client_port); + else + pconnPush(fd, httpState->peer->name, httpState->peer->http_port, NULL, client_addr, client_port); + } else { + pconnPush(fd, request->host, request->port, NULL, client_addr, client_port); + } + fwdComplete(httpState->fwd); + httpState->fd = -1; + httpStateFree(fd, httpState); + } else { + fwdComplete(httpState->fwd); + comm_close(fd); + } + } + return; + case 0: + /* Wait for more data or EOF condition */ + if (httpState->flags.keepalive_broken) { + commSetTimeout(fd, 10, NULL, NULL); + } else { + commSetTimeout(fd, Config.Timeout.read, NULL, NULL); + } + commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); + return; + case -1: + /* Server is nasty on us. Shut down */ + debug(11, Config.onoff.relaxed_header_parser <= 0 || entry->mem_obj->reply->keep_alive ? 1 : 2) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[orig_request->method], + storeUrl(entry)); +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) + icapSendRespMod(httpState->icap_writer, NULL, 0, 1); +#endif + fwdComplete(httpState->fwd); + comm_close(fd); + return; + default: + fatal("Unexpected httpPconnTransferDone() status\n"); + break; + } + } +} + +#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 +httpSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) +{ + HttpStateData *httpState = data; + StoreEntry *entry = httpState->entry; + debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n", + fd, (int) size, errflag); +#if URL_CHECKSUM_DEBUG + assert(entry->mem_obj->chksum == url_checksum(entry->mem_obj->url)); +#endif + if (size > 0) { + fd_bytes(fd, size, FD_WRITE); + kb_incr(&statCounter.server.all.kbytes_out, size); + kb_incr(&statCounter.server.http.kbytes_out, size); + } + if (errflag == COMM_ERR_CLOSING) + return; + if (errflag) { + ErrorState *err; + err = errorCon(ERR_WRITE_ERROR, HTTP_BAD_GATEWAY, httpState->fwd->request); + err->xerrno = errno; + fwdFail(httpState->fwd, err); + 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", 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 + * fully written to the server-side. If we start the timeout + * after connection establishment, then we are likely to hit + * 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); +#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; +} + +/* + * build request headers and append them to a given MemBuf + * used by httpBuildRequestPrefix() + * note: calls httpHeaderInit(), the caller is responsible for Clean()-ing + */ +void +httpBuildRequestHeader(request_t * request, + request_t * orig_request, + StoreEntry * entry, + HttpHeader * hdr_out, + http_state_flags flags) +{ + /* building buffer for complex strings */ +#define BBUF_SZ (MAX_URL+32) + LOCAL_ARRAY(char, bbuf, BBUF_SZ); + String strConnection = StringNull; + const HttpHeader *hdr_in = &orig_request->header; + int we_do_ranges; + const HttpHeaderEntry *e; + String strFwd; + HttpHeaderPos pos = HttpHeaderInitPos; + String etags = StringNull; + + httpHeaderInit(hdr_out, hoRequest); + /* append our IMS header */ + if (request->lastmod > -1) + httpHeaderPutTime(hdr_out, HDR_IF_MODIFIED_SINCE, request->lastmod); + if (request->etag) { + etags = httpHeaderGetList(hdr_in, HDR_IF_NONE_MATCH); + strListAddUnique(&etags, request->etag, ','); + } else if (request->etags) { + int i; + etags = httpHeaderGetList(hdr_in, HDR_IF_NONE_MATCH); + for (i = 0; i < request->etags->count; i++) + strListAddUnique(&etags, request->etags->items[i], ','); + } + if (strLen(etags)) + httpHeaderPutStr(hdr_out, HDR_IF_NONE_MATCH, strBuf(etags)); + stringClean(&etags); + /* decide if we want to do Ranges ourselves + * (and fetch the whole object now) + * We want to handle Ranges ourselves iff + * - we can actually parse client Range specs + * - the specs are expected to be simple enough (e.g. no out-of-order ranges) + * - reply will be cachable + * (If the reply will be uncachable we have to throw it away after + * serving this request, so it is better to forward ranges to + * the server and fetch only the requested content) + */ + if (NULL == orig_request->range) + we_do_ranges = 0; + else if (!orig_request->flags.cachable) + we_do_ranges = 0; + else if (orig_request->flags.auth) + we_do_ranges = 0; + else if (httpHdrRangeOffsetLimit(orig_request->range)) + we_do_ranges = 0; + else + we_do_ranges = 1; + debug(11, 8) ("httpBuildRequestHeader: range specs: %p, cachable: %d; we_do_ranges: %d\n", + orig_request->range, orig_request->flags.cachable, we_do_ranges); + + strConnection = httpHeaderGetList(hdr_in, HDR_CONNECTION); + while ((e = httpHeaderGetEntry(hdr_in, &pos))) { + debug(11, 5) ("httpBuildRequestHeader: %s: %s\n", + strBuf(e->name), strBuf(e->value)); + if (!httpRequestHdrAllowed(e, &strConnection)) { + debug(11, 2) ("'%s' header denied by anonymize_headers configuration\n", + strBuf(e->name)); + continue; + } + switch (e->id) { + case HDR_PROXY_AUTHORIZATION: + /* Only pass on proxy authentication to peers for which + * authentication forwarding is explicitly enabled + */ + if (flags.proxying && orig_request->peer_login && strcmp(orig_request->peer_login, "PASS") == 0) { + httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); + if (request->flags.connection_proxy_auth) + request->flags.pinned = 1; + } + break; + case HDR_AUTHORIZATION: + /* Pass on WWW authentication. + */ + if (!flags.originpeer) { + httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); + if (orig_request->flags.connection_auth) + orig_request->flags.pinned = 1; + } else { + /* In accelerators, only forward authentication if enabled + * (see also below for proxy->server authentication) + */ + if (orig_request->peer_login && (strcmp(orig_request->peer_login, "PASS") == 0 || strcmp(orig_request->peer_login, "PROXYPASS") == 0)) { + httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); + if (orig_request->flags.connection_auth) + orig_request->flags.pinned = 1; + } + } + break; + case HDR_HOST: + /* + * Normally Squid rewrites the Host: header. + * However, there is one case when we don't: If the URL + * went through our redirector and the admin configured + * 'redir_rewrites_host' to be off. + */ + if (orig_request->peer_domain) + httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->peer_domain); + else if (request->flags.redirected && !Config.onoff.redir_rewrites_host) + httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); + else { + /* use port# only if not default */ + if (orig_request->port == urlDefaultPort(orig_request->protocol)) { + httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->host); + } else { + httpHeaderPutStrf(hdr_out, HDR_HOST, "%s:%d", + orig_request->host, (int) orig_request->port); + } + } + break; + case HDR_IF_MODIFIED_SINCE: + /* append unless we added our own; + * note: at most one client's ims header can pass through */ + if (!httpHeaderHas(hdr_out, HDR_IF_MODIFIED_SINCE)) + httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); + break; + case HDR_IF_NONE_MATCH: + /* append unless we added our own; + * note: at most one client's ims header can pass through */ + if (!httpHeaderHas(hdr_out, HDR_IF_NONE_MATCH)) + httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); + break; + case HDR_MAX_FORWARDS: + if (orig_request->method == METHOD_TRACE) { + /* sacrificing efficiency over clarity, etc. */ + const int hops = httpHeaderGetInt(hdr_in, HDR_MAX_FORWARDS); + if (hops > 0) + httpHeaderPutInt(hdr_out, HDR_MAX_FORWARDS, hops - 1); + } + break; + case HDR_X_FORWARDED_FOR: + if (!opt_forwarded_for) + httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); + break; + case HDR_RANGE: + case HDR_IF_RANGE: + case HDR_REQUEST_RANGE: + if (!we_do_ranges) + httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); + break; + case HDR_VIA: + /* If Via is disabled then forward any received header as-is */ + if (!Config.onoff.via) + httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); + break; + case HDR_PROXY_CONNECTION: + case HDR_CONNECTION: + case HDR_CACHE_CONTROL: + /* append these after the loop if needed */ + break; + case HDR_FRONT_END_HTTPS: + if (!flags.front_end_https) + httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); + break; + default: + /* pass on all other header fields */ + httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); + } + } + + /* append Via */ + if (Config.onoff.via) { + String strVia = httpHeaderGetList(hdr_in, HDR_VIA); + snprintf(bbuf, BBUF_SZ, "%d.%d %s", + orig_request->http_ver.major, + orig_request->http_ver.minor, ThisCache); + strListAdd(&strVia, bbuf, ','); + httpHeaderPutStr(hdr_out, HDR_VIA, strBuf(strVia)); + stringClean(&strVia); + } + /* append X-Forwarded-For */ + if (opt_forwarded_for) { + strFwd = httpHeaderGetList(hdr_in, HDR_X_FORWARDED_FOR); + strListAdd(&strFwd, + (((orig_request->client_addr.s_addr != no_addr.s_addr) && opt_forwarded_for) ? + inet_ntoa(orig_request->client_addr) : "unknown"), ','); + httpHeaderPutStr(hdr_out, HDR_X_FORWARDED_FOR, strBuf(strFwd)); + stringClean(&strFwd); + } + /* append Host if not there already */ + if (!httpHeaderHas(hdr_out, HDR_HOST)) { + if (orig_request->peer_domain) { + httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->peer_domain); + } else if (orig_request->port == urlDefaultPort(orig_request->protocol)) { + /* use port# only if not default */ + httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->host); + } else { + httpHeaderPutStrf(hdr_out, HDR_HOST, "%s:%d", + orig_request->host, (int) orig_request->port); + } + } + /* append Authorization if known in URL, not in header and going direct */ + if (!httpHeaderHas(hdr_out, HDR_AUTHORIZATION)) { + if (!request->flags.proxying) { + if (*request->login) { + httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", + base64_encode(request->login)); + } else if (orig_request->extacl_user && orig_request->extacl_passwd) { + char loginbuf[256]; + snprintf(loginbuf, sizeof(loginbuf), "%s:%s", orig_request->extacl_user, orig_request->extacl_passwd); + httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", + base64_encode(loginbuf)); + } + } + } + /* append Proxy-Authorization if configured for peer, and proxying */ + if (request->flags.proxying && orig_request->peer_login && + !httpHeaderHas(hdr_out, HDR_PROXY_AUTHORIZATION)) { + if (*orig_request->peer_login == '*') { + /* Special mode, to pass the username to the upstream cache */ + char loginbuf[256]; + const char *username = "-"; + if (orig_request->auth_user_request) + username = authenticateUserRequestUsername(orig_request->auth_user_request); + else if (orig_request->extacl_user) + username = orig_request->extacl_user; + snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, orig_request->peer_login + 1); + httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s", + base64_encode(loginbuf)); + } else if (strcmp(orig_request->peer_login, "PASS") == 0) { + if (orig_request->extacl_user && orig_request->extacl_passwd) { + char loginbuf[256]; + snprintf(loginbuf, sizeof(loginbuf), "%s:%s", orig_request->extacl_user, orig_request->extacl_passwd); + httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s", + base64_encode(loginbuf)); + } + } else if (strcmp(orig_request->peer_login, "PROXYPASS") == 0) { + /* Nothing to do */ + } else { + httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s", + base64_encode(orig_request->peer_login)); + } + } + /* append WWW-Authorization if configured for peer */ + if (flags.originpeer && orig_request->peer_login && + !httpHeaderHas(hdr_out, HDR_AUTHORIZATION)) { + if (strcmp(orig_request->peer_login, "PASS") == 0) { + /* No credentials to forward.. (should have been done above if available) */ + } else if (strcmp(orig_request->peer_login, "PROXYPASS") == 0) { + /* Special mode, convert proxy authentication to WWW authentication + * (also applies to cookie authentication) + */ + const char *auth = httpHeaderGetStr(hdr_in, HDR_PROXY_AUTHORIZATION); + if (auth && strncasecmp(auth, "basic ", 6) == 0) { + httpHeaderPutStr(hdr_out, HDR_AUTHORIZATION, auth); + if (orig_request->flags.connection_auth) + orig_request->flags.pinned = 1; + } else if (orig_request->extacl_user && orig_request->extacl_passwd) { + char loginbuf[256]; + snprintf(loginbuf, sizeof(loginbuf), "%s:%s", orig_request->extacl_user, orig_request->extacl_passwd); + httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", + base64_encode(loginbuf)); + } + } else if (*orig_request->peer_login == '*') { + /* Special mode, to pass the username to the upstream cache */ + char loginbuf[256]; + const char *username = "-"; + if (orig_request->auth_user_request) + username = authenticateUserRequestUsername(orig_request->auth_user_request); + else if (orig_request->extacl_user) + username = orig_request->extacl_user; + snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, orig_request->peer_login + 1); + httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", + base64_encode(loginbuf)); + } else { + /* Fixed login string */ + httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", + base64_encode(orig_request->peer_login)); + } + } + /* append Cache-Control, add max-age if not there already */ + { + HttpHdrCc *cc = httpHeaderGetCc(hdr_in); + if (!cc) + cc = httpHdrCcCreate(); + 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)) + EBIT_SET(cc->mask, CC_NO_CACHE); + /* Enforce sibling relations */ + if (flags.only_if_cached) + EBIT_SET(cc->mask, CC_ONLY_IF_CACHED); + httpHeaderPutCc(hdr_out, cc); + httpHdrCcDestroy(cc); + } + /* maybe append Connection: keep-alive */ + if (flags.keepalive || request->flags.pinned) { + if (flags.proxying) { + httpHeaderPutStr(hdr_out, HDR_PROXY_CONNECTION, "keep-alive"); + } else { + httpHeaderPutStr(hdr_out, HDR_CONNECTION, "keep-alive"); + } + } + /* append Front-End-Https */ + if (flags.front_end_https) { + if (flags.front_end_https == 1 || request->protocol == PROTO_HTTPS) + httpHeaderPutStr(hdr_out, HDR_FRONT_END_HTTPS, "On"); + } + /* Now mangle the headers. */ + httpHdrMangleList(hdr_out, orig_request); + stringClean(&strConnection); +} + +/* build request prefix and append it to a given MemBuf; + * return the length of the prefix */ +int +httpBuildRequestPrefix(request_t * request, + request_t * orig_request, + StoreEntry * entry, + MemBuf * mb, + http_state_flags flags) +{ + const int offset = mb->size; + memBufPrintf(mb, "%s %s HTTP/1.0\r\n", + RequestMethodStr[request->method], + strLen(request->urlpath) ? strBuf(request->urlpath) : "/"); + /* build and pack headers */ + { + HttpHeader hdr; + Packer p; + httpBuildRequestHeader(request, orig_request, entry, &hdr, flags); + if (request->flags.pinned && request->flags.connection_auth) + request->flags.auth_sent = 1; + else + request->flags.auth_sent = httpHeaderHas(&hdr, HDR_AUTHORIZATION); + packerToMemInit(&p, mb); + httpHeaderPackInto(&hdr, &p); + httpHeaderClean(&hdr); + packerClean(&p); + } + /* append header terminator */ + memBufAppend(mb, crlf, 2); + return mb->size - offset; +} +/* This will be called when connect completes. Write request. */ +static void +httpSendRequest(HttpStateData * httpState) +{ + MemBuf mb; + request_t *req = httpState->request; + StoreEntry *entry = httpState->entry; + peer *p = httpState->peer; + CWCB *sendHeaderDone; + int fd = httpState->fd; + + debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", fd, httpState); + + /* Schedule read reply. (but no timeout set until request fully sent) */ + commSetTimeout(fd, Config.Timeout.lifetime, httpTimeout, httpState); + commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); + + if (httpState->orig_request->body_reader) + sendHeaderDone = httpSendRequestEntry; + else + sendHeaderDone = httpSendComplete; + + if (p != NULL) { + if (p->options.originserver) + httpState->flags.originpeer = 1; + else + httpState->flags.proxying = 1; + } else { + httpState->flags.proxying = 0; + httpState->flags.originpeer = 0; + } + /* + * Is keep-alive okay for all request methods? + */ + if (!Config.onoff.server_pconns) + httpState->flags.keepalive = 0; + else if (p == NULL) + httpState->flags.keepalive = 1; + else if (p->stats.n_keepalives_sent < 10) + httpState->flags.keepalive = 1; + else if ((double) p->stats.n_keepalives_recv / (double) p->stats.n_keepalives_sent > 0.50) + httpState->flags.keepalive = 1; + if (httpState->peer) { + if (neighborType(httpState->peer, httpState->request) == PEER_SIBLING && + !httpState->peer->options.allow_miss) + httpState->flags.only_if_cached = 1; + httpState->flags.front_end_https = httpState->peer->front_end_https; + } + memBufDefInit(&mb); + httpBuildRequestPrefix(req, + httpState->orig_request, + entry, + &mb, + httpState->flags); + if (req->flags.pinned) + httpState->flags.keepalive = 1; + debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", fd, mb.buf); + comm_write_mbuf(fd, mb, sendHeaderDone, httpState); +} + +void +httpStart(FwdState * fwd) +{ + 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", + RequestMethodStr[orig_req->method], + storeUrl(fwd->entry)); + httpState = cbdataAlloc(HttpStateData); + storeLockObject(fwd->entry); + httpState->fwd = fwd; + httpState->entry = fwd->entry; + httpState->fd = fd; + if (fwd->servers) + httpState->peer = fwd->servers->peer; /* might be NULL */ + if (httpState->peer) { + const char *url; + if (httpState->peer->options.originserver) + url = strBuf(orig_req->urlpath); + else + url = storeUrl(httpState->entry); + proxy_req = requestCreate(orig_req->method, + orig_req->protocol, url); + xstrncpy(proxy_req->host, httpState->peer->host, SQUIDHOSTNAMELEN); + proxy_req->port = httpState->peer->http_port; + proxy_req->flags = orig_req->flags; + proxy_req->lastmod = orig_req->lastmod; + httpState->request = requestLink(proxy_req); + httpState->orig_request = requestLink(orig_req); + proxy_req->flags.proxying = 1; + /* + * This NEIGHBOR_PROXY_ONLY check probably shouldn't be here. + * We might end up getting the object from somewhere else if, + * for example, the request to this neighbor fails. + */ + if (httpState->peer->options.proxy_only) + storeReleaseRequest(httpState->entry); +#if DELAY_POOLS + assert(delayIsNoDelay(fd) == 0); + if (httpState->peer->options.no_delay) + delaySetNoDelay(fd); +#endif + } else { + 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. + * Now its set in httpSendComplete() after the full request, + * including request body, has been written to the server. + */ +} + +static void +httpSendRequestEntryDone(int fd, void *data) +{ + HttpStateData *httpState = data; + aclCheck_t ch; + debug(11, 5) ("httpSendRequestEntryDone: FD %d\n", + fd); + memset(&ch, '\0', sizeof(ch)); + ch.request = httpState->request; + if (!Config.accessList.brokenPosts) { + debug(11, 5) ("httpSendRequestEntryDone: No brokenPosts list\n"); + httpSendComplete(fd, NULL, 0, 0, data); + } else if (!aclCheckFast(Config.accessList.brokenPosts, &ch)) { + debug(11, 5) ("httpSendRequestEntryDone: didn't match brokenPosts\n"); + httpSendComplete(fd, NULL, 0, 0, data); + } else { + debug(11, 2) ("httpSendRequestEntryDone: matched brokenPosts\n"); + comm_write(fd, "\r\n", 2, httpSendComplete, data, NULL); + } +} + +static void +httpRequestBodyHandler2(void *data) +{ + HttpStateData *httpState = (HttpStateData *) data; + char *buf = httpState->body_buf; + httpState->body_buf = NULL; + comm_write(httpState->fd, buf, httpState->body_buf_sz, httpSendRequestEntry, data, memFree8K); +} + +static void +httpRequestBodyHandler(char *buf, ssize_t size, void *data) +{ + HttpStateData *httpState = (HttpStateData *) data; + httpState->body_buf = NULL; + if (size > 0) { + if (httpState->reply_hdr_state >= 2 && !httpState->flags.abuse_detected) { + httpState->flags.abuse_detected = 1; + debug(11, 1) ("httpSendRequestEntryDone: Likely proxy abuse detected '%s' -> '%s'\n", + inet_ntoa(httpState->orig_request->client_addr), + storeUrl(httpState->entry)); + if (httpState->entry->mem_obj->reply->sline.status == HTTP_INVALID_HEADER) { + memFree8K(buf); + comm_close(httpState->fd); + return; + } + httpState->body_buf = buf; + httpState->body_buf_sz = size; + /* Give response some time to propagate before sending rest + * of request in case of error */ + eventAdd("POST delay on response", httpRequestBodyHandler2, httpState, 2.0, 1); + return; + } + comm_write(httpState->fd, buf, size, httpSendRequestEntry, data, memFree8K); + } else if (size == 0) { + /* End of body */ + memFree8K(buf); + httpSendRequestEntryDone(httpState->fd, data); + } else { + /* Failed to get whole body, probably aborted */ + memFree8K(buf); + httpSendComplete(httpState->fd, NULL, 0, COMM_ERR_CLOSING, data); + } +} + +static void +httpSendRequestEntry(int fd, char *bufnotused, size_t size, int errflag, void *data) +{ + HttpStateData *httpState = data; + StoreEntry *entry = httpState->entry; + debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n", + fd, (int) size, errflag); + if (size > 0) { + fd_bytes(fd, size, FD_WRITE); + kb_incr(&statCounter.server.all.kbytes_out, size); + kb_incr(&statCounter.server.http.kbytes_out, size); + } + if (errflag == COMM_ERR_CLOSING) + return; + if (errflag) { + ErrorState *err; + err = errorCon(ERR_WRITE_ERROR, HTTP_BAD_GATEWAY, httpState->fwd->request); + err->xerrno = errno; + fwdFail(httpState->fwd, err); + comm_close(fd); + return; + } + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + comm_close(fd); + return; + } + httpState->body_buf = memAllocate(MEM_8K_BUF); + requestReadBody(httpState->orig_request, httpState->body_buf, 8192, httpRequestBodyHandler, httpState); +} + +void +httpBuildVersion(http_version_t * version, unsigned int major, unsigned int minor) +{ + version->major = major; + version->minor = minor; +} diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/d9/4048b6315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/d9/4048b6315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/d9/4048b6315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/d9/4048b6315c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,32 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/fs/ufs/store_dir_ufs.c \ +$(ROOT)/fs/ufs/store_io_ufs.c + +OBJS += \ +./fs/ufs/store_dir_ufs.o \ +./fs/ufs/store_io_ufs.o + +DEPS += \ +${addprefix ./fs/ufs/, \ +store_dir_ufs.d \ +store_io_ufs.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +fs/ufs/%.o: $(ROOT)/fs/ufs/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/de/9075cf315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/de/9075cf315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/de/9075cf315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/de/9075cf315c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,35 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/fs/coss/async_io.c \ +$(ROOT)/fs/coss/store_dir_coss.c \ +$(ROOT)/fs/coss/store_io_coss.c + +OBJS += \ +./fs/coss/async_io.o \ +./fs/coss/store_dir_coss.o \ +./fs/coss/store_io_coss.o + +DEPS += \ +${addprefix ./fs/coss/, \ +async_io.d \ +store_dir_coss.d \ +store_io_coss.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +fs/coss/%.o: $(ROOT)/fs/coss/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/e7/d04236315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/e7/d04236315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/e7/d04236315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/e7/d04236315c88001b16add8656805b17d 2006-12-10 16:09:19.000000000 +0200 @@ -0,0 +1,988 @@ + +/* + * $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. + * + */ + +#include "squid.h" + +#define ICAP_PROXY_KEEP_ALIVE 0 + +/* + * These once-static functions are required to be global for ICAP + */ + +PF clientReadRequest; +PF connStateFree; +StoreEntry *clientCreateStoreEntry(clientHttpRequest *, method_t, request_flags); +int clientReadDefer(int fd, void *data); +int clientCheckContentLength(request_t * r); +void clientProcessRequest(clientHttpRequest *); +int clientCachable(clientHttpRequest *); +int clientHierarchical(clientHttpRequest *); +void clientReadBody(request_t * request, char *buf, size_t size, + CBCB * callback, void *cbdata); +static void icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, + CBCB * callback, void *cbdata); + +static PF icapReqModReadHttpHdrs; +static PF icapReqModReadHttpBody; +static CWCB icapReqModSendBodyChunk; +static CBCB icapReqModBodyHandler; +static BODY_HANDLER icapReqModBodyReader; +static STRCB icapReqModMemBufAppend; + +#define EXPECTED_ICAP_HEADER_LEN 256 +static const char *crlf = "\r\n"; + +/* + * icapExpectedHttpReqHdrSize + * + * calculate the size of the HTTP headers that we expect + * to read from the ICAP server. + */ +static int +icapExpectedHttpReqHdrSize(IcapStateData * icap) +{ + if (icap->enc.req_body > -1 && icap->enc.req_hdr > -1) + return (icap->enc.req_body - icap->enc.req_hdr); + if (icap->enc.null_body > -1) + return icap->enc.null_body; + fatal("icapExpectedHttpReqHdrSize: unexpected case"); + return 0; +} + +/* + * icapReqModCreateClientState + * + * Creates fake client_side data structures so we can use + * that module to read/parse the HTTP request that we read + * from the ICAP server. + */ +static clientHttpRequest * +icapReqModCreateClientState(IcapStateData * icap, request_t * request) +{ + clientHttpRequest *http; + if (!cbdataValid(icap->reqmod.client_cookie)) { + debug(81, 3) ("Whups, client cookie invalid\n"); + icap->reqmod.client_fd = -1; + return NULL; + } + http = cbdataAlloc(clientHttpRequest); + /* + * use our own urlCanonicalClean here, because urlCanonicalClean + * may strip everything after a question-mark. As http->uri + * is used when doing a request to a parent proxy, we need the full + * url here. + */ + http->uri = xstrdup(urlCanonical(icap->request)); + http->log_uri = xstrndup(http->uri, MAX_URL); + http->range_iter.boundary = StringNull; + http->request = requestLink(request ? request : icap->request); + http->flags.did_icap_reqmod = 1; + http->start = icap->reqmod.start; + if (request) + http->http_ver = request->http_ver; +#if ICAP_PROXY_KEEP_ALIVE + /* + * Here it is possible becouse we are using as client_cookie the original http->conn + * if we will keep this code we must declare an icap->conn field........ + * Will work if pipeline_prefetch is not enabled + * We are using a dummy ConnStateData structure, just to free + * old clientHttpRequest :-( + * OK,all this code is a hack and possibly must not exists in cvs ...... + */ + + http->conn = icap->reqmod.client_cookie; + assert(http->conn->chr->next == NULL); + { + ConnStateData *dummyconn; + dummyconn = cbdataAlloc(ConnStateData); + dummyconn->fd = icap->reqmod.client_fd; + dummyconn->pinning.fd = -1; + dummyconn->chr = http->conn->chr; + dummyconn->chr->conn = dummyconn; + comm_add_close_handler(dummyconn->fd, connStateFree, dummyconn); + } + http->conn->chr = http; +#else + http->conn = cbdataAlloc(ConnStateData); + http->conn->fd = icap->reqmod.client_fd; + http->conn->pinning.fd = -1; + http->conn->in.size = 0; + http->conn->in.buf = NULL; + http->conn->log_addr = icap->reqmod.log_addr; + http->conn->chr = http; + comm_add_close_handler(http->conn->fd, connStateFree, http->conn); +#endif + http->icap_reqmod = NULL; + return http; +} + +/* + * icapReqModInterpretHttpRequest + * + * Interpret an HTTP request that we read from the ICAP server. + * Create some "fake" clientHttpRequest and ConnStateData structures + * so we can pass this new request off to the routines in + * client_side.c. + */ +static void +icapReqModInterpretHttpRequest(IcapStateData * icap, request_t * request) +{ + clientHttpRequest *http = icapReqModCreateClientState(icap, request); + if (NULL == http) + return; + /* + * bits from clientReadRequest + */ + request->content_length = httpHeaderGetSize(&request->header, + HDR_CONTENT_LENGTH); + if (!urlCheckRequest(request) || + httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) { + ErrorState *err; + err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED, request); + request->flags.proxy_keepalive = 0; + http->entry = + clientCreateStoreEntry(http, request->method, null_request_flags); + errorAppendEntry(http->entry, err); + return; + } + if (!clientCheckContentLength(request)) { + ErrorState *err; + err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED, request); + http->entry = + clientCreateStoreEntry(http, request->method, null_request_flags); + errorAppendEntry(http->entry, err); + return; + } + /* Do we expect a request-body? */ + if (request->content_length > 0) { + debug(81, 5) ("handing request bodies in ICAP REQMOD\n"); + if (request->body_reader_data) + cbdataUnlock(request->body_reader_data); + request->body_reader = icapReqModBodyReader; + request->body_reader_data = icap; /* XXX cbdataLock? */ + cbdataLock(icap); /*Yes sure ..... */ + memBufDefInit(&icap->reqmod.http_entity.buf); + } + if (clientCachable(http)) + request->flags.cachable = 1; + if (clientHierarchical(http)) + request->flags.hierarchical = 1; + clientProcessRequest(http); +} + +/* + * icapReqModParseHttpError + * + * Handle an error when parsing the new HTTP request we read + * from the ICAP server. + */ +static void +icapReqModParseHttpError(IcapStateData * icap, const char *reason) +{ + debug(81, 1) ("icapReqModParseHttpError: %s\n", reason); +} + +/* + * icapEntryError + * + * A wrapper for errorCon() and errorAppendEntry(). + */ +static void +icapEntryError(IcapStateData * icap, err_type et, http_status hs, int xerrno) +{ + ErrorState *err; + clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); + if (NULL == http) + return; + http->entry = clientCreateStoreEntry(http, + icap->request->method, null_request_flags); + err = errorCon(et, hs, icap->request); + err->xerrno = xerrno; + errorAppendEntry(http->entry, err); +} + +/* + * icapReqModParseHttpRequest + * + * Parse the HTTP request that we read from the ICAP server. + * Creates and fills in the request_t structure. + */ +static void +icapReqModParseHttpRequest(IcapStateData * icap) +{ + char *mstr; + char *uri; + char *inbuf; + char *t; + char *token; + char *headers; + method_t method; + request_t *request; + http_version_t http_ver; + int reqlen = icap->reqmod.hdr_buf.size; + int hdrlen; + + /* + * Lazy, make a copy of the buf so I can chop it up with strtok() + */ + inbuf = xcalloc(reqlen + 1, 1); + memcpy(inbuf, icap->reqmod.hdr_buf.buf, reqlen); + + if ((mstr = strtok(inbuf, "\t ")) == NULL) { + debug(81, 1) ("icapReqModParseHttpRequest: Can't get request method\n"); + icapReqModParseHttpError(icap, "error:invalid-request-method"); + xfree(inbuf); + return; + } + method = urlParseMethod(mstr); + if (method == METHOD_NONE) { + debug(81, 1) ("icapReqModParseHttpRequest: Unsupported method '%s'\n", + mstr); + icapReqModParseHttpError(icap, "error:unsupported-request-method"); + xfree(inbuf); + return; + } + /* look for URL+HTTP/x.x */ + if ((uri = strtok(NULL, "\n")) == NULL) { + debug(81, 1) ("icapReqModParseHttpRequest: Missing URI\n"); + icapReqModParseHttpError(icap, "error:missing-url"); + xfree(inbuf); + return; + } + while (xisspace(*uri)) + uri++; + t = uri + strlen(uri); + assert(*t == '\0'); + token = NULL; + while (t > uri) { + t--; + if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) { + token = t + 1; + break; + } + } + while (t > uri && xisspace(*t)) + *(t--) = '\0'; + debug(81, 5) ("icapReqModParseHttpRequest: URI is '%s'\n", uri); + if (token == NULL) { + debug(81, 3) ("icapReqModParseHttpRequest: Missing HTTP identifier\n"); + icapReqModParseHttpError(icap, "error:missing-http-ident"); + xfree(inbuf); + return; + } + if (sscanf(token + 5, "%d.%d", &http_ver.major, &http_ver.minor) != 2) { + debug(81, 3) ("icapReqModParseHttpRequest: Invalid HTTP identifier.\n"); + icapReqModParseHttpError(icap, "error:invalid-http-ident"); + xfree(inbuf); + return; + } + debug(81, 6) ("icapReqModParseHttpRequest: Client HTTP version %d.%d.\n", + http_ver.major, http_ver.minor); + + headers = strtok(NULL, null_string); + hdrlen = inbuf + reqlen - headers; + + if ((request = urlParse(method, uri)) == NULL) { + debug(81, 3) ("Invalid URL: %s at %s:%d\n", uri, __FILE__, __LINE__); + icapEntryError(icap, ERR_INVALID_URL, HTTP_BAD_REQUEST, 0); + xfree(inbuf); + return; + } + /* compile headers */ + if (!httpHeaderParse(&request->header, headers, headers + hdrlen)) { + debug(81, 3) ("Failed to parse HTTP headers for: %s at %s:%d", + uri, __FILE__, __LINE__); + icapEntryError(icap, ERR_INVALID_REQ, HTTP_BAD_REQUEST, 0); + xfree(inbuf); + return; + } + debug(81, + 3) + ("icapReqModParseHttpRequest: successfully parsed the HTTP request\n"); + request->http_ver = http_ver; + request->client_addr = icap->request->client_addr; + request->client_port = icap->request->client_port; + request->my_addr = icap->request->my_addr; + request->my_port = icap->request->my_port; + request->class = icap->request->class; + if (icap->request->auth_user_request) { + /* Copy authentification info in new request */ + request->auth_user_request = icap->request->auth_user_request; + authenticateAuthUserRequestLock(request->auth_user_request); + } + request->content_length = httpHeaderGetSize(&request->header, + HDR_CONTENT_LENGTH); + if (strBuf(icap->request->extacl_log)) + request->extacl_log = stringDup(&icap->request->extacl_log); + if (icap->request->extacl_user) + request->extacl_user = xstrdup(icap->request->extacl_user); + if (icap->request->extacl_passwd) + request->extacl_passwd = xstrdup(icap->request->extacl_passwd); +#if ICAP_PROXY_KEEP_ALIVE + /* + * Copy the proxy_keepalive flag from the original request + */ + request->flags.proxy_keepalive = icap->request->flags.proxy_keepalive; + /* + * If proxy_keepalive was set for the original request, make + * sure that the adapated request also has the necessary headers + * for keepalive + */ + if (request->flags.proxy_keepalive) { + if (!httpMsgIsPersistent(http_ver, &request->header)) + request->flags.proxy_keepalive = 0; + } +#endif + icapReqModInterpretHttpRequest(icap, request); + xfree(inbuf); +} + +/* + * icapReqModHandoffRespMod + * + * Handles the case where a REQMOD request results in an HTTP REPLY + * (instead of an ICAP REPLY that contains a new HTTP REQUEST). We + * prepare the IcapStateData for passing off to the icap_reqmod + * code, where we have functions for reading HTTP replies in ICAP + * messages. + */ +static void +icapReqModHandoffRespMod(IcapStateData * icap) +{ + extern PF icapReadReply; + clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); + if (NULL == http) + return; + assert(icap->request); + http->entry = clientCreateStoreEntry(http, + icap->request->method, icap->request->flags); + icap->respmod.entry = http->entry; + storeLockObject(icap->respmod.entry); + + /* icap->http_flags = ? */ + memBufDefInit(&icap->respmod.buffer); + memBufDefInit(&icap->chunk_buf); + assert(icap->current_service); + icapReadReply(icap->icap_fd, icap); +} + +/* + * icapReqModKeepAliveOrClose + * + * Called when we are done reading from the ICAP server. + * Either close the connection or keep it open for a future + * transaction. + */ +static void +icapReqModKeepAliveOrClose(IcapStateData * icap) +{ + int fd = icap->icap_fd; + debug(81, 3) ("%s:%d FD %d\n", __FILE__, __LINE__, fd); + if (fd < 0) + return; + if (!icap->flags.keep_alive) { + debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, + __LINE__); + comm_close(fd); + return; + } + if (icap->request->content_length < 0) { + /* no message body */ + debug(81, 3) ("%s:%d no message body\n", __FILE__, __LINE__); + if (1 != icap->reqmod.hdr_state) { + /* didn't get to end of HTTP headers */ + debug(81, 3) ("%s:%d didnt find end of headers, closing\n", + __FILE__, __LINE__); + comm_close(fd); + return; + } + } else if (icap->reqmod.http_entity.bytes_read != + icap->request->content_length) { + debug(81, 3) ("%s:%d bytes_read (%" PRINTF_OFF_T ") != content_length (%" PRINTF_OFF_T ")\n", + __FILE__, __LINE__, icap->reqmod.http_entity.bytes_read, + icap->request->content_length); + /* an error */ + comm_close(fd); + return; + } + debug(81, 3) ("%s:%d looks good, keeping alive\n", __FILE__, __LINE__); + commSetDefer(fd, NULL, NULL); + commSetTimeout(fd, -1, NULL, NULL); + commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); + comm_remove_close_handler(fd, icapStateFree, icap); + pconnPush(fd, icap->current_service->hostname, icap->current_service->port, NULL, NULL, 0); + icap->icap_fd = -1; + icapStateFree(-1, icap); +} + +/* + * icapReqModReadHttpHdrs + * + * Read the HTTP reply from the ICAP server. Uses the values + * from the ICAP Encapsulation header to know how many bytes + * to read. + */ +static void +icapReqModReadHttpHdrs(int fd, void *data) +{ + IcapStateData *icap = data; + LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); + int rl; + debug(81, 3) ("icapReqModReadHttpHdrs:\n"); + assert(fd == icap->icap_fd); + assert(icap->enc.req_hdr == 0); + if (0 == icap->reqmod.hdr_state) { + int expect = icapExpectedHttpReqHdrSize(icap); + int so_far = icap->http_header_bytes_read_so_far; + int needed = expect - so_far; + debug(81, 3) ("expect=%d\n", expect); + debug(81, 3) ("so_far=%d\n", so_far); + debug(81, 3) ("needed=%d\n", needed); + assert(needed >= 0); + if (0 == expect) { + fatalf("unexpected condition in %s:%d", __FILE__, __LINE__); + } + rl = FD_READ_METHOD(fd, tmpbuf, needed); + debug(81, 3) ("icapReqModReadHttpHdrs: read %d bytes\n", rl); + if (rl < 0) { + fatalf("need to handle read error at %s:%d", __FILE__, __LINE__); + } + fd_bytes(fd, rl, FD_READ); + kb_incr(&statCounter.icap.all.kbytes_in, rl); + memBufAppend(&icap->reqmod.hdr_buf, tmpbuf, rl); + icap->http_header_bytes_read_so_far += rl; + if (rl != needed) { + /* still more header data to read */ + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, + 0); + return; + } + icap->reqmod.hdr_state = 1; + } + assert(1 == icap->reqmod.hdr_state); + debug(81, 3) ("icapReqModReadHttpHdrs: read the entire request headers\n"); + icapReqModParseHttpRequest(icap); + if (-1 == icap->reqmod.client_fd) { + /* we detected that the original client_side went away */ + icapReqModKeepAliveOrClose(icap); + } else if (icap->enc.req_body > -1) { + icap->chunk_size = 0; + memBufDefInit(&icap->chunk_buf); + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); + } else { + icapReqModKeepAliveOrClose(icap); + } +} + + +/* + * icapReqModReadIcapPart + * + * Read the ICAP reply header. + */ +static void +icapReqModReadIcapPart(int fd, void *data) +{ + IcapStateData *icap = data; + int version_major, version_minor; + const char *str_status; + int x; + const char *start; + const char *end; + int status; + int isIcap = 0; + int directResponse = 0; + + debug(81, 5) ("icapReqModReadIcapPart: FD %d httpState = %p\n", fd, data); + statCounter.syscalls.sock.reads++; + + x = icapReadHeader(fd, icap, &isIcap); + if (x < 0) { + /* Did not find a proper ICAP response */ + debug(81, 3) ("ICAP : Error path!\n"); + icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, + errno); + comm_close(fd); + return; + } + if (x == 0) { + /* + * Waiting for more headers. Schedule new read hander, but + * don't reset timeout. + */ + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); + return; + } + /* + * Parse the ICAP header + */ + assert(icap->icap_hdr.size); + debug(81, 3) ("Read icap header : <%s>\n", icap->icap_hdr.buf); + if ((status = + icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, + &version_major, &version_minor, &str_status)) < 0) { + debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); + /* is this correct in case of ICAP protocol error? */ + icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, + errno); + comm_close(fd); + return; + }; + if (200 != status && 201 != status) { + debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); + icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, + errno); + comm_close(fd); + return; + } + icapSetKeepAlive(icap, icap->icap_hdr.buf); + if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { + icapParseEncapsulated(icap, start, end); + } else { + debug(81, + 1) + ("WARNING: icapReqModReadIcapPart() did not find 'Encapsulated' header\n"); + } + if (icap->enc.res_hdr > -1) + directResponse = 1; + else if (icap->enc.res_body > -1) + directResponse = 1; + else + directResponse = 0; + debug(81, 3) ("icapReqModReadIcapPart: directResponse=%d\n", + directResponse); + + /* Check whether it is a direct reply - if so over to http part */ + if (directResponse) { + debug(81, + 3) + ("icapReqModReadIcapPart: FD %d, processing HTTP response for REQMOD!\n", + fd); + /* got the reply, no need to come here again */ + icap->flags.wait_for_reply = 0; + icap->flags.got_reply = 1; + icapReqModHandoffRespMod(icap); + return; + } + memBufDefInit(&icap->reqmod.hdr_buf); + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, 0); + return; +} + +/* + * icapSendReqModDone + * + * Called after we've sent the ICAP request. Checks for errors + * and installs the handler functions for the next step. + */ +static void +icapSendReqModDone(int fd, char *bufnotused, size_t size, int errflag, + void *data) +{ + IcapStateData *icap = data; + + debug(81, 5) ("icapSendReqModDone: 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 == COMM_ERR_CLOSING) + return; + if (errflag) { + debug(81, 3) ("icapSendReqModDone: unreachable=1, service=%s\n", + icap->current_service->uri); + icapOptSetUnreachable(icap->current_service); + icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, + errno); + comm_close(fd); + return; + } + /* Schedule read reply. */ + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); + /* + * Set the read timeout here because it hasn't been set yet. + * We only set the read timeout after the request has been + * fully written to the server-side. If we start the timeout + * after connection establishment, then we are likely to hit + * the timeout for POST/PUT requests that have very large + * request bodies. + */ + commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, icap); +} + + +/* + * icapSendReqMod + * + * Send the ICAP request, including HTTP request, to the ICAP server + * after connection has been established. + */ +static void +icapSendReqMod(int fd, int status, void *data) +{ + MemBuf mb; + MemBuf mb_hdr; + Packer p; + IcapStateData *icap = data; + char *client_addr; + int icap_fd = icap->icap_fd; + icap_service *service; + CWCB *theCallback; + + debug(81, 5) ("icapSendReqMod FD %d, status %d\n", fd, status); + icap->flags.connect_pending = 0; + + if (COMM_OK != status) { + debug(81, 1) ("Could not connect to ICAP server %s:%d: %s\n", + icap->current_service->hostname, + icap->current_service->port, xstrerror()); + debug(81, 3) ("icapSendReqMod: unreachable=1, service=%s\n", + icap->current_service->uri); + icapOptSetUnreachable(icap->current_service); + icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_SERVICE_UNAVAILABLE, errno); + comm_close(fd); + return; + } + fd_table[fd].pconn.uses++; + fd_table[fd].pconn.type = 2; + if (icap->request->content_length > 0) + theCallback = icapReqModSendBodyChunk; + else + theCallback = icapSendReqModDone; + + memBufDefInit(&mb); + memBufDefInit(&mb_hdr); + memBufPrintf(&mb_hdr, "%s %s HTTP/%d.%d\r\n", + RequestMethodStr[icap->request->method], + icap->reqmod.uri, + icap->request->http_ver.major, icap->request->http_ver.minor); + packerToMemInit(&p, &mb_hdr); + httpHeaderPackInto(&icap->request->header, &p); + packerClean(&p); + memBufAppend(&mb_hdr, crlf, 2); + service = icap->current_service; + assert(service); + client_addr = inet_ntoa(icap->request->client_addr); + + memBufPrintf(&mb, "REQMOD %s ICAP/1.0\r\n", service->uri); + memBufPrintf(&mb, "Encapsulated: req-hdr=0"); + /* TODO: Change the offset using 'request' if needed */ + if (icap->request->content_length > 0) + memBufPrintf(&mb, ", req-body=%d", mb_hdr.size); + else + memBufPrintf(&mb, ", null-body=%d", mb_hdr.size); + memBufAppend(&mb, crlf, 2); + + if (service->flags.need_x_client_ip && Config.icapcfg.send_client_ip) + memBufPrintf(&mb, "X-Client-IP: %s\r\n", client_addr); + + if (service->flags.need_x_server_ip && Config.icapcfg.send_server_ip) + icapAddOriginIP(&mb, icap->request->host); + + if ((service->flags.need_x_authenticated_user + && Config.icapcfg.send_auth_user) + && (icap->request->auth_user_request != NULL)) + icapAddAuthUserHeader(&mb, icap->request->auth_user_request); + if (service->keep_alive) { + icap->flags.keep_alive = 1; + } else { + icap->flags.keep_alive = 0; + memBufAppend(&mb, "Connection: close\r\n", 19); + } + memBufAppend(&mb, crlf, 2); + memBufAppend(&mb, mb_hdr.buf, mb_hdr.size); + memBufClean(&mb_hdr); + + debug(81, 5) ("icapSendReqMod: FD %d writing {%s}\n", icap->icap_fd, + mb.buf); + comm_write_mbuf(icap_fd, mb, theCallback, icap); +} + +/* + * icapReqModStart + * + * Initiate an ICAP REQMOD transaction. Create and fill in IcapStateData + * structure and request a TCP connection to the server. + */ +IcapStateData * +icapReqModStart(icap_service * service, const char *uri, request_t * request, + int fd, struct timeval start, struct in_addr log_addr, void *cookie) +{ + IcapStateData *icap = NULL; + + debug(81, 3) ("icapReqModStart: type=%d\n", (int) service->type); + + switch (service->type) { + case ICAP_SERVICE_REQMOD_PRECACHE: + break; + default: + fatalf("icapReqModStart: unsupported service type '%s'\n", + icap_service_type_str[service->type]); + break; + } + + if (service->unreachable) { + if (service->bypass) { + debug(81, + 5) ("icapReqModStart: BYPASS because service unreachable: %s\n", + service->uri); + return NULL; + } else { + debug(81, + 5) ("icapReqModStart: ERROR because service unreachable: %s\n", + service->uri); + return (IcapStateData *) - 1; + } + } + icap = icapAllocate(); + if (!icap) { + debug(81, 3) ("icapReqModStart: icapAllocate() failed\n"); + return NULL; + } + icap->current_service = service; + icap->preview_size = service->preview; + icap->reqmod.uri = uri; /* XXX should be xstrdup? */ + icap->reqmod.start = start; + icap->reqmod.log_addr = log_addr; + icap->request = requestLink(request); + icap->reqmod.hdr_state = 0; + icap->reqmod.client_fd = fd; + icap->reqmod.client_cookie = cookie; + cbdataLock(icap->reqmod.client_cookie); + + if (!icapConnect(icap, icapSendReqMod)) + return NULL; + + statCounter.icap.all.requests++; + debug(81, 3) ("icapReqModStart: returning %p\n", icap); + return icap; +} + +/* + * icapReqModSendBodyChunk + * + * A "comm_write" callback. This is called after comm_write() does + * its job to let us know how things went. If there are no errors, + * get another chunk of the body from client_side. + */ +static void +icapReqModSendBodyChunk(int fd, char *bufnotused, size_t size, int errflag, + void *data) +{ + IcapStateData *icap = data; + debug(81, 3) ("icapReqModSendBodyChunk: FD %d wrote %d errflag %d.\n", + fd, (int) size, errflag); + if (errflag == COMM_ERR_CLOSING) + return; + if (errflag) { + icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, + errno); + comm_close(fd); + return; + } + clientReadBody(icap->request, + memAllocate(MEM_8K_BUF), 8192, icapReqModBodyHandler, icap); +} + +/* + * icapReqModBodyHandler + * + * Called after Squid gets a chunk of the request entity from the + * client side. The body is chunkified and passed to comm_write. + * The comm_write callback depends on whether or not this is the + * last chunk. + */ +static void +icapReqModBodyHandler(char *buf, ssize_t size, void *data) +{ + IcapStateData *icap = data; + MemBuf mb; + CWCB *theCallback = icapReqModSendBodyChunk; + if (size < 0) { + debug(81, 1) ("icapReqModBodyHandler: %s\n", xstrerror()); + memFree8K(buf); + return; + } + memBufDefInit(&mb); + debug(81, 3) ("icapReqModBodyHandler: writing chunk size %d\n", size); + memBufPrintf(&mb, "%x\r\n", size); + if (size) + memBufAppend(&mb, buf, size); + else + theCallback = icapSendReqModDone; + memBufAppend(&mb, crlf, 2); + memFree8K(buf); + comm_write_mbuf(icap->icap_fd, mb, theCallback, icap); +} + +/* + * icapReqModReadHttpBody + * + * The read handler for the client's HTTP connection when reading + * message bodies. Called by comm_select(). + */ +static void +icapReqModReadHttpBody(int fd, void *data) +{ + IcapStateData *icap = data; + int len; + debug(81, 3) ("icapReqModReadHttpBody: FD %d called\n", fd); + len = memBufRead(fd, &icap->chunk_buf); + debug(81, 3) ("icapReqModReadHttpBody: read returns %d\n", len); + if (len < 0) { + debug(81, 3) ("icapReqModReadHttpBody: FD %d %s\n", fd, xstrerror()); + if (!ignoreErrno(errno)) + icap->flags.reqmod_http_entity_eof = 1; + } else if (0 == len) { + debug(81, 3) ("icapReqModReadHttpBody: FD %d EOF\n", fd); + icap->flags.reqmod_http_entity_eof = 1; + } else { + fd_bytes(fd, len, FD_READ); + kb_incr(&statCounter.icap.all.kbytes_in, len); + icap->reqmod.http_entity.bytes_read += + icapParseChunkedBody(icap, + icapReqModMemBufAppend, &icap->reqmod.http_entity.buf); + } + if (icap->chunk_size < 0 ) + icap->flags.reqmod_http_entity_eof = 1; + + if (!icap->flags.reqmod_http_entity_eof) + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); + /* + * Notify the other side if it is waiting for data from us + */ + debug(81, 3) ("%s:%d http_entity.callback=%p\n", __FILE__, __LINE__, + icap->reqmod.http_entity.callback); + debug(81, 3) ("%s:%d http_entity.buf.size=%d\n", __FILE__, __LINE__, + icap->reqmod.http_entity.buf.size); + if (icap->reqmod.http_entity.callback) { + icapReqModPassHttpBody(icap, + icap->reqmod.http_entity.callback_buf, + icap->reqmod.http_entity.callback_bufsize, + icap->reqmod.http_entity.callback, + icap->reqmod.http_entity.callback_data); + icap->reqmod.http_entity.callback = NULL; + cbdataUnlock(icap->reqmod.http_entity.callback_data); + } +} + +/* + * icapReqModPassHttpBody + * + * Called from http.c after request headers have been sent. + * This function feeds the http.c module chunks of the request + * body that were stored in the http_entity.buf MemBuf. + */ +static void +icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, + CBCB * callback, void *cbdata) +{ + debug(81, 3) ("icapReqModPassHttpBody: called\n"); + if (!buf) { + debug(81, 1) ("icapReqModPassHttpBody: FD %d called with %p, %d, %p (request aborted)\n", + icap->icap_fd, buf, (int) size, cbdata); + comm_close(icap->icap_fd); + return; + } + if (!cbdataValid(cbdata)) { + debug(81, + 1) + ("icapReqModPassHttpBody: FD %d callback data invalid, closing\n", + icap->icap_fd); + comm_close(icap->icap_fd); /*It is better to be sure that the connection will be closed..... */ + /*icapReqModKeepAliveOrClose(icap); */ + return; + } + debug(81, 3) ("icapReqModPassHttpBody: entity buf size = %d\n", + icap->reqmod.http_entity.buf.size); + if (icap->reqmod.http_entity.buf.size) { + int copy_sz = icap->reqmod.http_entity.buf.size; + if (copy_sz > size) + copy_sz = size; + xmemcpy(buf, icap->reqmod.http_entity.buf.buf, copy_sz); + /* XXX don't let Alex see this ugliness */ + xmemmove(icap->reqmod.http_entity.buf.buf, + icap->reqmod.http_entity.buf.buf + copy_sz, + icap->reqmod.http_entity.buf.size - copy_sz); + icap->reqmod.http_entity.buf.size -= copy_sz; + debug(81, 3) ("icapReqModPassHttpBody: giving %d bytes to other side\n", + copy_sz); + callback(buf, copy_sz, cbdata); + debug(81, 3) ("icapReqModPassHttpBody: entity buf size now = %d\n", + icap->reqmod.http_entity.buf.size); + return; + } + if (icap->flags.reqmod_http_entity_eof) { + debug(81, 3) ("icapReqModPassHttpBody: signalling EOF\n"); + callback(buf, 0, cbdata); + icapReqModKeepAliveOrClose(icap); + return; + } + /* + * We have no data for the other side at this point. Save all + * these values and use them when we do have data. + */ + assert(NULL == icap->reqmod.http_entity.callback); + icap->reqmod.http_entity.callback = callback; + icap->reqmod.http_entity.callback_data = cbdata; + icap->reqmod.http_entity.callback_buf = buf; + icap->reqmod.http_entity.callback_bufsize = size; + cbdataLock(icap->reqmod.http_entity.callback_data); +} + +/* + * Body reader handler for use with request->body_reader function + * Simple a wrapper for icapReqModPassHttpBody function + */ + +static void +icapReqModBodyReader(request_t * request, char *buf, size_t size, + CBCB * callback, void *cbdata) +{ + IcapStateData *icap = request->body_reader_data; + icapReqModPassHttpBody(icap, buf, size, callback, cbdata); +} + +/* + * icapReqModMemBufAppend + * + * stupid wrapper to eliminate compiler warnings + */ +static void +icapReqModMemBufAppend(void *data, const char *buf, ssize_t size) +{ + memBufAppend(data, buf, size); +} diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/ee/101d80315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/ee/101d80315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/ee/101d80315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/ee/101d80315c88001b16add8656805b17d 2006-12-10 16:10:53.000000000 +0200 @@ -0,0 +1,28 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +C_SRCS := +O_SRCS := +ASM_SRCS := +S_SRCS := +OBJ_SRCS := +OBJS := +DEPS := +EXECUTABLES := + +# Every subdirectory with source files must be described here +SUBDIRS := \ +. \ +repl/lru \ +repl/heap \ +fs/ufs \ +fs/null \ +fs/diskd \ +fs/coss \ +fs/aufs \ +auth/ntlm \ +auth/negotiate \ +auth/digest \ +auth/basic \ + diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/f0/80118ad06788001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/f0/80118ad06788001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/f0/80118ad06788001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/f0/80118ad06788001b16add8656805b17d 2006-12-10 16:09:19.000000000 +0200 @@ -0,0 +1,2655 @@ + +/* + * $Id: structs.h,v 1.504 2006/10/23 11:22:21 hno Exp $ + * + * + * 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. + * + */ + +#ifndef SQUID_STRUCTS_H +#define SQUID_STRUCTS_H + +#include "config.h" +#include "splay.h" + +struct _dlink_node { + void *data; + dlink_node *prev; + dlink_node *next; +}; + +struct _dlink_list { + dlink_node *head; + dlink_node *tail; +}; + +#if USE_SSL +struct _acl_cert_data { + splayNode *values; + char *attribute; +}; + +#endif + +struct _acl_user_data { + splayNode *names; + struct { + unsigned int case_insensitive:1; + unsigned int required:1; + } flags; +}; + +struct _acl_user_ip_data { + int max; + struct { + unsigned int strict:1; + } flags; +}; + +struct _acl_ip_data { + struct in_addr addr1; /* if addr2 non-zero then its a range */ + struct in_addr addr2; + struct in_addr mask; + acl_ip_data *next; /* used for parsing, not for storing */ +}; + +struct _acl_time_data { + int weekbits; + int start; + int stop; + acl_time_data *next; +}; + +struct _acl_name_list { + char name[ACL_NAME_SZ]; + acl_name_list *next; +}; + +struct _acl_proxy_auth_match_cache { + dlink_node link; + int matchrv; + void *acl_data; +}; + +struct _acl_hdr_data { + acl_hdr_data *next; + relist *reglist; + http_hdr_type hdr_id; + const char *hdr_name; +}; + +struct _auth_user_hash_pointer { + /* first two items must be same as hash_link */ + char *key; + auth_user_hash_pointer *next; + auth_user_t *auth_user; + dlink_node link; /* other hash entries that point to the same auth_user */ +}; + +struct _auth_user_ip_t { + dlink_node node; + /* IP addr this user authenticated from */ + struct in_addr ipaddr; + time_t ip_expiretime; +}; + +struct _auth_user_t { + /* extra fields for proxy_auth */ + /* this determines what scheme owns the user data. */ + auth_type_t auth_type; + /* the index +1 in the authscheme_list to the authscheme entry */ + int auth_module; + /* we only have one username associated with a given auth_user struct */ + auth_user_hash_pointer *usernamehash; + /* cache of acl lookups on this username */ + dlink_list proxy_match_cache; + /* what ip addresses has this user been seen at?, plus a list length cache */ + dlink_list ip_list; + int ipcount; + long expiretime; + /* how many references are outstanding to this instance */ + int references; + /* the auth scheme has it's own private data area */ + void *scheme_data; + /* the auth_user_request structures that link to this. Yes it could be a splaytree + * but how many requests will a single username have in parallel? */ + dlink_list requests; +}; + +struct _auth_user_request_t { + /* this is the object passed around by client_side and acl functions */ + /* it has request specific data, and links to user specific data */ + /* the user */ + auth_user_t *auth_user; + /* return a message on the 407 error pages */ + char *message; + /* any scheme specific request related data */ + void *scheme_data; + /* how many 'processes' are working on this data */ + int references; + /* We only attempt authentication once per http request. This + * is to allow multiple auth acl references from different _access areas + * when using connection based authentication + */ + auth_acl_t lastReply; +}; + + +/* + * This defines an auth scheme module + */ + +struct _authscheme_entry { + const char *typestr; + AUTHSACTIVE *Active; + AUTHSADDHEADER *AddHeader; + AUTHSADDTRAILER *AddTrailer; + AUTHSAUTHED *authenticated; + AUTHSAUTHUSER *authAuthenticate; + AUTHSCONFIGURED *configured; + AUTHSDUMP *dump; + AUTHSFIXERR *authFixHeader; + AUTHSFREE *FreeUser; + AUTHSFREECONFIG *freeconfig; + AUTHSUSERNAME *authUserUsername; + AUTHSONCLOSEC *oncloseconnection; /*optional */ + AUTHSCONNLASTHEADER *authConnLastHeader; + AUTHSDECODE *decodeauth; + AUTHSDIRECTION *getdirection; + AUTHSPARSE *parse; + AUTHSCHECKCONFIG *checkconfig; + AUTHSINIT *init; + AUTHSREQFREE *requestFree; + AUTHSSHUTDOWN *donefunc; + AUTHSSTART *authStart; + AUTHSSTATS *authStats; +}; + +/* + * This is a configured auth scheme + */ + +/* private data types */ +struct _authScheme { + /* pointer to the authscheme_list's string entry */ + const char *typestr; + /* the scheme id in the authscheme_list */ + int Id; + /* the scheme's configuration details. */ + void *scheme_data; +}; + +struct _acl_deny_info_list { + int err_page_id; + char *err_page_name; + acl_name_list *acl_list; + acl_deny_info_list *next; +}; + +#if USE_ARP_ACL + +struct _acl_arp_data { + char eth[6]; +}; + +#endif + +struct _String { + /* never reference these directly! */ + unsigned short int size; /* buffer size; 64K limit */ + unsigned short int len; /* current length */ + char *buf; +}; + +struct _header_mangler { + acl_access *access_list; + char *replacement; + /* What follows is only used by HDR_OTHER to build a list of named headers */ + char *name; + header_mangler *next; +}; + +struct _body_size { + dlink_node node; + acl_access *access_list; + squid_off_t maxsize; +}; + +struct _http_version_t { + unsigned int major; + unsigned int minor; +}; + +#if SQUID_SNMP + +struct _snmp_request_t { + u_char *buf; + u_char *outbuf; + int len; + int sock; + long reqid; + int outlen; + struct sockaddr_in from; + struct snmp_pdu *PDU; + aclCheck_t *acl_checklist; + u_char *community; + struct snmp_session session; +}; + +#endif + +struct _acl { + char name[ACL_NAME_SZ]; + squid_acl type; + void *data; + char *cfgline; + acl *next; +}; + +struct _acl_list { + int op; + acl *acl; + acl_list *next; +}; + +struct _acl_access { + int allow; + acl_list *acl_list; + char *cfgline; + acl_access *next; +}; + +struct _acl_address { + acl_address *next; + acl_list *acl_list; + struct in_addr addr; +}; + +struct _acl_tos { + acl_tos *next; + acl_list *acl_list; + int tos; +}; + +struct _aclCheck_t { + const acl_access *access_list; + struct in_addr src_addr; + struct in_addr dst_addr; + struct in_addr my_addr; + unsigned short my_port; + request_t *request; + /* for acls that look at reply data */ + HttpReply *reply; + ConnStateData *conn; /* hack for ident and NTLM */ + char rfc931[USER_IDENT_SZ]; + auth_user_request_t *auth_user_request; + acl_lookup_state state[ACL_ENUM_MAX]; +#if SQUID_SNMP + char *snmp_community; +#endif + PF *callback; + void *callback_data; + external_acl_entry *extacl_entry; + acl *current_acl; /* private, used by aclCheck */ +}; + +struct _wordlist { + char *key; + wordlist *next; +}; + +struct _intlist { + int i; + intlist *next; +}; + +struct _intrange { + int i; + int j; + intrange *next; +}; + +struct _ushortlist { + u_short i; + ushortlist *next; +}; + +struct _relist { + char *pattern; + regex_t regex; + relist *next; +}; + +struct _acl_request_type { + unsigned int accelerated:1; + unsigned int transparent:1; + unsigned int internal:1; +}; + +struct _sockaddr_in_list { + struct sockaddr_in s; + sockaddr_in_list *next; +}; + +struct _http_port_list { + http_port_list *next; + struct sockaddr_in s; + char *protocol; /* protocol name */ + char *name; /* visible name */ + char *defaultsite; /* default web site */ + char *urlgroup; /* default urlgroup */ + unsigned int transparent; /* transparent proxy */ + unsigned int accel; /* HTTP accelerator */ + unsigned int vhost; /* uses host header */ + unsigned int vport; /* virtual port support */ + unsigned int no_connection_auth; /* Don't support connection oriented auth */ +#if LINUX_TPROXY + unsigned int tproxy; +#endif +}; + +#if USE_SSL +struct _https_port_list { + http_port_list http; /* must be first */ + char *cert; + char *key; + int version; + char *cipher; + char *options; + char *clientca; + char *cafile; + char *capath; + char *crlfile; + char *dhfile; + char *sslflags; + char *sslcontext; + SSL_CTX *sslContext; +}; + +#endif + +#if DELAY_POOLS +struct _delaySpec { + int restore_bps; + int max_bytes; +}; + +/* malloc()'d only as far as used (class * sizeof(delaySpec)! + * order of elements very important! + */ +struct _delaySpecSet { + delaySpec aggregate; + delaySpec individual; + delaySpec network; +}; + +struct _delayConfig { + unsigned short pools; + unsigned short initial; + unsigned char *class; + delaySpecSet **rates; + acl_access **access; +}; + +#endif + +struct _RemovalPolicySettings { + char *type; + wordlist *args; +}; + +#if HS_FEAT_ICAP +struct _IcapConfig { + int onoff; + int preview_enable; + icap_service *service_head; + icap_class *class_head; + icap_access *access_head; + int preview_size; + int check_interval; + int send_client_ip; + int send_server_ip; + int send_auth_user; + char *auth_scheme; +}; + +#endif /* HS_FEAT_ICAP */ + +struct _SquidConfig { + struct { + squid_off_t maxSize; + int highWaterMark; + int lowWaterMark; + } Swap; + squid_off_t memMaxSize; + struct { + char *relayHost; + u_short relayPort; + peer *peer; + } Wais; + struct { + squid_off_t min; + int pct; + squid_off_t max; + } quickAbort; + squid_off_t readAheadGap; + RemovalPolicySettings *replPolicy; + RemovalPolicySettings *memPolicy; + time_t negativeTtl; + time_t negativeDnsTtl; + time_t positiveDnsTtl; + time_t shutdownLifetime; + struct { + time_t read; + time_t lifetime; + time_t forward; + time_t connect; + time_t peer_connect; + time_t request; + time_t persistent_request; + time_t pconn; + time_t siteSelect; + time_t deadPeer; + int icp_query; /* msec */ + int icp_query_max; /* msec */ + int mcast_icp_query; /* msec */ +#if USE_IDENT + time_t ident; +#endif +#if !USE_DNSSERVERS + time_t idns_retransmit; + time_t idns_query; +#endif + } Timeout; + squid_off_t maxRequestHeaderSize; + squid_off_t maxRequestBodySize; + squid_off_t maxReplyHeaderSize; + dlink_list ReplyBodySize; + struct { + u_short icp; +#if USE_HTCP + u_short htcp; +#endif +#if SQUID_SNMP + u_short snmp; +#endif + } Port; + struct { + http_port_list *http; +#if USE_SSL + https_port_list *https; +#endif + } Sockaddr; +#if SQUID_SNMP + struct { + char *configFile; + char *agentInfo; + } Snmp; +#endif +#if USE_WCCP + struct { + struct in_addr router; + struct in_addr address; + int version; + } Wccp; +#endif +#if USE_WCCPv2 + struct { + sockaddr_in_list *router; + struct in_addr address; + int forwarding_method; + int return_method; + int assignment_method; + int weight; + int rebuildwait; + void *info; + } Wccp2; +#endif + char *as_whois_server; + struct { + char *log; + char *store; + char *swap; +#if USE_USERAGENT_LOG + char *useragent; +#endif +#if USE_REFERER_LOG + char *referer; +#endif +#if WIP_FWD_LOG + char *forward; +#endif + logformat *logformats; + customlog *accesslogs; + int rotateNumber; + } Log; + char *adminEmail; + char *EmailFrom; + char *EmailProgram; + char *effectiveUser; + char *visible_appname_string; + char *effectiveGroup; + struct { +#if USE_DNSSERVERS + char *dnsserver; +#endif + struct { + wordlist *command; + int children; + int concurrency; + } url_rewrite; + struct { + wordlist *command; + int children; + int concurrency; + } location_rewrite; +#if USE_ICMP + char *pinger; +#endif +#if USE_UNLINKD + char *unlinkd; +#endif + char *diskd; +#if USE_SSL + char *ssl_password; +#endif + } Program; +#if USE_DNSSERVERS + int dnsChildren; +#endif + time_t authenticateGCInterval; + time_t authenticateTTL; + time_t authenticateIpTTL; + char *appendDomain; + int appendDomainLen; + char *debugOptions; + char *pidFilename; + char *mimeTablePathname; + char *etcHostsPath; + char *visibleHostname; + char *uniqueHostname; + wordlist *hostnameAliases; + char *errHtmlText; + struct { + char *host; + char *file; + time_t period; + u_short port; + } Announce; + struct { + struct in_addr udp_incoming; + struct in_addr udp_outgoing; +#if SQUID_SNMP + struct in_addr snmp_incoming; + struct in_addr snmp_outgoing; +#endif + struct in_addr client_netmask; + } Addrs; + squid_off_t tcpRcvBufsz; + squid_off_t udpMaxHitObjsz; + wordlist *hierarchy_stoplist; + wordlist *mcast_group_list; + wordlist *dns_testname_list; + wordlist *dns_nameservers; + peer *peers; + int npeers; + struct { + int size; + int low; + int high; + } ipcache; + struct { + int size; + } fqdncache; + int minDirectHops; + int minDirectRtt; + cachemgr_passwd *passwd_list; + struct { + int objectsPerBucket; + squid_off_t avgObjectSize; + squid_off_t maxObjectSize; + squid_off_t minObjectSize; + squid_off_t maxInMemObjSize; + } Store; + struct { + int high; + int low; + time_t period; + } Netdb; + struct { + int log_udp; + int res_defnames; + int anonymizer; + int client_db; + int query_icmp; + int icp_hit_stale; + int buffered_logs; +#if ALLOW_SOURCE_PING + int source_ping; +#endif + int common_log; + int log_mime_hdrs; + int log_fqdn; + int announce; + int mem_pools; + int test_reachability; + int half_closed_clients; +#if HTTP_VIOLATIONS + int reload_into_ims; +#endif + int offline; + int redir_rewrites_host; + int prefer_direct; + int nonhierarchical_direct; + int strip_query_terms; + int redirector_bypass; + int ignore_unknown_nameservers; + int client_pconns; + int server_pconns; + int error_pconns; +#if USE_CACHE_DIGESTS + int digest_generation; +#endif + int log_ip_on_direct; + int ie_refresh; + int vary_ignore_expire; + int pipeline_prefetch; + int request_entities; + int detect_broken_server_pconns; + int balance_on_multiple_ip; + int collapsed_forwarding; + int relaxed_header_parser; + int accel_no_pmtu_disc; + int global_internal_static; + int httpd_suppress_version_string; + int via; + int check_hostnames; + int allow_underscore; + int cache_vary; +#if FOLLOW_X_FORWARDED_FOR + int acl_uses_indirect_client; + int delay_pool_uses_indirect_client; + int log_uses_indirect_client; +#endif + } onoff; + acl *aclList; + struct { + acl_access *http; + acl_access *http2; + acl_access *icp; + acl_access *miss; + acl_access *NeverDirect; + acl_access *AlwaysDirect; + acl_access *ASlists; + acl_access *noCache; + acl_access *log; +#if SQUID_SNMP + acl_access *snmp; +#endif + acl_access *brokenPosts; +#if USE_IDENT + acl_access *identLookup; +#endif + acl_access *url_rewrite; + acl_access *location_rewrite; + acl_access *reply; + acl_address *outgoing_address; + acl_tos *outgoing_tos; +#if USE_HTCP + acl_access *htcp; + acl_access *htcp_clr; +#endif +#if FOLLOW_X_FORWARDED_FOR + acl_access *followXFF; +#endif + acl_access *vary_encoding; + } accessList; + acl_deny_info_list *denyInfoList; + struct _authConfig { + authScheme *schemes; + int n_allocated; + int n_configured; + } authConfig; + struct { + int list_width; + int list_wrap; + char *anon_user; + int passive; + int sanitycheck; + int telnet; + } Ftp; + refresh_t *Refresh; + struct _cacheSwap { + SwapDir *swapDirs; + int n_allocated; + int n_configured; + } cacheSwap; + struct { + char *directory; + int use_short_names; + } icons; + char *errorDirectory; + struct { + int maxtries; + int onerror; + } retry; + struct { + squid_off_t limit; + } MemPools; +#if DELAY_POOLS + delayConfig Delay; +#endif + struct { + int icp_average; + int dns_average; + int http_average; + int icp_min_poll; + int dns_min_poll; + int http_min_poll; + } comm_incoming; + int max_open_disk_fds; + int uri_whitespace; + squid_off_t rangeOffsetLimit; +#if MULTICAST_MISS_STREAM + struct { + struct in_addr addr; + int ttl; + unsigned short port; + char *encode_key; + } mcast_miss; +#endif + header_mangler header_access[HDR_ENUM_END]; + char *coredump_dir; + char *chroot_dir; +#if USE_CACHE_DIGESTS + struct { + int bits_per_entry; + time_t rebuild_period; + time_t rewrite_period; + squid_off_t swapout_chunk_size; + int rebuild_chunk_percentage; + } digest; +#endif +#if USE_SSL + struct { + int unclean_shutdown; + char *ssl_engine; + } SSL; +#endif + struct { + int high_rptm; + int high_pf; + squid_off_t high_memory; + } warnings; + char *store_dir_select_algorithm; + int sleep_after_fork; /* microseconds */ + time_t minimum_expiry_time; /* seconds */ + external_acl *externalAclHelperList; + errormap *errorMapList; +#if USE_SSL + struct { + char *cert; + char *key; + int version; + char *options; + char *cipher; + char *cafile; + char *capath; + char *crlfile; + char *flags; + SSL_CTX *sslContext; + } ssl_client; +#endif + time_t refresh_stale_window; + int umask; +#ifdef HS_FEAT_ICAP + IcapConfig icapcfg; +#endif +}; + +struct _SquidConfig2 { + struct { + int enable_purge; + } onoff; + uid_t effectiveUserID; + gid_t effectiveGroupID; +}; + +struct _close_handler { + PF *handler; + void *data; + close_handler *next; +}; + +struct _dread_ctrl { + int fd; + off_t file_offset; + size_t req_len; + char *buf; + int end_of_file; + DRCB *handler; + void *client_data; +}; + +struct _dwrite_q { + off_t file_offset; + char *buf; + size_t len; + size_t buf_offset; + dwrite_q *next; + FREE *free_func; +}; + +struct _CommWriteStateData { + int valid; + char *buf; + size_t size; + size_t offset; + CWCB *handler; + void *handler_data; + FREE *free_func; +}; + + +/* ETag support is rudimantal; + * this struct is likely to change + * Note: "str" points to memory in HttpHeaderEntry (for now) + * so ETags should be used as tmp variables only (for now) */ +struct _ETag { + const char *str; /* quoted-string */ + int weak; /* true if it is a weak validator */ +}; + +struct _fde { + unsigned int type; + u_short local_port; + u_short remote_port; + struct in_addr local_addr; + unsigned char tos; + char ipaddr[16]; /* dotted decimal address of peer */ + char desc[FD_DESC_SZ]; + struct { + unsigned int open:1; + unsigned int close_request:1; + unsigned int write_daemon:1; + unsigned int closing:1; + unsigned int socket_eof:1; + unsigned int nolinger:1; + unsigned int nonblocking:1; + unsigned int ipc:1; + unsigned int called_connect:1; + unsigned int nodelay:1; + unsigned int close_on_exec:1; + unsigned int backoff:1; /* keep track of whether the fd is backed off */ + } flags; + comm_pending read_pending; + comm_pending write_pending; + squid_off_t bytes_read; + squid_off_t bytes_written; + struct { + int uses; + int type; + } pconn; + int uses; /* ie # req's over persistent conn */ + struct _fde_disk { + DWCB *wrt_handle; + void *wrt_handle_data; + dwrite_q *write_q; + dwrite_q *write_q_tail; + off_t offset; + } disk; + PF *read_handler; + void *read_data; + PF *write_handler; + void *write_data; + PF *timeout_handler; + time_t timeout; + void *timeout_data; + void *lifetime_data; + close_handler *close_handler; /* linked list */ + DEFER *defer_check; /* check if we should defer read */ + void *defer_data; + struct _CommWriteStateData rwstate; /* State data for comm_write */ + READ_HANDLER *read_method; + WRITE_HANDLER *write_method; +#if USE_SSL + SSL *ssl; +#endif +#ifdef _SQUID_MSWIN_ + struct { + long handle; + } win32; +#endif +#if DELAY_POOLS + int slow_id; +#endif +}; + +struct _fileMap { + int max_n_files; + int n_files_in_map; + int toggle; + int nwords; + unsigned long *file_map; +}; + +/* auto-growing memory-resident buffer with printf interface */ +/* note: when updating this struct, update MemBufNULL #define */ +struct _MemBuf { + /* public, read-only */ + char *buf; + mb_size_t size; /* used space, does not count 0-terminator */ + + /* private, stay away; use interface function instead */ + mb_size_t max_capacity; /* when grows: assert(new_capacity <= max_capacity) */ + mb_size_t capacity; /* allocated space */ + unsigned stolen:1; /* the buffer has been stolen for use by someone else */ +}; + +/* see Packer.c for description */ +struct _Packer { + /* protected, use interface functions instead */ + append_f append; + vprintf_f packer_vprintf; + void *real_handle; /* first parameter to real append and vprintf */ +}; + +/* http status line */ +struct _HttpStatusLine { + /* public, read only */ + http_version_t version; + const char *reason; /* points to a _constant_ string (default or supplied), never free()d */ + http_status status; +}; + +/* + * Note: HttpBody is used only for messages with a small content that is + * known a priory (e.g., error messages). + */ +struct _HttpBody { + /* private */ + MemBuf mb; +}; + +/* http header extention field */ +struct _HttpHdrExtField { + String name; /* field-name from HTTP/1.1 (no column after name) */ + String value; /* field-value from HTTP/1.1 */ +}; + +/* http cache control header field */ +struct _HttpHdrCc { + int mask; + int max_age; + int s_maxage; + int max_stale; + String other; +}; + +/* http byte-range-spec */ +struct _HttpHdrRangeSpec { + squid_off_t offset; + squid_off_t length; +}; + +/* There may be more than one byte range specified in the request. + * This object holds all range specs in order of their appearence + * in the request because we SHOULD preserve that order. + */ +struct _HttpHdrRange { + Stack specs; +}; + +/* http content-range header field */ +struct _HttpHdrContRange { + HttpHdrRangeSpec spec; + squid_off_t elength; /* entity length, not content length */ +}; + +/* some fields can hold either time or etag specs (e.g. If-Range) */ +struct _TimeOrTag { + const char *tag; /* entity tag */ + time_t time; + int valid; /* true if struct is usable */ +}; + +/* data for iterating thru range specs */ +struct _HttpHdrRangeIter { + HttpHdrRangePos pos; + const HttpHdrRangeSpec *spec; /* current spec at pos */ + squid_off_t debt_size; /* bytes left to send from the current spec */ + squid_off_t prefix_size; /* the size of the incoming HTTP msg prefix */ + String boundary; /* boundary for multipart responses */ +}; + +/* constant attributes of http header fields */ +struct _HttpHeaderFieldAttrs { + const char *name; + http_hdr_type id; + field_type type; +}; + +/* per field statistics */ +struct _HttpHeaderFieldStat { + int aliveCount; /* created but not destroyed (count) */ + int seenCount; /* #fields we've seen */ + int parsCount; /* #parsing attempts */ + int errCount; /* #pasring errors */ + int repCount; /* #repetitons */ +}; + +/* compiled version of HttpHeaderFieldAttrs plus stats */ +struct _HttpHeaderFieldInfo { + http_hdr_type id; + String name; + field_type type; + HttpHeaderFieldStat stat; +}; + +struct _HttpHeaderEntry { + http_hdr_type id; + String name; + String value; +}; + +struct _HttpHeader { + /* protected, do not use these, use interface functions instead */ + Array entries; /* parsed fields in raw format */ + HttpHeaderMask mask; /* bit set <=> entry present */ + http_hdr_owner_type owner; /* request or reply */ + int len; /* length when packed, not counting terminating '\0' */ +}; + +struct _HttpReply { + /* unsupported, writable, may disappear/change in the future */ + int hdr_sz; /* sums _stored_ status-line, headers, and */ + + /* public, readable; never update these or their .hdr equivalents directly */ + squid_off_t content_length; + time_t date; + time_t last_modified; + time_t expires; + String content_type; + HttpHdrCc *cache_control; + HttpHdrContRange *content_range; + short int keep_alive; + + /* public, readable */ + HttpMsgParseState pstate; /* the current parsing state */ + + /* public, writable, but use httpReply* interfaces when possible */ + HttpStatusLine sline; + HttpHeader header; + HttpBody body; /* for small constant memory-resident text bodies only */ +}; + +struct _http_state_flags { + unsigned int proxying:1; + unsigned int keepalive:1; + unsigned int only_if_cached:1; + unsigned int keepalive_broken:1; + unsigned int abuse_detected:1; + unsigned int request_sent:1; + unsigned int front_end_https:2; + unsigned int originpeer:1; +}; + +#ifdef HS_FEAT_ICAP +struct _IcapStateData { + request_t *request; + http_state_flags http_flags; + HttpStateData *httpState; /* needed to parse HTTP headers only */ + int icap_fd; + int sc; + icap_service *current_service; + MemBuf icap_hdr; + struct { + int res_hdr; + int res_body; + int req_hdr; + int req_body; + int opt_body; + int null_body; + } enc; + int bytes_to_gobble; + int chunk_size; + MemBuf chunk_buf; + int preview_size; + squid_off_t fake_content_length; + int http_header_bytes_read_so_far; + struct { + const char *uri; /* URI for REQMODs */ + int client_fd; + struct timeval start; /* for logging */ + struct in_addr log_addr; /* for logging */ + int hdr_state; + MemBuf hdr_buf; + void *client_cookie; + struct { + MemBuf buf; + CBCB *callback; + void *callback_data; + char *callback_buf; + size_t callback_bufsize; + squid_off_t bytes_read; + } http_entity; + } reqmod; + struct { + StoreEntry *entry; + MemBuf buffer; + MemBuf req_hdr_copy; /* XXX barf */ + MemBuf resp_copy; /* XXX barf^max */ + squid_off_t res_body_sz; + } respmod; + struct { + unsigned int connect_requested:1; + unsigned int connect_pending:1; + unsigned int write_pending:1; + unsigned int keep_alive:1; + unsigned int http_server_eof:1; + unsigned int send_zero_chunk:1; + unsigned int got_reply:1; + unsigned int wait_for_reply:1; + unsigned int wait_for_preview_reply:1; + unsigned int preview_done:1; + unsigned int copy_response:1; + unsigned int no_content:1; + unsigned int reqmod_http_entity_eof:1; + } flags; +}; + +struct _icap_service { + icap_service *next; + char *name; /* name to be used when referencing ths service */ + char *uri; /* uri of server/service to use */ + char *type_name; /* {req|resp}mod_{pre|post}cache */ + + char *hostname; + unsigned short int port; + char *resource; + icap_service_t type; /* parsed type */ + icap_method_t method; + ushort bypass; /* flag: bypass allowed */ + ushort unreachable; /* flag: set to 1 if options request fails */ + IcapOptData *opt; /* temp data needed during opt request */ + struct { + unsigned int allow_204:1; + unsigned int need_x_client_ip:1; + unsigned int need_x_server_ip:1; + unsigned int need_x_authenticated_user:1; + } flags; + int preview; + String istag; + String transfer_preview; + String transfer_ignore; + String transfer_complete; + int max_connections; + int options_ttl; + int keep_alive; +}; + +struct _icap_service_list { + icap_service_list *next; + icap_service *services[16]; + int nservices; /* Number of services already used */ + int last_service_used; /* Last services used, use to do a round robin */ +}; + +struct _icap_class { + icap_class *next; + char *name; + wordlist *services; + icap_service_list *isl; + ushort hidden; /* for unnamed classes */ +}; + +struct _icap_access { + icap_access *next; + char *service_name; + icap_class *class; + acl_access *access; +}; + +struct _IcapOptData { + char *buf; + off_t offset; + size_t size; + off_t headlen; +}; + +#endif + +struct _HttpStateData { + StoreEntry *entry; + request_t *request; + MemBuf reply_hdr; + int reply_hdr_state; + peer *peer; /* peer request made to */ + int eof; /* reached end-of-object? */ + request_t *orig_request; + int fd; + http_state_flags flags; + FwdState *fwd; +#ifdef HS_FEAT_ICAP + struct _IcapStateData *icap_writer; +#endif + char *body_buf; + int body_buf_sz; +}; + + +struct _icpUdpData { + struct sockaddr_in address; + void *msg; + size_t len; + icpUdpData *next; +#ifndef LESS_TIMING + struct timeval start; +#endif + log_type logcode; + struct timeval queue_time; +}; + +struct _ping_data { + struct timeval start; + struct timeval stop; + int n_sent; + int n_recv; + int n_replies_expected; + int timeout; /* msec */ + int timedout; + int w_rtt; + int p_rtt; +}; + +struct _HierarchyLogEntry { + hier_code code; + char host[SQUIDHOSTNAMELEN]; + ping_data ping; + char cd_host[SQUIDHOSTNAMELEN]; /* the host of selected by cd peer */ + lookup_t cd_lookup; /* cd prediction: none, miss, hit */ + int n_choices; /* #peers we selected from (cd only) */ + int n_ichoices; /* #peers with known rtt we selected from (cd only) */ + struct timeval peer_select_start; + struct timeval store_complete_stop; +}; + +struct _AccessLogEntry { + const char *url; + struct { + method_t method; + int code; + const char *content_type; + http_version_t version; + } http; + struct { + icp_opcode opcode; + } icp; + struct { + struct in_addr caddr; + squid_off_t size; + log_type code; + int msec; + const char *rfc931; + const char *authuser; +#if USE_SSL + const char *ssluser; +#endif + } cache; + struct { + char *request; + char *reply; + } headers; + struct { + const char *method_str; + } private; + HierarchyLogEntry hier; + HttpReply *reply; + request_t *request; +}; + +struct _clientHttpRequest { + ConnStateData *conn; + request_t *request; /* Parsed URL ... */ + request_t *orig_request; /* Parsed URL ... */ + store_client *sc; /* The store_client we're using */ + store_client *old_sc; /* ... for entry to be validated */ + char *uri; + char *log_uri; + struct { + squid_off_t offset; + squid_off_t size; + } out; + HttpReply *reply; /* it is important for clientHttpRequest + * to have its own HttpReply for + * logging, especially in cases + * where the reply headers sent to + * the client are different than + * those received from the origin server. */ + HttpHdrRangeIter range_iter; /* data for iterating thru range specs */ + size_t req_sz; /* raw request size on input, not current request size */ + StoreEntry *entry; + StoreEntry *old_entry; + log_type log_type; +#if USE_CACHE_DIGESTS + const char *lookup_type; /* temporary hack: storeGet() result: HIT/MISS/NONE */ +#endif + struct timeval start; + http_version_t http_ver; + int redirect_state; + aclCheck_t *acl_checklist; /* need ptr back so we can unreg if needed */ + clientHttpRequest *next; + AccessLogEntry al; + struct { + unsigned int accel:1; + unsigned int transparent:1; + unsigned int internal:1; + unsigned int done_copying:1; + unsigned int purging:1; + unsigned int did_icap_reqmod:1; + unsigned int hit:1; + } flags; + struct { + http_status status; + char *location; + } redirect; + dlink_node active; + squid_off_t maxBodySize; +#if HS_FEAT_ICAP + IcapStateData *icap_reqmod; +#endif +}; + +struct _ConnStateData { + int fd; + struct { + char *buf; + size_t offset; + size_t size; + } in; + struct { + squid_off_t size_left; /* How much body left to process */ + request_t *request; /* Parameters passed to clientReadBody */ + char *buf; + size_t bufsize; + CBCB *callback; + void *cbdata; + } body; + auth_type_t auth_type; /* Is this connection based authentication ? if so + * what type it is. */ + /* note this is ONLY connection based because NTLM is against HTTP spec */ + /* the user details for connection based authentication */ + auth_user_request_t *auth_user_request; + clientHttpRequest *chr; + struct sockaddr_in peer; + struct sockaddr_in me; + struct in_addr log_addr; + char rfc931[USER_IDENT_SZ]; + int nrequests; + struct { + int n; + time_t until; + } defer; + http_port_list *port; + int transparent; + struct { + int fd; /* pinned server side connection */ + char *host; /* host name of pinned connection */ + int port; /* port of pinned connection */ + int pinned; /* this connection was pinned */ + int auth; /* pinned for www authentication */ + peer *peer; /* peer the connection goes via */ + } pinning; +}; + +struct _ipcache_addrs { + struct in_addr *in_addrs; + unsigned char *bad_mask; + unsigned char count; + unsigned char cur; + unsigned char badcount; +}; + +struct _domain_ping { + char *domain; + int do_ping; /* boolean */ + domain_ping *next; +}; + +struct _domain_type { + char *domain; + peer_t type; + domain_type *next; +}; + +#if USE_CACHE_DIGESTS +struct _Version { + short int current; /* current version */ + short int required; /* minimal version that can safely handle current version */ +}; + +/* digest control block; used for transmission and storage */ +struct _StoreDigestCBlock { + Version ver; + int capacity; + int count; + int del_count; + int mask_size; + unsigned char bits_per_entry; + unsigned char hash_func_count; + short int reserved_short; + int reserved[32 - 6]; +}; + +struct _DigestFetchState { + PeerDigest *pd; + StoreEntry *entry; + StoreEntry *old_entry; + store_client *sc; + store_client *old_sc; + request_t *request; + squid_off_t offset; + squid_off_t mask_offset; + time_t start_time; + time_t resp_time; + time_t expires; + struct { + int msg; + int bytes; + } sent, recv; +}; + +/* statistics for cache digests and other hit "predictors" */ +struct _cd_guess_stats { + /* public, read-only */ + int true_hits; + int false_hits; + int true_misses; + int false_misses; + int close_hits; /* tmp, remove it later */ +}; + +struct _PeerDigest { + peer *peer; /* pointer back to peer structure, argh */ + CacheDigest *cd; /* actual digest structure */ + String host; /* copy of peer->host */ + const char *req_result; /* text status of the last request */ + struct { + unsigned int needed:1; /* there were requests for this digest */ + unsigned int usable:1; /* can be used for lookups */ + unsigned int requested:1; /* in process of receiving [fresh] digest */ + } flags; + struct { + /* all times are absolute unless augmented with _delay */ + time_t initialized; /* creation */ + time_t needed; /* first lookup/use by a peer */ + time_t next_check; /* next scheduled check/refresh event */ + time_t retry_delay; /* delay before re-checking _invalid_ digest */ + time_t requested; /* requested a fresh copy of a digest */ + time_t req_delay; /* last request response time */ + time_t received; /* received the current copy of a digest */ + time_t disabled; /* disabled for good */ + } times; + struct { + cd_guess_stats guess; + int used_count; + struct { + int msgs; + kb_t kbytes; + } sent, recv; + } stats; +}; + +#endif + +struct _peer { + char *name; + char *host; + peer_t type; + struct sockaddr_in in_addr; + struct { + int pings_sent; + int pings_acked; + int fetches; + int rtt; + int ignored_replies; + int n_keepalives_sent; + int n_keepalives_recv; + time_t probe_start; + time_t last_query; + time_t last_reply; + time_t last_connect_failure; + time_t last_connect_probe; + int logged_state; /* so we can print dead/revived msgs */ + int conn_open; /* current opened connections */ + } stats; + struct { + int version; + int counts[ICP_END]; + u_short port; + } icp; +#if USE_HTCP + struct { + double version; + int counts[2]; + u_short port; + } htcp; +#endif + u_short http_port; + domain_ping *peer_domain; + domain_type *typelist; + acl_access *access; + struct { + unsigned int proxy_only:1; + unsigned int no_query:1; + unsigned int no_digest:1; + unsigned int default_parent:1; + unsigned int roundrobin:1; + unsigned int mcast_responder:1; + unsigned int closest_only:1; +#if USE_HTCP + unsigned int htcp:1; + unsigned int htcp_oldsquid:1; +#endif + unsigned int no_netdb_exchange:1; +#if DELAY_POOLS + unsigned int no_delay:1; +#endif + unsigned int allow_miss:1; + unsigned int originserver:1; + unsigned int userhash:1; + unsigned int sourcehash:1; +#if USE_CARP + unsigned int carp:1; +#endif + } options; + int weight; + struct { + double avg_n_members; + int n_times_counted; + int n_replies_expected; + int ttl; + int id; + struct { + unsigned int count_event_pending:1; + unsigned int counting:1; + } flags; + } mcast; +#if USE_CACHE_DIGESTS + PeerDigest *digest; + char *digest_url; +#endif + int tcp_up; /* 0 if a connect() fails */ + struct in_addr addresses[10]; + int n_addresses; + int rr_count; + int rr_lastcount; + peer *next; + int test_fd; +#if USE_CARP + struct { + unsigned int hash; + double load_multiplier; + double load_factor; /* normalized weight value */ + } carp; +#endif + struct { + unsigned int hash; + double load_multiplier; + double load_factor; + } userhash; + struct { + unsigned int hash; + double load_multiplier; + double load_factor; + } sourcehash; + char *login; /* Proxy authorization */ + time_t connect_timeout; + int max_conn; + struct { + char *url; + int min, max; + int interval; + int timeout; + int state; + time_t last; + PeerMonitor *data; + } monitor; + char *domain; /* Forced domain */ +#if USE_SSL + int use_ssl; + char *sslcert; + char *sslkey; + int sslversion; + char *ssloptions; + char *sslcipher; + char *sslcafile; + char *sslcapath; + char *sslcrlfile; + char *sslflags; + char *ssldomain; + SSL_CTX *sslContext; + SSL_SESSION *sslSession; +#endif + int front_end_https; + int connection_auth; +}; + +struct _net_db_name { + hash_link hash; /* must be first */ + net_db_name *next; + netdbEntry *net_db_entry; +}; + +struct _net_db_peer { + const char *peername; + double hops; + double rtt; + time_t expires; +}; + +struct _netdbEntry { + hash_link hash; /* must be first */ + char network[16]; + int pings_sent; + int pings_recv; + double hops; + double rtt; + time_t next_ping_time; + time_t last_use_time; + int link_count; + net_db_name *hosts; + net_db_peer *peers; + int n_peers_alloc; + int n_peers; +}; + +struct _ps_state { + request_t *request; + StoreEntry *entry; + int always_direct; + int never_direct; + int direct; + PSC *callback; + void *callback_data; + FwdServer *servers; + /* + * Why are these struct sockaddr_in instead of peer *? Because a + * peer structure can become invalid during the peer selection + * phase, specifically after a reconfigure. Thus we need to lookup + * the peer * based on the address when we are finally ready to + * reference the peer structure. + */ + struct sockaddr_in first_parent_miss; + struct sockaddr_in closest_parent_miss; + /* + * ->hit and ->secho can be peer* because they should only be + * accessed during the thread when they are set + */ + peer *hit; + peer_t hit_type; +#if ALLOW_SOURCE_PING + peer *secho; +#endif + ping_data ping; + aclCheck_t *acl_checklist; +}; + +#if USE_ICMP +struct _pingerEchoData { + struct in_addr to; + unsigned char opcode; + int psize; + char payload[PINGER_PAYLOAD_SZ]; +}; + +struct _pingerReplyData { + struct in_addr from; + unsigned char opcode; + int rtt; + int hops; + int psize; + char payload[PINGER_PAYLOAD_SZ]; +}; + +#endif + +struct _icp_common_t { + unsigned char opcode; /* opcode */ + unsigned char version; /* version number */ + unsigned short length; /* total length (bytes) */ + u_num32 reqnum; /* req number (req'd for UDP) */ + u_num32 flags; + u_num32 pad; + u_num32 shostid; /* sender host id */ +}; + +struct _iostats { + struct { + int reads; + int reads_deferred; + int read_hist[16]; + int writes; + int write_hist[16]; + } Http, Ftp, Gopher, Wais; +}; + +struct _mem_node { + char data[SM_PAGE_SIZE]; + int len; + int uses; + mem_node *next; +}; + +struct _mem_hdr { + mem_node *head; + mem_node *tail; + squid_off_t origin_offset; +}; + +/* keep track each client receiving data from that particular StoreEntry */ +struct _store_client { + int type; + squid_off_t copy_offset; + squid_off_t seen_offset; + size_t copy_size; + char *copy_buf; + STCB *callback; + void *callback_data; + StoreEntry *entry; /* ptr to the parent StoreEntry, argh! */ + storeIOState *swapin_sio; + struct { + unsigned int disk_io_pending:1; + unsigned int store_copying:1; + unsigned int copy_event_pending:1; + } flags; +#if DELAY_POOLS + delay_id delay_id; +#endif + dlink_node node; +#if STORE_CLIENT_LIST_DEBUG + void *owner; +#endif +}; + + +/* Removal policies */ + +struct _RemovalPolicyNode { + void *data; +}; + +struct _RemovalPolicy { + const char *_type; + void *_data; + void (*Free) (RemovalPolicy * policy); + void (*Add) (RemovalPolicy * policy, StoreEntry * entry, RemovalPolicyNode * node); + void (*Remove) (RemovalPolicy * policy, StoreEntry * entry, RemovalPolicyNode * node); + void (*Referenced) (RemovalPolicy * policy, const StoreEntry * entry, RemovalPolicyNode * node); + void (*Dereferenced) (RemovalPolicy * policy, const StoreEntry * entry, RemovalPolicyNode * node); + RemovalPolicyWalker *(*WalkInit) (RemovalPolicy * policy); + RemovalPurgeWalker *(*PurgeInit) (RemovalPolicy * policy, int max_scan); + void (*Stats) (RemovalPolicy * policy, StoreEntry * entry); +}; + +struct _RemovalPolicyWalker { + RemovalPolicy *_policy; + void *_data; + const StoreEntry *(*Next) (RemovalPolicyWalker * walker); + void (*Done) (RemovalPolicyWalker * walker); +}; + +struct _RemovalPurgeWalker { + RemovalPolicy *_policy; + void *_data; + int scanned, max_scan, locked; + StoreEntry *(*Next) (RemovalPurgeWalker * walker); + void (*Done) (RemovalPurgeWalker * walker); +}; + +/* This structure can be freed while object is purged out from memory */ +struct _MemObject { + method_t method; + char *url; + mem_hdr data_hdr; + squid_off_t inmem_hi; + squid_off_t inmem_lo; + int serverfd; /* Record the server's fd if we have too much + * data waiting to send to the client */ + dlink_list clients; + int nclients; + struct { + squid_off_t queue_offset; /* relative to in-mem data */ + mem_node *memnode; /* which node we're currently paging out */ + storeIOState *sio; + } swapout; + HttpReply *reply; + request_t *request; + struct timeval start_ping; + IRCB *ping_reply_callback; + void *ircb_data; + struct { + STABH *callback; + void *data; + } abort; + char *log_url; + RemovalPolicyNode repl; + int id; + squid_off_t object_sz; + size_t swap_hdr_sz; +#if URL_CHECKSUM_DEBUG + unsigned int chksum; +#endif + const char *vary_hdr; + const char *vary_headers; + const char *vary_encoding; + StoreEntry *ims_entry; + time_t refresh_timestamp; +}; + +struct _StoreEntry { + hash_link hash; /* must be first */ + MemObject *mem_obj; + RemovalPolicyNode repl; + /* START OF ON-DISK STORE_META_STD TLV field */ + time_t timestamp; + time_t lastref; + time_t expires; + time_t lastmod; + squid_file_sz swap_file_sz; + u_short refcount; + u_short flags; + /* END OF ON-DISK STORE_META_STD */ + sfileno swap_filen:25; + sdirno swap_dirn:7; + u_short lock_count; /* Assume < 65536! */ + mem_status_t mem_status:3; + ping_status_t ping_status:3; + store_status_t store_status:3; + swap_status_t swap_status:3; +}; + +struct _SwapDir { + const char *type; + int cur_size; + int low_size; + int max_size; + char *path; + int index; /* This entry's index into the swapDirs array */ + squid_off_t max_objsize; + RemovalPolicy *repl; + int removals; + int scanned; + struct { + unsigned int selected:1; + unsigned int read_only:1; + } flags; + STINIT *init; /* Initialise the fs */ + STCHECKCONFIG *checkconfig; /* Verify configuration */ + STNEWFS *newfs; /* Create a new fs */ + STDUMP *dump; /* Dump fs config snippet */ + STFREE *freefs; /* Free the fs data */ + STDBLCHECK *dblcheck; /* Double check the obj integrity */ + STSTATFS *statfs; /* Dump fs statistics */ + STMAINTAINFS *maintainfs; /* Replacement maintainence */ + STCHECKOBJ *checkobj; /* Check if the fs will store an object */ + STCHECKLOADAV *checkload; /* Check if the fs is getting overloaded .. */ + /* These two are notifications */ + STREFOBJ *refobj; /* Reference this object */ + STUNREFOBJ *unrefobj; /* Unreference this object */ + STCALLBACK *callback; /* Handle pending callbacks */ + STSYNC *sync; /* Sync the directory */ + struct { + STOBJCREATE *create; + STOBJOPEN *open; + STOBJCLOSE *close; + STOBJREAD *read; + STOBJWRITE *write; + STOBJUNLINK *unlink; + STOBJRECYCLE *recycle; + } obj; + struct { + STLOGOPEN *open; + STLOGCLOSE *close; + STLOGWRITE *write; + struct { + STLOGCLEANSTART *start; + STLOGCLEANNEXTENTRY *nextentry; + STLOGCLEANWRITE *write; + STLOGCLEANDONE *done; + void *state; + } clean; + int writes_since_clean; + } log; + struct { + int blksize; + } fs; + void *fsdata; +}; + +struct _request_flags { + unsigned int range:1; + unsigned int nocache:1; + unsigned int ims:1; + unsigned int auth:1; + unsigned int cachable:1; + unsigned int hierarchical:1; + unsigned int loopdetect:1; + unsigned int proxy_keepalive:1; + unsigned int proxying:1; /* this should be killed, also in httpstateflags */ + unsigned int refresh:1; + unsigned int redirected:1; + unsigned int need_validation:1; +#if HTTP_VIOLATIONS + unsigned int nocache_hack:1; /* for changing/ignoring no-cache requests */ +#endif + unsigned int accelerated:1; + unsigned int transparent:1; + unsigned int internal:1; + unsigned int body_sent:1; + unsigned int reset_tcp:1; + unsigned int must_keepalive:1; + unsigned int connection_auth:1; /* Request wants connection oriented auth */ + unsigned int connection_proxy_auth:1; /* Request wants connection oriented auth */ + unsigned int no_connection_auth:1; /* Connection oriented auth can not be supported */ + unsigned int pinned:1; /* Request seont on a pinned connection */ + unsigned int auth_sent:1; /* Authentication forwarded */ +#if LINUX_TPROXY + unsigned int tproxy:1; +#endif + unsigned int collapsed:1; /* This request was collapsed. Don't trust the store entry to be valid */ +}; + +struct _link_list { + void *ptr; + struct _link_list *next; +}; + +struct _storeIOState { + sdirno swap_dirn; + sfileno swap_filen; + StoreEntry *e; /* Need this so the FS layers can play god */ + mode_t mode; + squid_off_t st_size; /* do stat(2) after read open */ + squid_off_t offset; /* current on-disk offset pointer */ + squid_off_t write_offset; /* current storeWrite offset pointer */ + STFNCB *file_callback; /* called on delayed sfileno assignments */ + STIOCB *callback; + void *callback_data; + struct { + STRCB *callback; + void *callback_data; + } read; + struct { + unsigned int closing:1; /* debugging aid */ + } flags; + void *fsstate; +}; + +struct _request_t { + method_t method; + protocol_t protocol; + char login[MAX_LOGIN_SZ]; + char host[SQUIDHOSTNAMELEN + 1]; + auth_user_request_t *auth_user_request; + u_short port; + String urlpath; + char *canonical; + int link_count; /* free when zero */ + request_flags flags; + HttpHdrCc *cache_control; + HttpHdrRange *range; + http_version_t http_ver; + time_t ims; + int imslen; + int max_forwards; + /* these in_addr's could probably be sockaddr_in's */ + in_port_t client_port; + struct in_addr client_addr; +#if FOLLOW_X_FORWARDED_FOR + struct in_addr indirect_client_addr; /* after following X-Forwarded-For */ +#endif /* FOLLOW_X_FORWARDED_FOR */ + struct in_addr my_addr; + unsigned short my_port; + HttpHeader header; + squid_off_t content_length; + HierarchyLogEntry hier; + err_type err_type; + char *peer_login; /* Configured peer login:password */ + time_t lastmod; /* Used on refreshes */ + char *vary_hdr; /* Used when varying entities are detected. Changes how the store key is calculated */ + char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */ + String vary_encoding; /* Used when varying entities are detected. Changes how the store key is calculated. */ + VaryData *vary; + Array *etags; /* possible known entity tags (Vary MISS) */ + char *etag; /* current entity tag, cache validation */ + unsigned int done_etag:1; /* We have done clientProcessETag on this, don't attempt it again */ + char *urlgroup; /* urlgroup, returned by redirectors */ + char *peer_domain; /* Configured peer forceddomain */ +#if HS_FEAT_ICAP + icap_class *class; +#endif + BODY_HANDLER *body_reader; + void *body_reader_data; + String extacl_log; /* String to be used for access.log purposes */ + const char *extacl_user; /* User name returned by extacl lookup */ + const char *extacl_passwd; /* Password returned by extacl lookup */ +#if FOLLOW_X_FORWARDED_FOR + /* XXX a list of IP addresses would be a better data structure + * than this String */ + String x_forwarded_for_iterator; +#endif /* FOLLOW_X_FORWARDED_FOR */ + ConnStateData *pinned_connection; /* If set then this request is tighly tied to the corresponding client side connetion */ +}; + +struct _cachemgr_passwd { + char *passwd; + wordlist *actions; + cachemgr_passwd *next; +}; + +struct _refresh_t { + const char *pattern; + regex_t compiled_pattern; + time_t min; + double pct; + time_t max; + refresh_t *next; + struct { + unsigned int icase:1; +#if HTTP_VIOLATIONS + unsigned int override_expire:1; + unsigned int override_lastmod:1; + unsigned int reload_into_ims:1; + unsigned int ignore_reload:1; + unsigned int ignore_no_cache:1; + unsigned int ignore_private:1; + unsigned int ignore_auth:1; +#endif + } flags; +}; + +struct _ErrorState { + err_type type; + int page_id; + http_status http_status; + auth_user_request_t *auth_user_request; + request_t *request; + char *url; + int xerrno; + char *dnsserver_msg; + time_t ttl; + struct in_addr src_addr; + char *redirect_url; + ERCB *callback; + void *callback_data; + struct { + unsigned int flag_cbdata:1; + } flags; + struct { + wordlist *server_msg; + char *request; + char *reply; + } ftp; + char *request_hdrs; +}; + +/* + * "very generic" histogram; + * see important comments on hbase_f restrictions in StatHist.c + */ +struct _StatHist { + int *bins; + int capacity; + double min; + double max; + double scale; + hbase_f *val_in; /* e.g., log() for log-based histogram */ + hbase_f *val_out; /* e.g., exp() for log based histogram */ +}; + +/* + * if you add a field to StatCounters, + * you MUST sync statCountersInitSpecial, statCountersClean, and statCountersCopy + */ +struct _StatCounters { + struct { + int clients; + int requests; + int hits; + int mem_hits; + int disk_hits; + int errors; + kb_t kbytes_in; + kb_t kbytes_out; + kb_t hit_kbytes_out; + StatHist miss_svc_time; + StatHist nm_svc_time; + StatHist nh_svc_time; + StatHist hit_svc_time; + StatHist all_svc_time; + } client_http; + struct { + struct { + int requests; + int errors; + kb_t kbytes_in; + kb_t kbytes_out; + } all , http, ftp, other; + } +#if HS_FEAT_ICAP + icap, +#endif + server; + struct { + int pkts_sent; + int queries_sent; + int replies_sent; + int pkts_recv; + int queries_recv; + int replies_recv; + int hits_sent; + int hits_recv; + int replies_queued; + int replies_dropped; + kb_t kbytes_sent; + kb_t q_kbytes_sent; + kb_t r_kbytes_sent; + kb_t kbytes_recv; + kb_t q_kbytes_recv; + kb_t r_kbytes_recv; + StatHist query_svc_time; + StatHist reply_svc_time; + int query_timeouts; + int times_used; + } icp; + struct { + int pkts_sent; + int pkts_recv; + } htcp; + struct { + int requests; + } unlink; + struct { + StatHist svc_time; + } dns; + struct { + int times_used; + kb_t kbytes_sent; + kb_t kbytes_recv; + kb_t memory; + int msgs_sent; + int msgs_recv; +#if USE_CACHE_DIGESTS + cd_guess_stats guess; +#endif + StatHist on_xition_count; + } cd; + struct { + int times_used; + } netdb; + int page_faults; + int select_loops; + int select_fds; + double select_time; + double cputime; + struct timeval timestamp; + StatHist comm_icp_incoming; + StatHist comm_dns_incoming; + StatHist comm_http_incoming; + StatHist select_fds_hist; + struct { + struct { + int opens; + int closes; + int reads; + int writes; + int seeks; + int unlinks; + } disk; + struct { + int accepts; + int sockets; + int connects; + int binds; + int closes; + int reads; + int writes; + int recvfroms; + int sendtos; + } sock; + int polls; + int selects; + } syscalls; + int aborted_requests; + struct { + int files_cleaned; + int outs; + int ins; + } swap; +}; + +/* per header statistics */ +struct _HttpHeaderStat { + const char *label; + HttpHeaderMask *owner_mask; + + StatHist hdrUCountDistr; + StatHist fieldTypeDistr; + StatHist ccTypeDistr; + + int parsedCount; + int ccParsedCount; + int destroyedCount; + int busyDestroyedCount; +}; + + +struct _tlv { + char type; + int length; + void *value; + tlv *next; +}; + +/* + * Do we need to have the dirn in here? I don't think so, since we already + * know the dirn .. + */ +struct _storeSwapLogData { + char op; + sfileno swap_filen; + time_t timestamp; + time_t lastref; + time_t expires; + time_t lastmod; + squid_file_sz swap_file_sz; + u_short refcount; + u_short flags; + unsigned char key[MD5_DIGEST_CHARS]; +}; + +struct _storeSwapLogHeader { + char op; + int version; + int record_size; +}; + +#if SIZEOF_SQUID_FILE_SZ != SIZEOF_SIZE_T +struct _storeSwapLogDataOld { + char op; + sfileno swap_filen; + time_t timestamp; + time_t lastref; + time_t expires; + time_t lastmod; + size_t swap_file_sz; + u_short refcount; + u_short flags; + unsigned char key[MD5_DIGEST_CHARS]; +}; + +#endif + + +/* object to track per-action memory usage (e.g. #idle objects) */ +struct _MemMeter { + ssize_t level; /* current level (count or volume) */ + ssize_t hwater_level; /* high water mark */ + time_t hwater_stamp; /* timestamp of last high water mark change */ +}; + +/* object to track per-pool memory usage (alloc = inuse+idle) */ +struct _MemPoolMeter { + MemMeter alloc; + MemMeter inuse; + MemMeter idle; + gb_t saved; + gb_t total; +}; + +/* a pool is a [growing] space for objects of the same size */ +struct _MemPool { + const char *label; + size_t obj_size; +#if DEBUG_MEMPOOL + size_t real_obj_size; /* with alignment */ +#endif + Stack pstack; /* stack for free pointers */ + MemPoolMeter meter; +#if DEBUG_MEMPOOL + MemPoolMeter diff_meter; +#endif +}; + +struct _ClientInfo { + hash_link hash; /* must be first */ + struct in_addr addr; + struct { + int result_hist[LOG_TYPE_MAX]; + int n_requests; + kb_t kbytes_in; + kb_t kbytes_out; + kb_t hit_kbytes_out; + } Http, Icp; + struct { + time_t time; + int n_req; + int n_denied; + } cutoff; + int n_established; /* number of current established connections */ + time_t last_seen; +}; + +struct _CacheDigest { + /* public, read-only */ + char *mask; /* bit mask */ + int mask_size; /* mask size in bytes */ + int capacity; /* expected maximum for .count, not a hard limit */ + int bits_per_entry; /* number of bits allocated for each entry from capacity */ + int count; /* number of digested entries */ + int del_count; /* number of deletions performed so far */ +}; + +struct _FwdServer { + peer *peer; /* NULL --> origin server */ + hier_code code; + FwdServer *next; +}; + +struct _FwdState { + int client_fd; /* XXX unnecessary */ + StoreEntry *entry; + request_t *request; + FwdServer *servers; + int server_fd; + ErrorState *err; + time_t start; + int n_tries; + int origin_tries; +#if WIP_FWD_LOG + http_status last_status; +#endif + struct { + unsigned int dont_retry:1; + unsigned int ftp_pasv_failed:1; + } flags; +#if LINUX_NETFILTER + struct sockaddr_in src; +#endif +}; + +#if USE_HTCP +struct _htcpReplyData { + int hit; + HttpHeader hdr; + u_num32 msg_id; + double version; + struct { + /* cache-to-origin */ + double rtt; + int samp; + int hops; + } cto; +}; + +#endif + + +struct _helper_request { + char *buf; + HLPCB *callback; + void *data; + struct timeval dispatch_time; +}; + +struct _helper_stateful_request { + char *buf; + HLPSCB *callback; + void *data; + struct timeval dispatch_time; +}; + + +struct _helper { + wordlist *cmdline; + dlink_list servers; + dlink_list queue; + const char *id_name; + int n_to_start; + int n_running; + int n_active; + int ipc_type; + int concurrency; + time_t last_queue_warn; + struct { + int requests; + int replies; + int queue_size; + int max_queue_size; + int avg_svc_time; + } stats; + time_t last_restart; +}; + +struct _helper_stateful { + wordlist *cmdline; + dlink_list servers; + dlink_list queue; + const char *id_name; + int n_to_start; + int n_running; + int n_active; + int ipc_type; + int concurrency; + MemPool *datapool; + HLPSAVAIL *IsAvailable; + HLPSRESET *Reset; + time_t last_queue_warn; + struct { + int requests; + int replies; + int queue_size; + int max_queue_size; + int avg_svc_time; + } stats; + time_t last_restart; +}; + +struct _helper_server { + int index; + int pid; + int rfd; + int wfd; + MemBuf wqueue; + char *rbuf; + size_t rbuf_sz; + int roffset; + dlink_node link; + helper *parent; + helper_request **requests; + struct _helper_flags { + unsigned int writing:1; + unsigned int closing:1; + unsigned int shutdown:1; + } flags; + struct { + int uses; + unsigned int pending; + } stats; + void *hIpc; +}; + + +struct _helper_stateful_server { + int index; + int pid; + int rfd; + int wfd; + char *buf; + size_t buf_sz; + int offset; + struct timeval dispatch_time; + struct timeval answer_time; + dlink_node link; + statefulhelper *parent; + helper_stateful_request *request; + struct _helper_stateful_flags { + unsigned int alive:1; + unsigned int busy:1; + unsigned int closing:1; + unsigned int shutdown:1; + unsigned int reserved:1; + } flags; + struct { + int uses; + int submits; + int releases; + } stats; + void *data; /* State data used by the calling routines */ + void *hIpc; +}; + +/* + * use this when you need to pass callback data to a blocking + * operation, but you don't want to add that pointer to cbdata + */ +struct _generic_cbdata { + void *data; +}; + +struct _store_rebuild_data { + int objcount; /* # objects successfully reloaded */ + int expcount; /* # objects expired */ + int scancount; /* # entries scanned or read from state file */ + int clashcount; /* # swapfile clashes avoided */ + int dupcount; /* # duplicates purged */ + int cancelcount; /* # SWAP_LOG_DEL objects purged */ + int invalid; /* # bad lines */ + int badflags; /* # bad e->flags */ + int bad_log_op; + int zero_object_sz; +}; + +/* + * This defines an fs type + */ + +struct _storefs_entry { + const char *typestr; + STFSPARSE *parsefunc; + STFSRECONFIGURE *reconfigurefunc; + STFSSHUTDOWN *donefunc; +}; + +/* + * This defines an repl type + */ + +struct _storerepl_entry { + const char *typestr; + REMOVALPOLICYCREATE *create; +}; + +/* + * Async disk IO - this defines a async disk io queue + */ + +struct _diskd_queue { + int smsgid; /* send sysvmsg id */ + int rmsgid; /* recv sysvmsg id */ + int wfd; /* queue file descriptor ? */ + int away; /* number of requests away */ + int sent_count; /* number of messages sent */ + int recv_count; /* number of messages received */ + struct { + char *buf; /* shm buffer */ + link_list *stack; + int id; /* sysvshm id */ + } shm; +}; + +struct _Logfile { + int fd; + char path[MAXPATHLEN]; + char *buf; + size_t bufsz; + ssize_t offset; + struct { + unsigned int fatal; + unsigned int syslog; + } flags; + int syslog_priority; +}; + +struct _logformat { + char *name; + logformat_token *format; + logformat *next; +}; + +struct _customlog { + char *filename; + acl_list *aclList; + logformat *logFormat; + Logfile *logfile; + customlog *next; + enum { + CLF_UNKNOWN, + CLF_AUTO, + CLF_CUSTOM, + CLF_SQUID, + CLF_COMMON, + CLF_NONE + } type; +}; + +struct cache_dir_option { + const char *name; + void (*parse) (SwapDir * sd, const char *option, const char *value, int reconfiguring); + void (*dump) (StoreEntry * e, const char *option, SwapDir * sd); +}; + +struct error_map_entry { + struct error_map_entry *next; + char *value; + int status; +}; +struct _errormap { + errormap *next; + char *url; + struct error_map_entry *map; +}; + +struct _VaryData { + char *key; + char *etag; + Array etags; +}; + +#endif /* SQUID_STRUCTS_H */ diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/f3/60c7157c6888001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/f3/60c7157c6888001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/f3/60c7157c6888001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/f3/60c7157c6888001b16add8656805b17d 2006-12-10 16:09:19.000000000 +0200 @@ -0,0 +1,5286 @@ + +# +# $Id: cf.data.pre,v 1.374 2006/10/12 20:48:48 wessels Exp $ +# +# +# 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) 2000 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. +# + +COMMENT_START + WELCOME TO SQUID @VERSION@ + ---------------------------- + + This is the default Squid configuration file. You may wish + to look at the Squid home page (http://www.squid-cache.org/) + for the FAQ and other documentation. + + The default Squid config file shows what the defaults for + various options happen to be. If you don't need to change the + default, you shouldn't uncomment the line. Doing so may cause + run-time problems. In some cases "none" refers to no default + setting at all, while in other cases it refers to a valid + option - the comments for that keyword indicate if this is the + case. + +COMMENT_END + +COMMENT_START + NETWORK OPTIONS + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: http_port ascii_port +TYPE: http_port_list +DEFAULT: none +LOC: Config.Sockaddr.http +DOC_START + Usage: port [options] + hostname:port [options] + 1.2.3.4:port [options] + + The socket addresses where Squid will listen for HTTP client + requests. You may specify multiple socket addresses. + There are three forms: port alone, hostname with port, and + IP address with port. If you specify a hostname or IP + address, Squid binds the socket to that specific + address. This replaces the old 'tcp_incoming_address' + option. Most likely, you do not need to bind to a specific + address, so you can use the port number alone. + + The default port number is 3128. + + If you are running Squid in accelerator mode, you + probably want to listen on port 80 also, or instead. + + The -a command line option will override the *first* port + number listed here. That option will NOT override an IP + address, however. + + You may specify multiple socket addresses on multiple lines. + + options are: + transparent Support for transparent proxies + vhost Accelerator using Host directive + vport Accelerator with IP virtual host support + vport= As above, but uses specified port number + rather than the http_port number. + defaultsite= Main web site name for accelerators. + urlgroup= Default urlgroup to mark requests + with (see also acl urlgroup and + url_rewrite_program) + protocol= Protocol to reconstruct accelerated + requests with. Defaults to http. + no-connection-auth + Prevent forwarding of Microsoft + connection oriented authentication + (NTLM, Negotiate and Kerberos) + tproxy Support Linux TPROXY for spoofing + outgoing connections using the client + IP address. + + If you run Squid on a dual-homed machine with an internal + and an external interface we recommend you to specify the + internal address:port in http_port. This way Squid will only be + visible on the internal address. + +NOCOMMENT_START +# Squid normally listens to port 3128 +http_port 3128 +NOCOMMENT_END +DOC_END + +NAME: https_port +IFDEF: USE_SSL +TYPE: https_port_list +DEFAULT: none +LOC: Config.Sockaddr.https +DOC_START + Usage: [ip:]port cert=certificate.pem [key=key.pem] [options...] + + The socket address where Squid will listen for HTTPS client + requests. + + This is really only useful for situations where you are running + squid in accelerator mode and you want to do the SSL work at the + accelerator level. + + You may specify multiple socket addresses on multiple lines, + each with their own SSL certificate and/or options. + + Options: + + defaultsite= The name of the https site presented on + this port. + + urlgroup= Default urlgroup to mark requests with (see + also acl urlgroup and url_rewrite_program) + + protocol= Protocol to reconstruct accelerated requests + with. Defaults to https. + + cert= Path to SSL certificate (PEM format) + + key= Path to SSL private key file (PEM format) + if not specified, the certificate file is + assumed to be a combined certificate and + key file + + version= The version of SSL/TLS supported + 1 automatic (default) + 2 SSLv2 only + 3 SSLv3 only + 4 TLSv1 only + + cipher= Colon separated list of supported ciphers + + options= Various SSL engine options. The most important + being: + NO_SSLv2 Disallow the use of SSLv2 + NO_SSLv3 Disallow the use of SSLv3 + NO_TLSv1 Disallow the use of TLSv1 + SINGLE_DH_USE Always create a new key when using + temporary/ephemeral DH key exchanges + See src/ssl_support.c or OpenSSL SSL_CTX_set_options + documentation for a complete list of options. + + clientca= File containing the list of CAs to use when + requesting a client certificate + + cafile= File containing additional CA certificates to + use when verifying client certificates. If unset + clientca will be used. + + capath= Directory containing additional CA certificates + and CRL lists to use when verifying client certificates + + crlfile= File of additional CRL lists to use when verifying + the client certificate, in addition to CRLs stored in + the capath. Implies VERIFY_CRL flag below. + + dhparams= File containing DH parameters for temporary/ephemeral + DH key exchanges + + sslflags= Various flags modifying the use of SSL: + DELAYED_AUTH + Don't request client certificates + immediately, but wait until acl processing + requires a certificate (not yet implemented) + NO_DEFAULT_CA + Don't use the default CA lists built in + to OpenSSL. + NO_SESSION_REUSE + Don't allow for session reuse. Each connection + will result in a new SSL session. + VERIFY_CRL + Verify CRL lists when accepting client + certificates + VERIFY_CRL_ALL + Verify CRL lists for all certificates in the + client certificate chain + + sslcontext= SSL session ID context identifier. + +DOC_END + +NAME: ssl_unclean_shutdown +IFDEF: USE_SSL +TYPE: onoff +DEFAULT: off +LOC: Config.SSL.unclean_shutdown +DOC_START + Some browsers (especially MSIE) bugs out on SSL shutdown + messages. +DOC_END + +NAME: ssl_engine +IFDEF: USE_SSL +TYPE: string +LOC: Config.SSL.ssl_engine +DEFAULT: none +DOC_START + The OpenSSL engine to use. You will need to set this if you + would like to use hardware SSL acceleration for example. +DOC_END + +NAME: sslproxy_client_certificate +IFDEF: USE_SSL +DEFAULT: none +LOC: Config.ssl_client.cert +TYPE: string +DOC_START + Client SSL Certificate to use when proxying https:// URLs +DOC_END + +NAME: sslproxy_client_key +IFDEF: USE_SSL +DEFAULT: none +LOC: Config.ssl_client.key +TYPE: string +DOC_START + Client SSL Key to use when proxying https:// URLs +DOC_END + +NAME: sslproxy_version +IFDEF: USE_SSL +DEFAULT: 1 +LOC: Config.ssl_client.version +TYPE: int +DOC_START + SSL version level to use when proxying https:// URLs +DOC_END + +NAME: sslproxy_options +IFDEF: USE_SSL +DEFAULT: none +LOC: Config.ssl_client.options +TYPE: string +DOC_START + SSL engine options to use when proxying https:// URLs +DOC_END + +NAME: sslproxy_cipher +IFDEF: USE_SSL +DEFAULT: none +LOC: Config.ssl_client.cipher +TYPE: string +DOC_START + SSL cipher list to use when proxying https:// URLs +DOC_END + +NAME: sslproxy_cafile +IFDEF: USE_SSL +DEFAULT: none +LOC: Config.ssl_client.cafile +TYPE: string +DOC_START +DOC_END + +NAME: sslproxy_capath +IFDEF: USE_SSL +DEFAULT: none +LOC: Config.ssl_client.capath +TYPE: string +DOC_START +DOC_END + +NAME: sslproxy_flags +IFDEF: USE_SSL +DEFAULT: none +LOC: Config.ssl_client.flags +TYPE: string +DOC_START +DOC_END + +NAME: sslpassword_program +IFDEF: USE_SSL +DEFAULT: none +LOC: Config.Program.ssl_password +TYPE: string +DOC_START + Specify a program used for entering SSL key passphrases + when using encrypted SSL certificate keys. If not specified + keys must either be unencrypted, or Squid started with the -N + option to allow it to query interactively for the passphrase. +DOC_END + +NAME: icp_port udp_port +TYPE: ushort +DEFAULT: @DEFAULT_ICP_PORT@ +LOC: Config.Port.icp +DOC_START + The port number where Squid sends and receives ICP queries to + and from neighbor caches. Default is 3130. To disable use + "0". May be overridden with -u on the command line. +DOC_END + +NAME: htcp_port +IFDEF: USE_HTCP +TYPE: ushort +DEFAULT: 4827 +LOC: Config.Port.htcp +DOC_START + The port number where Squid sends and receives HTCP queries to + and from neighbor caches. Default is 4827. To disable use + "0". +DOC_END + + +NAME: mcast_groups +TYPE: wordlist +LOC: Config.mcast_group_list +DEFAULT: none +DOC_START + This tag specifies a list of multicast groups which your server + should join to receive multicasted ICP queries. + + NOTE! Be very careful what you put here! Be sure you + understand the difference between an ICP _query_ and an ICP + _reply_. This option is to be set only if you want to RECEIVE + multicast queries. Do NOT set this option to SEND multicast + ICP (use cache_peer for that). ICP replies are always sent via + unicast, so this option does not affect whether or not you will + receive replies from multicast group members. + + You must be very careful to NOT use a multicast address which + is already in use by another group of caches. + + If you are unsure about multicast, please read the Multicast + chapter in the Squid FAQ (http://www.squid-cache.org/FAQ/). + + Usage: mcast_groups 239.128.16.128 224.0.1.20 + + By default, Squid doesn't listen on any multicast groups. +DOC_END + + +NAME: udp_incoming_address +TYPE: address +LOC:Config.Addrs.udp_incoming +DEFAULT: 0.0.0.0 +DOC_NONE + +NAME: udp_outgoing_address +TYPE: address +LOC: Config.Addrs.udp_outgoing +DEFAULT: 255.255.255.255 +DOC_START + udp_incoming_address is used for the ICP socket receiving packets + from other caches. + udp_outgoing_address is used for ICP packets sent out to other + caches. + + The default behavior is to not bind to any specific address. + + A udp_incoming_address value of 0.0.0.0 indicates Squid + should listen for UDP messages on all available interfaces. + + If udp_outgoing_address is set to 255.255.255.255 (the default) + it will use the same socket as udp_incoming_address. Only + change this if you want to have ICP queries sent using another + address than where this Squid listens for ICP queries from other + caches. + + NOTE, udp_incoming_address and udp_outgoing_address can not + have the same value since they both use port 3130. +DOC_END + +COMMENT_START + OPTIONS WHICH AFFECT THE NEIGHBOR SELECTION ALGORITHM + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: cache_peer +TYPE: peer +DEFAULT: none +LOC: Config.peers +DOC_START + To specify other caches in a hierarchy, use the format: + + cache_peer hostname type http_port icp_port [options] + + For example, + + # proxy icp + # hostname type port port options + # -------------------- -------- ----- ----- ----------- + cache_peer parent.foo.net parent 3128 3130 [proxy-only] + cache_peer sib1.foo.net sibling 3128 3130 [proxy-only] + cache_peer sib2.foo.net sibling 3128 3130 [proxy-only] + + type: either 'parent', 'sibling', or 'multicast'. + + proxy_port: The port number where the cache listens for proxy + requests. + + icp_port: Used for querying neighbor caches about + objects. To have a non-ICP neighbor + specify '7' for the ICP port and make sure the + neighbor machine has the UDP echo port + enabled in its /etc/inetd.conf file. + + options: proxy-only + weight=n + ttl=n + no-query + default + round-robin + multicast-responder + closest-only + no-digest + no-netdb-exchange + no-delay + login=user:password | PASS | *:password + connect-timeout=nn + digest-url=url + allow-miss + max-conn + htcp + htcp-oldsquid + carp-load-factor + originserver + userhash + sourcehash + name=xxx + monitorurl=url + monitorsize=sizespec + monitorinterval=seconds + monitortimeout=seconds + group=name + forceddomain=name + ssl + sslcert=/path/to/ssl/certificate + sslkey=/path/to/ssl/key + sslversion=1|2|3|4 + sslcipher=... + ssloptions=... + front-end-https[=on|auto] + connection-auth[=on|off|auto] + + use 'proxy-only' to specify objects fetched + from this cache should not be saved locally. + + use 'weight=n' to specify a weighted parent. + The weight must be an integer. The default weight + is 1, larger weights are favored more. + + use 'ttl=n' to specify a IP multicast TTL to use + when sending an ICP queries to this address. + Only useful when sending to a multicast group. + Because we don't accept ICP replies from random + hosts, you must configure other group members as + peers with the 'multicast-responder' option below. + + use 'no-query' to NOT send ICP queries to this + neighbor. + + use 'default' if this is a parent cache which can + be used as a "last-resort." You should probably + only use 'default' in situations where you cannot + use ICP with your parent cache(s). + + use 'round-robin' to define a set of parents which + should be used in a round-robin fashion in the + absence of any ICP queries. + + 'multicast-responder' indicates the named peer + is a member of a multicast group. ICP queries will + not be sent directly to the peer, but ICP replies + will be accepted from it. + + 'closest-only' indicates that, for ICP_OP_MISS + replies, we'll only forward CLOSEST_PARENT_MISSes + and never FIRST_PARENT_MISSes. + + use 'no-digest' to NOT request cache digests from + this neighbor. + + 'no-netdb-exchange' disables requesting ICMP + RTT database (NetDB) from the neighbor. + + use 'no-delay' to prevent access to this neighbor + from influencing the delay pools. + + use 'login=user:password' if this is a personal/workgroup + proxy and your parent requires proxy authentication. + Note: The string can include URL escapes (i.e. %20 for + spaces). This also means % must be written as %%. + + use 'login=PASS' to forward authentication to the peer. + Needed if the peer requires login. + Note: To combine this with local authentication the Basic + authentication scheme must be used, and both servers must + share the same user database as HTTP only allows for + a single login (one for proxy, one for origin server). + + use 'login=*:password' to pass the username to the + upstream cache, but with a fixed password. This is meant + to be used when the peer is in another administrative + domain, but it is still needed to identify each user. + The star can optionally be followed by some extra + information which is added to the username. This can + be used to identify this proxy to the peer, similar to + the login=username:password option above. + + use 'connect-timeout=nn' to specify a peer + specific connect timeout (also see the + peer_connect_timeout directive) + + use 'digest-url=url' to tell Squid to fetch the cache + digest (if digests are enabled) for this host from + the specified URL rather than the Squid default + location. + + use 'allow-miss' to disable Squid's use of only-if-cached + when forwarding requests to siblings. This is primarily + useful when icp_hit_stale is used by the sibling. To + extensive use of this option may result in forwarding + loops, and you should avoid having two-way peerings + with this option. (for example to deny peer usage on + requests from peer by denying cache_peer_access if the + source is a peer) + + use 'max-conn' to limit the amount of connections Squid + may open to this peer. + + use 'htcp' to send HTCP, instead of ICP, queries + to the neighbor. You probably also want to + set the "icp port" to 4827 instead of 3130. + + use 'htcp-oldsquid' to send HTCP to old Squid versions + + use 'carp-load-factor=f' to define a parent + cache as one participating in a CARP array. + The 'f' values for all CARP parents must add + up to 1.0. + + 'originserver' causes this parent peer to be contacted as + a origin server. Meant to be used in accelerator setups. + + use 'userhash' to load-balance amongst a set of parents + based on the client proxy_auth or ident username. + + use 'sourcehash' to load-balanse amongs a set of parents + based on the client source ip. + + use 'name=xxx' if you have multiple peers on the same + host but different ports. This name can then be used to + differentiate the peers in cache_peer_access and similar + directives. + + use 'monitorurl=url' to have periodically request a given + URL from the peer, and only consider the peer as alive + if this monitoring is successful (default none) + + use 'monitorsize=min[-max]' to limit the size range of + 'monitorurl' replies considered valid. Defaults to 0 to + accept any size replies as valid. + + use 'monitorinterval=seconds' to change frequency of + how often the peer is monitored with 'monitorurl' + (default 300 for a 5 minute interval). If set to 0 + then monitoring is disabled even if a URL is defined. + + use 'monitortimeout=seconds' to change the timeout of + 'monitorurl'. Defaults to 'monitorinterval'. + + use 'forceddomain=name' to forcibly set the Host header + of requests forwarded to this peer. Useful in accelerator + setups where the server (peer) expects a certain domain + name and using redirectors to feed this domain name + is not feasible. + + use 'ssl' to indicate that connections to this peer should + bs SSL/TLS encrypted. + + use 'sslcert=/path/to/ssl/certificate' to specify a client + SSL certificate to use when connecting to this peer. + + use 'sslkey=/path/to/ssl/key' to specify the private SSL + key corresponding to sslcert above. If 'sslkey' is not + specified then 'sslcert' is assumed to reference a + combined file containing both the certificate and the key. + + use sslversion=1|2|3|4 to specify the SSL version to use + when connecting to this peer + 1 = automatic (default) + 2 = SSL v2 only + 3 = SSL v3 only + 4 = TLS v1 only + + use sslcipher=... to specify the list of valid SSL ciphers + to use when connecting to this peer. + + use ssloptions=... to specify various SSL engine options: + NO_SSLv2 Disallow the use of SSLv2 + NO_SSLv3 Disallow the use of SSLv3 + NO_TLSv1 Disallow the use of TLSv1 + See src/ssl_support.c or the OpenSSL documentation for + a more complete list. + + use sslcafile=... to specify a file containing + additional CA certificates to use when verifying the + peer certificate. + + use sslcapath=... to specify a directory containing + additional CA certificates to use when verifying the + peer certificate. + + use sslcrlfile=... to specify a certificate revocation + list file to use when verifying the peer certificate. + + use sslflags=... to specify various flags modifying the + SSL implementation: + DONT_VERIFY_PEER + Accept certificates even if they fail to + verify. + NO_DEFAULT_CA + Don't use the default CA list built in + to OpenSSL. + + use ssldomain= to specify the peer name as advertised + in it's certificate. Used for verifying the correctness + of the received peer certificate. If not specified the + peer hostname will be used. + + use front-end-https to enable the "Front-End-Https: On" + header needed when using Squid as a SSL frontend in front + of Microsoft OWA. See MS KB document Q307347 for details + on this header. If set to auto then the header will + only be added if the request is forwarded as a https:// + URL. + + use connection-auth=off to tell Squid that this peer does + not support Microsoft connection oriented authentication, + and any such challenges received from there should be + ignored. Default is auto to automatically determine the + status of the peer. + + NOTE: non-ICP/HTCP neighbors must be specified as 'parent'. +DOC_END + +NAME: cache_peer_domain cache_host_domain +TYPE: hostdomain +DEFAULT: none +LOC: none +DOC_START + Use to limit the domains for which a neighbor cache will be + queried. Usage: + + cache_peer_domain cache-host domain [domain ...] + cache_peer_domain cache-host !domain + + For example, specifying + + cache_peer_domain parent.foo.net .edu + + has the effect such that UDP query packets are sent to + 'bigserver' only when the requested object exists on a + server in the .edu domain. Prefixing the domain name + with '!' means the cache will be queried for objects + NOT in that domain. + + NOTE: * Any number of domains may be given for a cache-host, + either on the same or separate lines. + * When multiple domains are given for a particular + cache-host, the first matched domain is applied. + * Cache hosts with no domain restrictions are queried + for all requests. + * There are no defaults. + * There is also a 'cache_peer_access' tag in the ACL + section. +DOC_END + + +NAME: neighbor_type_domain +TYPE: hostdomaintype +DEFAULT: none +LOC: none +DOC_START + usage: neighbor_type_domain neighbor parent|sibling domain domain ... + + Modifying the neighbor type for specific domains is now + possible. You can treat some domains differently than the the + default neighbor type specified on the 'cache_peer' line. + Normally it should only be necessary to list domains which + should be treated differently because the default neighbor type + applies for hostnames which do not match domains listed here. + +EXAMPLE: + cache_peer parent cache.foo.org 3128 3130 + neighbor_type_domain cache.foo.org sibling .com .net + neighbor_type_domain cache.foo.org sibling .au .de +DOC_END + +NAME: icp_query_timeout +COMMENT: (msec) +DEFAULT: 0 +TYPE: int +LOC: Config.Timeout.icp_query +DOC_START + Normally Squid will automatically determine an optimal ICP + query timeout value based on the round-trip-time of recent ICP + queries. If you want to override the value determined by + Squid, set this 'icp_query_timeout' to a non-zero value. This + value is specified in MILLISECONDS, so, to use a 2-second + timeout (the old default), you would write: + + icp_query_timeout 2000 +DOC_END + +NAME: maximum_icp_query_timeout +COMMENT: (msec) +DEFAULT: 2000 +TYPE: int +LOC: Config.Timeout.icp_query_max +DOC_START + Normally the ICP query timeout is determined dynamically. But + sometimes it can lead to very large values (say 5 seconds). + Use this option to put an upper limit on the dynamic timeout + value. Do NOT use this option to always use a fixed (instead + of a dynamic) timeout value. To set a fixed timeout see the + 'icp_query_timeout' directive. +DOC_END + +NAME: mcast_icp_query_timeout +COMMENT: (msec) +DEFAULT: 2000 +TYPE: int +LOC: Config.Timeout.mcast_icp_query +DOC_START + For multicast peers, Squid regularly sends out ICP "probes" to + count how many other peers are listening on the given multicast + address. This value specifies how long Squid should wait to + count all the replies. The default is 2000 msec, or 2 + seconds. +DOC_END + +NAME: dead_peer_timeout +COMMENT: (seconds) +DEFAULT: 10 seconds +TYPE: time_t +LOC: Config.Timeout.deadPeer +DOC_START + This controls how long Squid waits to declare a peer cache + as "dead." If there are no ICP replies received in this + amount of time, Squid will declare the peer dead and not + expect to receive any further ICP replies. However, it + continues to send ICP queries, and will mark the peer as + alive upon receipt of the first subsequent ICP reply. + + This timeout also affects when Squid expects to receive ICP + replies from peers. If more than 'dead_peer' seconds have + passed since the last ICP reply was received, Squid will not + expect to receive an ICP reply on the next query. Thus, if + your time between requests is greater than this timeout, you + will see a lot of requests sent DIRECT to origin servers + instead of to your parents. +DOC_END + + +NAME: hierarchy_stoplist +TYPE: wordlist +DEFAULT: none +LOC: Config.hierarchy_stoplist +DOC_START + A list of words which, if found in a URL, cause the object to + be handled directly by this cache. In other words, use this + to not query neighbor caches for certain objects. You may + list this option multiple times. Note: never_direct overrides + this option. +NOCOMMENT_START +#We recommend you to use at least the following line. +hierarchy_stoplist cgi-bin ? +NOCOMMENT_END +DOC_END + + +NAME: cache no_cache +TYPE: acl_access +DEFAULT: none +LOC: Config.accessList.noCache +DOC_START + A list of ACL elements which, if matched, cause the request to + not be satisfied from the cache and the reply to not be cached. + In other words, use this to force certain objects to never be cached. + + You must use the word 'DENY' to indicate the ACL names which should + NOT be cached. + + Default is to allow all to be cached +NOCOMMENT_START +#We recommend you to use the following two lines. +acl QUERY urlpath_regex cgi-bin \? +cache deny QUERY +NOCOMMENT_END +DOC_END + +NAME: cache_vary +TYPE: onoff +DEFAULT: on +LOC: Config.onoff.cache_vary +DOC_START + Set to off to disable caching of Vary:in objects. +DOC_END + +NAME: broken_vary_encoding +TYPE: acl_access +DEFAULT: none +LOC: Config.accessList.vary_encoding +DOC_START + Many servers have broken support for on-the-fly Content-Encoding, + returning the same ETag on both plain and gzip:ed variants. + Vary replies matching this access list will have the cache split + on the Accept-Encoding header of the request and not trusting the + ETag to be unique. + +NOCOMMENT_START +# Apache mod_gzip and mod_deflate known to be broken so don't trust +# Apache to signal ETag correctly on such responses +acl apache rep_header Server ^Apache +broken_vary_encoding allow apache +NOCOMMENT_END +DOC_END + +COMMENT_START + OPTIONS WHICH AFFECT THE CACHE SIZE + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: cache_mem +COMMENT: (bytes) +TYPE: b_size_t +DEFAULT: 8 MB +LOC: Config.memMaxSize +DOC_START + NOTE: THIS PARAMETER DOES NOT SPECIFY THE MAXIMUM PROCESS SIZE. + IT ONLY PLACES A LIMIT ON HOW MUCH ADDITIONAL MEMORY SQUID WILL + USE AS A MEMORY CACHE OF OBJECTS. SQUID USES MEMORY FOR OTHER + THINGS AS WELL. SEE THE SQUID FAQ SECTION 8 FOR DETAILS. + + 'cache_mem' specifies the ideal amount of memory to be used + for: + * In-Transit objects + * Hot Objects + * Negative-Cached objects + + Data for these objects are stored in 4 KB blocks. This + parameter specifies the ideal upper limit on the total size of + 4 KB blocks allocated. In-Transit objects take the highest + priority. + + In-transit objects have priority over the others. When + additional space is needed for incoming data, negative-cached + and hot objects will be released. In other words, the + negative-cached and hot objects will fill up any unused space + not needed for in-transit objects. + + If circumstances require, this limit will be exceeded. + Specifically, if your incoming request rate requires more than + 'cache_mem' of memory to hold in-transit objects, Squid will + exceed this limit to satisfy the new requests. When the load + decreases, blocks will be freed until the high-water mark is + reached. Thereafter, blocks will be used to store hot + objects. +DOC_END + + +NAME: cache_swap_low +COMMENT: (percent, 0-100) +TYPE: int +DEFAULT: 90 +LOC: Config.Swap.lowWaterMark +DOC_NONE + +NAME: cache_swap_high +COMMENT: (percent, 0-100) +TYPE: int +DEFAULT: 95 +LOC: Config.Swap.highWaterMark +DOC_START + + The low- and high-water marks for cache object replacement. + Replacement begins when the swap (disk) usage is above the + low-water mark and attempts to maintain utilization near the + low-water mark. As swap utilization gets close to high-water + mark object eviction becomes more aggressive. If utilization is + close to the low-water mark less replacement is done each time. + + Defaults are 90% and 95%. If you have a large cache, 5% could be + hundreds of MB. If this is the case you may wish to set these + numbers closer together. +DOC_END + +NAME: maximum_object_size +COMMENT: (bytes) +TYPE: b_size_t +DEFAULT: 4096 KB +LOC: Config.Store.maxObjectSize +DOC_START + Objects larger than this size will NOT be saved on disk. The + value is specified in kilobytes, and the default is 4MB. If + you wish to get a high BYTES hit ratio, you should probably + increase this (one 32 MB object hit counts for 3200 10KB + hits). If you wish to increase speed more than your want to + save bandwidth you should leave this low. + + NOTE: if using the LFUDA replacement policy you should increase + this value to maximize the byte hit rate improvement of LFUDA! + See replacement_policy below for a discussion of this policy. +DOC_END + +NAME: minimum_object_size +COMMENT: (bytes) +TYPE: b_size_t +DEFAULT: 0 KB +LOC: Config.Store.minObjectSize +DOC_START + Objects smaller than this size will NOT be saved on disk. The + value is specified in kilobytes, and the default is 0 KB, which + means there is no minimum. +DOC_END + +NAME: maximum_object_size_in_memory +COMMENT: (bytes) +TYPE: b_size_t +DEFAULT: 8 KB +LOC: Config.Store.maxInMemObjSize +DOC_START + Objects greater than this size will not be attempted to kept in + the memory cache. This should be set high enough to keep objects + accessed frequently in memory to improve performance whilst low + enough to keep larger objects from hoarding cache_mem. +DOC_END + +NAME: ipcache_size +COMMENT: (number of entries) +TYPE: int +DEFAULT: 1024 +LOC: Config.ipcache.size +DOC_NONE + +NAME: ipcache_low +COMMENT: (percent) +TYPE: int +DEFAULT: 90 +LOC: Config.ipcache.low +DOC_NONE + +NAME: ipcache_high +COMMENT: (percent) +TYPE: int +DEFAULT: 95 +LOC: Config.ipcache.high +DOC_START + The size, low-, and high-water marks for the IP cache. +DOC_END + +NAME: fqdncache_size +COMMENT: (number of entries) +TYPE: int +DEFAULT: 1024 +LOC: Config.fqdncache.size +DOC_START + Maximum number of FQDN cache entries. +DOC_END + +NAME: cache_replacement_policy +TYPE: removalpolicy +LOC: Config.replPolicy +DEFAULT: lru +DOC_START + The cache replacement policy parameter determines which + objects are evicted (replaced) when disk space is needed. + + lru : Squid's original list based LRU policy + heap GDSF : Greedy-Dual Size Frequency + heap LFUDA: Least Frequently Used with Dynamic Aging + heap LRU : LRU policy implemented using a heap + + Applies to any cache_dir lines listed below this. + + The LRU policies keeps recently referenced objects. + + The heap GDSF policy optimizes object hit rate by keeping smaller + popular objects in cache so it has a better chance of getting a + hit. It achieves a lower byte hit rate than LFUDA though since + it evicts larger (possibly popular) objects. + + The heap LFUDA policy keeps popular objects in cache regardless of + their size and thus optimizes byte hit rate at the expense of + hit rate since one large, popular object will prevent many + smaller, slightly less popular objects from being cached. + + Both policies utilize a dynamic aging mechanism that prevents + cache pollution that can otherwise occur with frequency-based + replacement policies. + + NOTE: if using the LFUDA replacement policy you should increase + the value of maximum_object_size above its default of 4096 KB to + to maximize the potential byte hit rate improvement of LFUDA. + + For more information about the GDSF and LFUDA cache replacement + policies see http://www.hpl.hp.com/techreports/1999/HPL-1999-69.html + and http://fog.hpl.external.hp.com/techreports/98/HPL-98-173.html. +DOC_END + +NAME: memory_replacement_policy +TYPE: removalpolicy +LOC: Config.memPolicy +DEFAULT: lru +DOC_START + The memory replacement policy parameter determines which + objects are purged from memory when memory space is needed. + + See cache_replacement_policy for details. +DOC_END + + +COMMENT_START + LOGFILE PATHNAMES AND CACHE DIRECTORIES + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: cache_dir +TYPE: cachedir +DEFAULT: none +DEFAULT_IF_NONE: ufs @DEFAULT_SWAP_DIR@ 100 16 256 +LOC: Config.cacheSwap +DOC_START + Usage: + + cache_dir Type Directory-Name Fs-specific-data [options] + + You can specify multiple cache_dir lines to spread the + cache among different disk partitions. + + Type specifies the kind of storage system to use. Only "ufs" + is built by default. To enable any of the other storage systems + see the --enable-storeio configure option. + + 'Directory' is a top-level directory where cache swap + files will be stored. If you want to use an entire disk + for caching, this can be the mount-point directory. + The directory must exist and be writable by the Squid + process. Squid will NOT create this directory for you. + Only using COSS, a raw disk device or a stripe file can + be specified, but the configuration of the "cache_wap_log" + tag is mandatory. + + The ufs store type: + + "ufs" is the old well-known Squid storage format that has always + been there. + + cache_dir ufs Directory-Name Mbytes L1 L2 [options] + + 'Mbytes' is the amount of disk space (MB) to use under this + directory. The default is 100 MB. Change this to suit your + configuration. Do NOT put the size of your disk drive here. + Instead, if you want Squid to use the entire disk drive, + subtract 20% and use that value. + + 'Level-1' is the number of first-level subdirectories which + will be created under the 'Directory'. The default is 16. + + 'Level-2' is the number of second-level subdirectories which + will be created under each first-level directory. The default + is 256. + + The aufs store type: + + "aufs" uses the same storage format as "ufs", utilizing + POSIX-threads to avoid blocking the main Squid process on + disk-I/O. This was formerly known in Squid as async-io. + + cache_dir aufs Directory-Name Mbytes L1 L2 [options] + + see argument descriptions under ufs above + + The diskd store type: + + "diskd" uses the same storage format as "ufs", utilizing a + separate process to avoid blocking the main Squid process on + disk-I/O. + + cache_dir diskd Directory-Name Mbytes L1 L2 [options] [Q1=n] [Q2=n] + + see argument descriptions under ufs above + + Q1 specifies the number of unacknowledged I/O requests when Squid + stops opening new files. If this many messages are in the queues, + Squid won't open new files. Default is 64 + + Q2 specifies the number of unacknowledged messages when Squid + starts blocking. If this many messages are in the queues, + Squid blocks until it receives some replies. Default is 72 + + When Q1 < Q2 (the default), the cache directory is optimized + for lower response time at the expense of a decrease in hit + ratio. If Q1 > Q2, the cache directory is optimized for + higher hit ratio at the expense of an increase in response + time. + + The COSS store type: + + block-size=n defines the "block size" for COSS cache_dir's. + Squid uses file numbers as block numbers. Since file numbers + are limited to 24 bits, the block size determines the maximum + size of the COSS partition. The default is 512 bytes, which + leads to a maximum cache_dir size of 512<<24, or 8 GB. Note + you should not change the COSS block size after Squid + has written some objects to the cache_dir. + + overwrite-percent=n defines the percentage of disk that COSS + must write to before a given object will be moved to the + current stripe. A value of "n" closer to 100 will cause COSS + to waste less disk space by having multiple copies of an object + on disk, but will increase the chances of overwriting a popular + object as COSS overwrites stripes. A value of "n" close to 0 + will cause COSS to keep all current objects in the current COSS + stripe at the expense of the hit rate. The default value of 50 + will allow any given object to be stored on disk a maximum of + 2 times. + + max-stripe-waste=n defines the maximum amount of space that COSS + will waste in a given stripe (in bytes). When COSS writes data + to disk, it will potentially waste up to "max-size" worth of disk + space for each 1MB of data written. If "max-size" is set to a + large value (ie >256k), this could potentially result in large + amounts of wasted disk space. Setting this value to a lower value + (ie 64k or 32k) will result in a COSS disk refusing to cache + larger objects until the COSS stripe has been filled to within + "max-stripe-waste" of the maximum size (1MB). + + membufs=n defines the number of "memory-only" stripes that COSS + will use. When an cache hit is performed on a COSS stripe before + COSS has reached the overwrite-percent value for that object, + COSS will use a series of memory buffers to hold the object in + while the data is sent to the client. This will define the maximum + number of memory-only buffers that COSS will use. The default value + is 10, which will use a maximum of 10MB of memory for buffers. + + maxfullbufs=n defines the maximum number of stripes a COSS partition + will have in memory waiting to be freed (either because the disk is + under load and the stripe is unwritten, or because clients are still + transferring data from objects using the memory). In order to try + and maintain a good hit rate under load, COSS will reserve the last + 2 full stripes for object hits. (ie a COSS cache_dir will reject + new objects when the number of full stripes is 2 less than maxfullbufs) + + Common options: + + read-only, this cache_dir is read only. + + max-size=n, refers to the max object size this storedir supports. + It is used to initially choose the storedir to dump the object. + Note: To make optimal use of the max-size limits you should order + the cache_dir lines with the smallest max-size value first and the + ones with no max-size specification last. + + Note that for coss, max-size must be less than COSS_MEMBUF_SZ + (hard coded at 1 MB). +DOC_END + +NAME: logformat +TYPE: logformat +LOC: Config.Log.logformats +DEFAULT: none +DOC_START + Usage: + + logformat + + Defines an access log format. + + The is a string with embedded % format codes + + % format codes all follow the same basic structure where all but + the formatcode is optional. Output strings are automatically escaped + as required according to their context and the output format + modifiers are usually not needed, but can be specified if an explicit + output format is desired. + + % ["|[|'|#] [-] [[0]width] [{argument}] formatcode + + " output in quoted string format + [ output in squid text log format as used by log_mime_hdrs + # output in URL quoted format + ' output as-is + + - left aligned + width field width. If starting with 0 then the + output is zero padded + {arg} argument such as header name etc + + Format codes: + + >a Client source IP address + >A Client FQDN + h Request header. Optional header name argument + on the format header[:[separator]element] + h + un User name + ul User login + ui User ident + us User SSL + ue User external acl + Hs HTTP status code + Ss Squid request status (TCP_MISS etc) + Sh Squid hierarchy status (DEFAULT_PARENT etc) + mt MIME content type + rm Request method (GET/POST etc) + ru Request URL + rv Request protocol version + ea Log string returned by external acl + a %Ss/%03Hs %a %Ss/%03Hs %h] [%a %ui %un [%tl] "%rm %ru HTTP/%rv" %Hs %a %ui %un [%tl] "%rm %ru HTTP/%rv" %Hs %h" "%{User-Agent}>h" %Ss:%Sh +DOC_END + +NAME: access_log cache_access_log +TYPE: access_log +LOC: Config.Log.accesslogs +DEFAULT: none +DOC_START + These files log client request activities. Has a line every HTTP or + ICP request. The format is: + access_log [ [acl acl ...]] + + Will log to the specified file using the specified format (which + must be defined in a logformat directive) those entries which match + ALL the acl's specified (which must be defined in acl clauses). + If no acl is specified, all requests will be logged to this file. + + To disable logging of a request use the filepath "none", in which case + a logformat name should not be specified. + + To log the request via syslog specify a filepath of "syslog" +NOCOMMENT_START +access_log @DEFAULT_ACCESS_LOG@ squid +NOCOMMENT_END +DOC_END + +NAME: cache_log +TYPE: string +DEFAULT: @DEFAULT_CACHE_LOG@ +LOC: Config.Log.log +DOC_START + Cache logging file. This is where general information about + your cache's behavior goes. You can increase the amount of data + logged to this file with the "debug_options" tag below. +DOC_END + + +NAME: cache_store_log +TYPE: string +DEFAULT: @DEFAULT_STORE_LOG@ +LOC: Config.Log.store +DOC_START + Logs the activities of the storage manager. Shows which + objects are ejected from the cache, and which objects are + saved and for how long. To disable, enter "none". There are + not really utilities to analyze this data, so you can safely + disable it. +DOC_END + + +NAME: cache_swap_log cache_swap_state +TYPE: string +LOC: Config.Log.swap +DEFAULT: none +DOC_START + Location for the cache "swap.state" file. This log file holds + the metadata of objects saved on disk. It is used to rebuild + the cache during startup. Normally this file resides in each + 'cache_dir' directory, but you may specify an alternate + pathname here. Note you must give a full filename, not just + a directory. Since this is the index for the whole object + list you CANNOT periodically rotate it! + + If %s can be used in the file name it will be replaced with a + a representation of the cache_dir name where each / is replaced + with '.'. This is needed to allow adding/removing cache_dir + lines when cache_swap_log is being used. + + If have more than one 'cache_dir', and %s is not used in the name + these swap logs will have names such as: + + cache_swap_log.00 + cache_swap_log.01 + cache_swap_log.02 + + The numbered extension (which is added automatically) + corresponds to the order of the 'cache_dir' lines in this + configuration file. If you change the order of the 'cache_dir' + lines in this file, these log files will NOT correspond to + the correct 'cache_dir' entry (unless you manually rename + them). We recommend you do NOT use this option. It is + better to keep these log files in each 'cache_dir' directory. +DOC_END + + +NAME: emulate_httpd_log +COMMENT: on|off +TYPE: onoff +DEFAULT: off +LOC: Config.onoff.common_log +DOC_START + The Cache can emulate the log file format which many 'httpd' + programs use. To disable/enable this emulation, set + emulate_httpd_log to 'off' or 'on'. The default + is to use the native log format since it includes useful + information Squid-specific log analyzers use. +DOC_END + +NAME: log_ip_on_direct +COMMENT: on|off +TYPE: onoff +DEFAULT: on +LOC: Config.onoff.log_ip_on_direct +DOC_START + Log the destination IP address in the hierarchy log tag when going + direct. Earlier Squid versions logged the hostname here. If you + prefer the old way set this to off. +DOC_END + +NAME: mime_table +TYPE: string +DEFAULT: @DEFAULT_MIME_TABLE@ +LOC: Config.mimeTablePathname +DOC_START + Pathname to Squid's MIME table. You shouldn't need to change + this, but the default file contains examples and formatting + information if you do. +DOC_END + + +NAME: log_mime_hdrs +COMMENT: on|off +TYPE: onoff +LOC: Config.onoff.log_mime_hdrs +DEFAULT: off +DOC_START + The Cache can record both the request and the response MIME + headers for each HTTP transaction. The headers are encoded + safely and will appear as two bracketed fields at the end of + the access log (for either the native or httpd-emulated log + formats). To enable this logging set log_mime_hdrs to 'on'. +DOC_END + + +NAME: useragent_log +TYPE: string +LOC: Config.Log.useragent +DEFAULT: none +IFDEF: USE_USERAGENT_LOG +DOC_START + Squid will write the User-Agent field from HTTP requests + to the filename specified here. By default useragent_log + is disabled. +DOC_END + + +NAME: referer_log referrer_log +TYPE: string +LOC: Config.Log.referer +DEFAULT: none +IFDEF: USE_REFERER_LOG +DOC_START + Squid will write the Referer field from HTTP requests to the + filename specified here. By default referer_log is disabled. + Note that "referer" is actually a misspelling of "referrer" + however the misspelt version has been accepted into the HTTP RFCs + and we accept both. +DOC_END + + +NAME: pid_filename +TYPE: string +DEFAULT: @DEFAULT_PID_FILE@ +LOC: Config.pidFilename +DOC_START + A filename to write the process-id to. To disable, enter "none". +DOC_END + + +NAME: debug_options +TYPE: eol +DEFAULT: ALL,1 +LOC: Config.debugOptions +DOC_START + Logging options are set as section,level where each source file + is assigned a unique section. Lower levels result in less + output, Full debugging (level 9) can result in a very large + log file, so be careful. The magic word "ALL" sets debugging + levels for all sections. We recommend normally running with + "ALL,1". +DOC_END + + +NAME: log_fqdn +COMMENT: on|off +TYPE: onoff +DEFAULT: off +LOC: Config.onoff.log_fqdn +DOC_START + Turn this on if you wish to log fully qualified domain names + in the access.log. To do this Squid does a DNS lookup of all + IP's connecting to it. This can (in some situations) increase + latency, which makes your cache seem slower for interactive + browsing. +DOC_END + + +NAME: client_netmask +TYPE: address +LOC: Config.Addrs.client_netmask +DEFAULT: 255.255.255.255 +DOC_START + A netmask for client addresses in logfiles and cachemgr output. + Change this to protect the privacy of your cache clients. + A netmask of 255.255.255.0 will log all IP's in that range with + the last digit set to '0'. +DOC_END + + +COMMENT_START + OPTIONS FOR EXTERNAL SUPPORT PROGRAMS + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: ftp_user +TYPE: string +DEFAULT: Squid@ +LOC: Config.Ftp.anon_user +DOC_START + If you want the anonymous login password to be more informative + (and enable the use of picky ftp servers), set this to something + reasonable for your domain, like wwwuser@somewhere.net + + The reason why this is domainless by default is the + request can be made on the behalf of a user in any domain, + depending on how the cache is used. + Some ftp server also validate the email address is valid + (for example perl.com). +DOC_END + +NAME: ftp_list_width +TYPE: int +DEFAULT: 32 +LOC: Config.Ftp.list_width +DOC_START + Sets the width of ftp listings. This should be set to fit in + the width of a standard browser. Setting this too small + can cut off long filenames when browsing ftp sites. +DOC_END + +NAME: ftp_passive +TYPE: onoff +DEFAULT: on +LOC: Config.Ftp.passive +DOC_START + If your firewall does not allow Squid to use passive + connections, turn off this option. +DOC_END + +NAME: ftp_sanitycheck +TYPE: onoff +DEFAULT: on +LOC: Config.Ftp.sanitycheck +DOC_START + For security and data integrity reasons Squid by default performs + sanity checks of the addresses of FTP data connections ensure the + data connection is to the requested server. If you need to allow + FTP connections to servers using another IP address for the data + connection turn this off. +DOC_END + +NAME: ftp_telnet_protocol +TYPE: onoff +DEFAULT: on +LOC: Config.Ftp.telnet +DOC_START + The FTP protocol is officially defined to use the telnet protocol + as transport channel for the control connection. However, many + implementations are broken and does not respect this aspect of + the FTP protocol. + + If you have trouble accessing files with ASCII code 255 in the + path or similar problems involving this ASCII code you can + try setting this directive to off. If that helps, report to the + operator of the FTP server in question that their FTP server + is broken and does not follow the FTP standard. +DOC_END + +NAME: check_hostnames +TYPE: onoff +DEFAULT: on +LOC: Config.onoff.check_hostnames +DOC_START + For security and stability reasons Squid by default checks + hostnames for Internet standard RFC compliance. If you do not want + Squid to perform these checks then turn this directive off. +DOC_END + +NAME: allow_underscore +TYPE: onoff +DEFAULT: on +LOC: Config.onoff.allow_underscore +DOC_START + Underscore characters is not strictly allowed in Internet hostnames + but nevertheless used by many sites. Set this to off if you want + Squid to be strict about the standard. +DOC_END + +NAME: cache_dns_program +TYPE: string +IFDEF: USE_DNSSERVERS +DEFAULT: @DEFAULT_DNSSERVER@ +LOC: Config.Program.dnsserver +DOC_START + Specify the location of the executable for dnslookup process. +DOC_END + +NAME: dns_children +TYPE: int +IFDEF: USE_DNSSERVERS +DEFAULT: 5 +LOC: Config.dnsChildren +DOC_START + The number of processes spawn to service DNS name lookups. + For heavily loaded caches on large servers, you should + probably increase this value to at least 10. The maximum + is 32. The default is 5. + + You must have at least one dnsserver process. +DOC_END + +NAME: dns_retransmit_interval +TYPE: time_t +DEFAULT: 5 seconds +LOC: Config.Timeout.idns_retransmit +IFDEF: !USE_DNSSERVERS +DOC_START + Initial retransmit interval for DNS queries. The interval is + doubled each time all configured DNS servers have been tried. + +DOC_END + +NAME: dns_timeout +TYPE: time_t +DEFAULT: 2 minutes +LOC: Config.Timeout.idns_query +IFDEF: !USE_DNSSERVERS +DOC_START + DNS Query timeout. If no response is received to a DNS query + within this time all DNS servers for the queried domain + are assumed to be unavailable. +DOC_END + +NAME: dns_defnames +COMMENT: on|off +TYPE: onoff +DEFAULT: off +LOC: Config.onoff.res_defnames +DOC_START + Normally the RES_DEFNAMES resolver option is disabled + (see res_init(3)). This prevents caches in a hierarchy + from interpreting single-component hostnames locally. To allow + Squid to handle single-component names, enable this option. +DOC_END + +NAME: dns_nameservers +TYPE: wordlist +DEFAULT: none +LOC: Config.dns_nameservers +DOC_START + Use this if you want to specify a list of DNS name servers + (IP addresses) to use instead of those given in your + /etc/resolv.conf file. + On Windows platforms, if no value is specified here or in + the /etc/resolv.conf file, the list of DNS name servers are + taken from the Windows registry, both static and dynamic DHCP + configurations are supported. + + Example: dns_nameservers 10.0.0.1 192.172.0.4 +DOC_END + +NAME: hosts_file +TYPE: string +DEFAULT: @DEFAULT_HOSTS@ +LOC: Config.etcHostsPath +DOC_START + Location of the host-local IP name-address associations + database. Most Operating Systems have such a file on different + default locations: + - Un*X & Linux: /etc/hosts + - Windows NT/2000: %SystemRoot%\system32\drivers\etc\hosts + (%SystemRoot% value install default is c:\winnt) + - Windows XP/2003: %SystemRoot%\system32\drivers\etc\hosts + (%SystemRoot% value install default is c:\windows) + - Windows 9x/Me: %windir%\hosts + (%windir% value is usually c:\windows) + - Cygwin: /etc/hosts + + The file contains newline-separated definitions, in the + form ip_address_in_dotted_form name [name ...] names are + whitespace-separated. Lines beginning with an hash (#) + character are comments. + + The file is checked at startup and upon configuration. + If set to 'none', it won't be checked. + If append_domain is used, that domain will be added to + domain-local (i.e. not containing any dot character) host + definitions. +DOC_END + +NAME: diskd_program +TYPE: string +DEFAULT: @DEFAULT_DISKD@ +LOC: Config.Program.diskd +DOC_START + Specify the location of the diskd executable. + Note that this is only useful if you have compiled in + diskd as one of the store io modules. +DOC_END + +NAME: unlinkd_program +IFDEF: USE_UNLINKD +TYPE: string +DEFAULT: @DEFAULT_UNLINKD@ +LOC: Config.Program.unlinkd +DOC_START + Specify the location of the executable for file deletion process. +DOC_END + +NAME: pinger_program +TYPE: string +DEFAULT: @DEFAULT_PINGER@ +LOC: Config.Program.pinger +IFDEF: USE_ICMP +DOC_START + Specify the location of the executable for the pinger process. +DOC_END + + +NAME: url_rewrite_program redirect_program +TYPE: programline +LOC: Config.Program.url_rewrite.command +DEFAULT: none +DOC_START + Specify the location of the executable for the URL rewriter. + Since they can perform almost any function there isn't one included. + + For each requested URL rewriter will receive on line with the format + + URL client_ip "/" fqdn user method urlgroup + + And the rewriter may return a rewritten URL. The other components of + the request line does not need to be returned (ignored if they are). + + The rewriter can also indicate that a client-side redirect should + be performed to the new URL. This is done by prefixing the returned + URL with "301:" (moved permanently) or 302: (moved temporarily). + + It can also return a "urlgroup" that can subsequently be matched + in cache_peer_access and similar ACL driven rules. An urlgroup is + returned by prefixing the returned url with "!urlgroup!" + + By default, a URL rewriter is not used. +DOC_END + +NAME: url_rewrite_children redirect_children +TYPE: int +DEFAULT: 5 +LOC: Config.Program.url_rewrite.children +DOC_START + The number of redirector processes to spawn. If you start + too few Squid will have to wait for them to process a backlog of + URLs, slowing it down. If you start too many they will use RAM + and other system resources. +DOC_END + +NAME: url_rewrite_concurrency redirect_concurrency +TYPE: int +DEFAULT: 0 +LOC: Config.Program.url_rewrite.concurrency +DOC_START + The number of requests each redirector helper can handle in + parallel. Defaults to 0 which indicates that the redirector + is a old-style singlethreaded redirector. +DOC_END + +NAME: url_rewrite_host_header redirect_rewrites_host_header +TYPE: onoff +DEFAULT: on +LOC: Config.onoff.redir_rewrites_host +DOC_START + By default Squid rewrites any Host: header in redirected + requests. If you are running an accelerator this may + not be a wanted effect of a redirector. + + WARNING: Entries are cached on the result of the URL rewriting + process, so be careful if you have domain-virtual hosts. +DOC_END + +NAME: url_rewrite_access redirector_access +TYPE: acl_access +DEFAULT: none +LOC: Config.accessList.url_rewrite +DOC_START + If defined, this access list specifies which requests are + sent to the redirector processes. By default all requests + are sent. +DOC_END + +NAME: location_rewrite_program +TYPE: programline +LOC: Config.Program.location_rewrite.command +DEFAULT: none +DOC_START + Specify the location of the executable for the Location rewriter, + used to rewrite server generated redirects. Usually used in + conjunction with a url_rewrite_program + + For each Location header received the location rewriter will receive + one line with the format: + + location URL requested URL urlgroup + + And the rewriter may return a rewritten Location URL or a blank line. + The other components of the request line does not need to be returned + (ignored if they are). + + By default, a Location rewriter is not used. +DOC_END + +NAME: location_rewrite_children +TYPE: int +DEFAULT: 5 +LOC: Config.Program.location_rewrite.children +DOC_START + The number of location rewriting processes to spawn. If you start + too few Squid will have to wait for them to process a backlog of + URLs, slowing it down. If you start too many they will use RAM + and other system resources. +DOC_END + +NAME: location_rewrite_concurrency +TYPE: int +DEFAULT: 0 +LOC: Config.Program.location_rewrite.concurrency +DOC_START + The number of requests each Location rewriter helper can handle in + parallel. Defaults to 0 which indicates that the helper + is a old-style singlethreaded helper. +DOC_END + +NAME: location_rewrite_access +TYPE: acl_access +DEFAULT: none +LOC: Config.accessList.location_rewrite +DOC_START + If defined, this access list specifies which requests are + sent to the location rewriting processes. By default all Location + headers are sent. +DOC_END + + +NAME: auth_param +TYPE: authparam +LOC: Config.authConfig +DEFAULT: none +DOC_START + This is used to define parameters for the various authentication + schemes supported by Squid. + + format: auth_param scheme parameter [setting] + + The order in which authentication schemes are presented to the client is + dependent on the order the scheme first appears in config file. IE + has a bug (it's not RFC 2617 compliant) in that it will use the basic + scheme if basic is the first entry presented, even if more secure + schemes are presented. For now use the order in the recommended + settings section below. If other browsers have difficulties (don't + recognize the schemes offered even if you are using basic) either + put basic first, or disable the other schemes (by commenting out their + program entry). + + Once an authentication scheme is fully configured, it can only be + shutdown by shutting squid down and restarting. Changes can be made on + the fly and activated with a reconfigure. I.E. You can change to a + different helper, but not unconfigure the helper completely. + + Please note that while this directive defines how Squid processes + authentication it does not automatically activate authentication. + To use authentication you must in addition make use of ACLs based + on login name in http_access (proxy_auth, proxy_auth_regex or + external with %LOGIN used in the format tag). The browser will be + challenged for authentication on the first such acl encountered + in http_access processing and will also be re-challenged for new + login credentials if the request is being denied by a proxy_auth + type acl. + + WARNING: authentication can't be used in a transparently intercepting + proxy as the client then thinks it is talking to an origin server and + not the proxy. This is a limitation of bending the TCP/IP protocol to + transparently intercepting port 80, not a limitation in Squid. + + === Parameters for the basic scheme follow. === + + "program" cmdline + Specify the command for the external authenticator. Such a program + reads a line containing "username password" and replies "OK" or + "ERR" in an endless loop. "ERR" responses may optionally be followed + by a error description available as %m in the returned error page. + + By default, the basic authentication scheme is not used unless a + program is specified. + + If you want to use the traditional proxy authentication, jump over to + the helpers/basic_auth/NCSA directory and type: + % make + % make install + + Then, set this line to something like + + auth_param basic program @DEFAULT_PREFIX@/libexec/ncsa_auth @DEFAULT_PREFIX@/etc/passwd + + "children" numberofchildren + The number of authenticator processes to spawn. If you start too few + squid will have to wait for them to process a backlog of credential + verifications, slowing it down. When credential verifications are + done via a (slow) network you are likely to need lots of + authenticator processes. + auth_param basic children 5 + + "concurrency" numberofconcurrentrequests + The number of concurrent requests/channels the helper supports. + Changes the protocol used to include a channel number first on + the request/response line, allowing multiple requests to be sent + to the same helper in parallell without wating for the response. + Must not be set unless it's known the helper supports this. + + "realm" realmstring + Specifies the realm name which is to be reported to the client for + the basic proxy authentication scheme (part of the text the user + will see when prompted their username and password). + auth_param basic realm Squid proxy-caching web server + + "credentialsttl" timetolive + Specifies how long squid assumes an externally validated + username:password pair is valid for - in other words how often the + helper program is called for that user. Set this low to force + revalidation with short lived passwords. Note that setting this high + does not impact your susceptibility to replay attacks unless you are + using an one-time password system (such as SecureID). If you are using + such a system, you will be vulnerable to replay attacks unless you + also use the max_user_ip ACL in an http_access rule. + auth_param basic credentialsttl 2 hours + + "casesensitive" on|off + Specifies if usernames are case sensitive. Most user databases are + case insensitive allowing the same username to be spelled using both + lower and upper case letters, but some are case sensitive. This + makes a big difference for user_max_ip ACL processing and similar. + auth_param basic casesensitive off + + "blankpassword" on|off + Specifies if blank passwords should be supported. Defaults to off + as there is multiple authentication backends which handles blank + passwords as "guest" access. + + === Parameters for the digest scheme follow === + + "program" cmdline + Specify the command for the external authenticator. Such a program + reads a line containing "username":"realm" and replies with the + appropriate H(A1) value base64 encoded or ERR if the user (or his H(A1) + hash) does not exists. See RFC 2616 for the definition of H(A1). + "ERR" responses may optionally be followed by a error description + available as %m in the returned error page. + + By default, the digest authentication scheme is not used unless a + program is specified. + + If you want to use a digest authenticator, jump over to the + helpers/digest_auth/ directory and choose the authenticator to use. + It it's directory type + % make + % make install + + Then, set this line to something like + + auth_param digest program @DEFAULT_PREFIX@/libexec/digest_auth_pw @DEFAULT_PREFIX@/etc/digpass + + + "children" numberofchildren + The number of authenticator processes to spawn. If you start too few + squid will have to wait for them to process a backlog of credential + verifications, slowing it down. When credential verifications are + done via a (slow) network you are likely to need lots of + authenticator processes. + auth_param digest children 5 + + "concurrency" numberofconcurrentrequests + The number of concurrent requests/channels the helper supports. + Changes the protocol used to include a channel number first on + the request/response line, allowing multiple requests to be sent + to the same helper in parallell without wating for the response. + Must not be set unless it's known the helper supports this. + + "realm" realmstring + Specifies the realm name which is to be reported to the client for the + digest proxy authentication scheme (part of the text the user will see + when prompted their username and password). + auth_param digest realm Squid proxy-caching web server + + "nonce_garbage_interval" timeinterval + Specifies the interval that nonces that have been issued to clients are + checked for validity. + auth_param digest nonce_garbage_interval 5 minutes + + "nonce_max_duration" timeinterval + Specifies the maximum length of time a given nonce will be valid for. + auth_param digest nonce_max_duration 30 minutes + + "nonce_max_count" number + Specifies the maximum number of times a given nonce can be used. + auth_param digest nonce_max_count 50 + + "nonce_strictness" on|off + Determines if squid requires strict increment-by-1 behavior for nonce + counts, or just incrementing (off - for use when useragents generate + nonce counts that occasionally miss 1 (ie, 1,2,4,6)). + auth_param digest nonce_strictness off + + "check_nonce_count" on|off + This directive if set to off can disable the nonce count check + completely to work around buggy digest qop implementations in certain + mainstream browser versions. Default on to check the nonce count to + protect from authentication replay attacks. + auth_param digest check_nonce_count on + + "post_workaround" on|off + This is a workaround to certain buggy browsers who sends an incorrect + request digest in POST requests when reusing the same nonce as acquired + earlier in response to a GET request. + auth_param digest post_workaround off + + === NTLM scheme options follow === + + "program" cmdline + Specify the command for the external NTLM authenticator. Such a + program participates in the NTLMSSP exchanges between Squid and the + client and reads commands according to the Squid NTLMSSP helper + protocol. See helpers/ntlm_auth/ for details. Recommended ntlm + authenticator is ntlm_auth from Samba-3.X, but a number of other + ntlm authenticators is available. + + By default, the ntlm authentication scheme is not used unless a + program is specified. + + auth_param ntlm program /path/to/samba/bin/ntlm_auth --helper-protocol=squid-2.5-ntlmssp + + "children" numberofchildren + The number of authenticator processes to spawn. If you start too few + squid will have to wait for them to process a backlog of credential + verifications, slowing it down. When credential verifications are + done via a (slow) network you are likely to need lots of + authenticator processes. + auth_param ntlm children 5 + + "keep_alive" on|off + This option enables the use of keep-alive on the initial + authentication request. It has been reported some versions of MSIE + have problems if this is enabled, but performance will be increased + if enabled. + + auth_param ntlm keep_alive on + + === Negotiate scheme options follow === + + "program" cmdline + Specify the command for the external Negotiate authenticator. Such a + program participates in the SPNEGO exchanges between Squid and the + client and reads commands according to the Squid ntlmssp helper + protocol. See helpers/ntlm_auth/ for details. Recommended SPNEGO + authenticator is ntlm_auth from Samba-4.X. + + By default, the Negotiate authentication scheme is not used unless a + program is specified. + + auth_param negotiate program /path/to/samba/bin/ntlm_auth --helper-protocol=gss-spnego + + "children" numberofchildren + The number of authenticator processes to spawn. If you start too few + squid will have to wait for them to process a backlog of credential + verifications, slowing it down. When credential verifications are + done via a (slow) network you are likely to need lots of + authenticator processes. + auth_param negotiate children 5 + + "keep_alive" on|off + If you experience problems with PUT/POST requests when using the + Negotiate authentication scheme then you can try setting this to + off. This will cause Squid to forcibly close the connection on + the initial requests where the browser asks which schemes are + supported by the proxy. + + auth_param negotiate keep_alive on + +NOCOMMENT_START +#Recommended minimum configuration per scheme: +#auth_param negotiate program +#auth_param negotiate children 5 +#auth_param negotiate keep_alive on +#auth_param ntlm program +#auth_param ntlm children 5 +#auth_param ntlm keep_alive on +#auth_param digest program +#auth_param digest children 5 +#auth_param digest realm Squid proxy-caching web server +#auth_param digest nonce_garbage_interval 5 minutes +#auth_param digest nonce_max_duration 30 minutes +#auth_param digest nonce_max_count 50 +#auth_param basic program +#auth_param basic children 5 +#auth_param basic realm Squid proxy-caching web server +#auth_param basic credentialsttl 2 hours +#auth_param basic casesensitive off +NOCOMMENT_END +DOC_END + +NAME: authenticate_cache_garbage_interval +TYPE: time_t +DEFAULT: 1 hour +LOC: Config.authenticateGCInterval +DOC_START + The time period between garbage collection across the username cache. + This is a tradeoff between memory utilization (long intervals - say + 2 days) and CPU (short intervals - say 1 minute). Only change if you + have good reason to. +DOC_END + +NAME: authenticate_ttl +TYPE: time_t +DEFAULT: 1 hour +LOC: Config.authenticateTTL +DOC_START + The time a user & their credentials stay in the logged in user cache + since their last request. When the garbage interval passes, all user + credentials that have passed their TTL are removed from memory. +DOC_END + +NAME: authenticate_ip_ttl +TYPE: time_t +LOC: Config.authenticateIpTTL +DEFAULT: 0 seconds +DOC_START + If you use proxy authentication and the 'max_user_ip' ACL, this + directive controls how long Squid remembers the IP addresses + associated with each user. Use a small value (e.g., 60 seconds) if + your users might change addresses quickly, as is the case with + dialups. You might be safe using a larger value (e.g., 2 hours) in a + corporate LAN environment with relatively static address assignments. +DOC_END + +NAME: external_acl_type +TYPE: externalAclHelper +LOC: Config.externalAclHelperList +DEFAULT: none +DOC_START + This option defines external acl classes using a helper program to + look up the status + + external_acl_type name [options] FORMAT.. /path/to/helper [helper arguments..] + + Options: + + ttl=n TTL in seconds for cached results (defaults to 3600 + for 1 hour) + negative_ttl=n + TTL for cached negative lookups (default same + as ttl) + children=n number of processes spawn to service external acl + lookups of this type. + concurrency=n concurrency level per process. Use 0 for simple helpers + who can only process a single request at a time. + Note: see compatibility note below + cache=n result cache size, 0 is unbounded (default) + grace= Percentage remaining of TTL where a refresh of a + cached entry should be initiated without needing to + wait for a new reply. (default 0 for no grace period) + protocol=2.5 Compatibility mode for Squid-2.5 external acl helpers + + FORMAT specifications + + %LOGIN Authenticated user login name + %IDENT Ident user name + %SRC Client IP + %SRCPORT Client source port + %DST Requested host + %PROTO Requested protocol + %PORT Requested port + %METHOD Request method + %MYADDR Squid interface address + %MYPORT Squid http_port number + %PATH Requested URL-path (including query-string if any) + %USER_CERT SSL User certificate in PEM format + %USER_CERTCHAIN SSL User certificate chain in PEM format + %USER_CERT_xx SSL User certificate subject attribute xx + %USER_CA_xx SSL User certificate issuer attribute xx + %{Header} HTTP request header + %{Hdr:member} HTTP request header list member + %{Hdr:;member} + HTTP request header list member using ; as + list separator. ; can be any non-alphanumeric + character. + %ACL The ACL name + %DATA The ACL arguments. If not used then any arguments + is automatically added at the end + + In addition, any string specified in the referencing acl will + also be included in the helper request line, after the specified + formats (see the "acl external" directive) + + The helper receives lines per the above format specification, + and returns lines starting with OK or ERR indicating the validity + of the request and optionally followed by additional keywords with + more details. + + General result syntax: + + OK/ERR keyword=value ... + + Defined keywords: + + user= The users name (login also understood) + password= The users password (for PROXYPASS login= cache_peer) + message= Error message or similar used as %o in error messages + (error also understood) + log= String to be logged in access.log. Available as + %ea in logformat specifications + + Keyword values need to be enclosed in quotes if they may contain + whitespace, or the whitespace escaped using \. Any quotes or \ + characters within the keyword value must be \ escaped. + + If protocol=3.0 then URL escaping of the strings is used instead + of the above described quoting format. + + Compatibility Note: The children= option was named concurrency= in + Squid-2.5.STABLE3 and earlier, and was accepted as an alias for the + duration of the Squid-2.5 releases to keep compatibility. However, + the meaning of concurrency= option has changed in Squid-2.6 to match + that of Squid-3 and the old syntax no longer works. +DOC_END + +COMMENT_START + OPTIONS FOR TUNING THE CACHE + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: wais_relay_host +TYPE: string +DEFAULT: none +LOC: Config.Wais.relayHost +DOC_NONE + +NAME: wais_relay_port +TYPE: ushort +DEFAULT: 0 +LOC: Config.Wais.relayPort +DOC_START + Relay WAIS request to host (1st arg) at port (2 arg). +DOC_END + + +NAME: request_header_max_size +COMMENT: (KB) +TYPE: b_size_t +DEFAULT: 20 KB +LOC: Config.maxRequestHeaderSize +DOC_START + This specifies the maximum size for HTTP headers in a request. + Request headers are usually relatively small (about 512 bytes). + Placing a limit on the request header size will catch certain + bugs (for example with persistent connections) and possibly + buffer-overflow or denial-of-service attacks. +DOC_END + +NAME: request_body_max_size +COMMENT: (KB) +TYPE: b_size_t +DEFAULT: 0 KB +LOC: Config.maxRequestBodySize +DOC_START + This specifies the maximum size for an HTTP request body. + In other words, the maximum size of a PUT/POST request. + A user who attempts to send a request with a body larger + than this limit receives an "Invalid Request" error message. + If you set this parameter to a zero (the default), there will + be no limit imposed. +DOC_END + +NAME: refresh_pattern +TYPE: refreshpattern +LOC: Config.Refresh +DEFAULT: none +DOC_START + usage: refresh_pattern [-i] regex min percent max [options] + + By default, regular expressions are CASE-SENSITIVE. To make + them case-insensitive, use the -i option. + + 'Min' is the time (in minutes) an object without an explicit + expiry time should be considered fresh. The recommended + value is 0, any higher values may cause dynamic applications + to be erroneously cached unless the application designer + has taken the appropriate actions. + + 'Percent' is a percentage of the objects age (time since last + modification age) an object without explicit expiry time + will be considered fresh. + + 'Max' is an upper limit on how long objects without an explicit + expiry time will be considered fresh. + + options: override-expire + override-lastmod + reload-into-ims + ignore-reload + ignore-no-cache + ignore-private + ignore-auth + + override-expire enforces min age even if the server + sent a Expires: header. Doing this VIOLATES the HTTP + standard. Enabling this feature could make you liable + for problems which it causes. + + override-lastmod enforces min age even on objects + that were modified recently. + + reload-into-ims changes client no-cache or ``reload'' + to If-Modified-Since requests. Doing this VIOLATES the + HTTP standard. Enabling this feature could make you + liable for problems which it causes. + + ignore-reload ignores a client no-cache or ``reload'' + header. Doing this VIOLATES the HTTP standard. Enabling + this feature could make you liable for problems which + it causes. + + ignore-no-cache ignores any ``Pragma: no-cache'' and + ``Cache-control: no-cache'' headers received from a server. + The HTTP RFC never allows the use of this (Pragma) header + from a server, only a client, though plenty of servers + send it anyway. + + ignore-private ignores any ``Cache-control: private'' + headers received from a server. Doing this VIOLATES + the HTTP standard. Enabling this feature could make you + liable for problems which it causes. + + ignore-auth caches responses to requests with authorization, + irrespective of ``Cache-control'' headers received from + a server. Doing this VIOLATES the HTTP standard. Enabling + this feature could make you liable for problems which + it causes. + + Basically a cached object is: + + FRESH if expires < now, else STALE + STALE if age > max + FRESH if lm-factor < percent, else STALE + FRESH if age < min + else STALE + + The refresh_pattern lines are checked in the order listed here. + The first entry which matches is used. If none of the entries + match the default will be used. + + Note, you must uncomment all the default lines if you want + to change one. The default setting is only active if none is + used. + +Suggested default: +NOCOMMENT_START +refresh_pattern ^ftp: 1440 20% 10080 +refresh_pattern ^gopher: 1440 0% 1440 +refresh_pattern . 0 20% 4320 +NOCOMMENT_END +DOC_END + +NAME: quick_abort_min +COMMENT: (KB) +TYPE: kb_size_t +DEFAULT: 16 KB +LOC: Config.quickAbort.min +DOC_NONE + +NAME: quick_abort_max +COMMENT: (KB) +TYPE: kb_size_t +DEFAULT: 16 KB +LOC: Config.quickAbort.max +DOC_NONE + +NAME: quick_abort_pct +COMMENT: (percent) +TYPE: int +DEFAULT: 95 +LOC: Config.quickAbort.pct +DOC_START + The cache by default continues downloading aborted requests + which are almost completed (less than 16 KB remaining). This + may be undesirable on slow (e.g. SLIP) links and/or very busy + caches. Impatient users may tie up file descriptors and + bandwidth by repeatedly requesting and immediately aborting + downloads. + + When the user aborts a request, Squid will check the + quick_abort values to the amount of data transfered until + then. + + If the transfer has less than 'quick_abort_min' KB remaining, + it will finish the retrieval. + + If the transfer has more than 'quick_abort_max' KB remaining, + it will abort the retrieval. + + If more than 'quick_abort_pct' of the transfer has completed, + it will finish the retrieval. + + If you do not want any retrieval to continue after the client + has aborted, set both 'quick_abort_min' and 'quick_abort_max' + to '0 KB'. + + If you want retrievals to always continue if they are being + cached set 'quick_abort_min' to '-1 KB'. +DOC_END + +NAME: read_ahead_gap +COMMENT: buffer-size +TYPE: b_size_t +LOC: Config.readAheadGap +DEFAULT: 16 KB +DOC_START + The amount of data the cache will buffer ahead of what has been + sent to the client when retrieving an object from another server. +DOC_END + +NAME: negative_ttl +COMMENT: time-units +TYPE: time_t +LOC: Config.negativeTtl +DEFAULT: 5 minutes +DOC_START + Time-to-Live (TTL) for failed requests. Certain types of + failures (such as "connection refused" and "404 Not Found") are + negatively-cached for a configurable amount of time. The + default is 5 minutes. Note that this is different from + negative caching of DNS lookups. +DOC_END + + +NAME: positive_dns_ttl +COMMENT: time-units +TYPE: time_t +LOC: Config.positiveDnsTtl +DEFAULT: 6 hours +DOC_START + Upper limit on how long Squid will cache positive DNS responses. + Default is 6 hours (360 minutes). This directive must be set + larger than negative_dns_ttl. +DOC_END + + +NAME: negative_dns_ttl +COMMENT: time-units +TYPE: time_t +LOC: Config.negativeDnsTtl +DEFAULT: 1 minute +DOC_START + Time-to-Live (TTL) for negative caching of failed DNS lookups. + This also makes sets the lower cache limit on positive lookups. + Minimum value is 1 second, and it is not recommendable to go + much below 10 seconds. +DOC_END + +NAME: range_offset_limit +COMMENT: (bytes) +TYPE: b_size_t +LOC: Config.rangeOffsetLimit +DEFAULT: 0 KB +DOC_START + Sets a upper limit on how far into the the file a Range request + may be to cause Squid to prefetch the whole file. If beyond this + limit Squid forwards the Range request as it is and the result + is NOT cached. + + This is to stop a far ahead range request (lets say start at 17MB) + from making Squid fetch the whole object up to that point before + sending anything to the client. + + A value of -1 causes Squid to always fetch the object from the + beginning so it may cache the result. (2.0 style) + + A value of 0 causes Squid to never fetch more than the + client requested. (default) +DOC_END + +NAME: collapsed_forwarding +COMMENT: (on|off) +TYPE: onoff +LOC: Config.onoff.collapsed_forwarding +DEFAULT: off +DOC_START + This option enables multiple requests for the same URI to be + processed as one request. Normally disabled to avoid increased + latency on dynamic content, but there can be benefit from enabling + this in accelerator setups where the web servers are the bottleneck + and reliable and returns mostly cacheable information. +DOC_END + +NAME: refresh_stale_hit +COMMENT: (time) +TYPE: time_t +DEFAULT: 0 seconds +LOC: Config.refresh_stale_window +DOC_START + This option changes the refresh algorithm to allow concurrent + requests while an object is being refreshed to be processed as + cache hits if the object expired less than X seconds ago. Default + is 0 to disable this feature. This option is mostly interesting + in accelerator setups where a few objects is accessed very + frequently. +DOC_END + +COMMENT_START + TIMEOUTS + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: forward_timeout +COMMENT: time-units +TYPE: time_t +LOC: Config.Timeout.forward +DEFAULT: 4 minutes +DOC_START + This parameter specifies how long Squid should at most attempt in + finding a forwarding path for the request before giving up. +DOC_END + +NAME: connect_timeout +COMMENT: time-units +TYPE: time_t +LOC: Config.Timeout.connect +DEFAULT: 1 minute +DOC_START + This parameter specifies how long to wait for the TCP connect to + the requested server or peer to complete before Squid should + attempt to find another path where to forward the request. +DOC_END + +NAME: peer_connect_timeout +COMMENT: time-units +TYPE: time_t +LOC: Config.Timeout.peer_connect +DEFAULT: 30 seconds +DOC_START + This parameter specifies how long to wait for a pending TCP + connection to a peer cache. The default is 30 seconds. You + may also set different timeout values for individual neighbors + with the 'connect-timeout' option on a 'cache_peer' line. +DOC_END + +NAME: read_timeout +COMMENT: time-units +TYPE: time_t +LOC: Config.Timeout.read +DEFAULT: 15 minutes +DOC_START + The read_timeout is applied on server-side connections. After + each successful read(), the timeout will be extended by this + amount. If no data is read again after this amount of time, + the request is aborted and logged with ERR_READ_TIMEOUT. The + default is 15 minutes. +DOC_END + + +NAME: request_timeout +TYPE: time_t +LOC: Config.Timeout.request +DEFAULT: 5 minutes +DOC_START + How long to wait for an HTTP request after initial + connection establishment. +DOC_END + + +NAME: persistent_request_timeout +TYPE: time_t +LOC: Config.Timeout.persistent_request +DEFAULT: 1 minute +DOC_START + How long to wait for the next HTTP request on a persistent + connection after the previous request completes. +DOC_END + + +NAME: client_lifetime +COMMENT: time-units +TYPE: time_t +LOC: Config.Timeout.lifetime +DEFAULT: 1 day +DOC_START + The maximum amount of time a client (browser) is allowed to + remain connected to the cache process. This protects the Cache + from having a lot of sockets (and hence file descriptors) tied up + in a CLOSE_WAIT state from remote clients that go away without + properly shutting down (either because of a network failure or + because of a poor client implementation). The default is one + day, 1440 minutes. + + NOTE: The default value is intended to be much larger than any + client would ever need to be connected to your cache. You + should probably change client_lifetime only as a last resort. + If you seem to have many client connections tying up + filedescriptors, we recommend first tuning the read_timeout, + request_timeout, persistent_request_timeout and quick_abort values. +DOC_END + +NAME: half_closed_clients +TYPE: onoff +LOC: Config.onoff.half_closed_clients +DEFAULT: on +DOC_START + Some clients may shutdown the sending side of their TCP + connections, while leaving their receiving sides open. Sometimes, + Squid can not tell the difference between a half-closed and a + fully-closed TCP connection. By default, half-closed client + connections are kept open until a read(2) or write(2) on the + socket returns an error. Change this option to 'off' and Squid + will immediately close client connections when read(2) returns + "no more data to read." +DOC_END + +NAME: pconn_timeout +TYPE: time_t +LOC: Config.Timeout.pconn +DEFAULT: 120 seconds +DOC_START + Timeout for idle persistent connections to servers and other + proxies. +DOC_END + +NAME: ident_timeout +TYPE: time_t +IFDEF: USE_IDENT +LOC: Config.Timeout.ident +DEFAULT: 10 seconds +DOC_START + Maximum time to wait for IDENT lookups to complete. + + If this is too high, and you enabled IDENT lookups from untrusted + users, you might be susceptible to denial-of-service by having + many ident requests going at once. +DOC_END + + +NAME: shutdown_lifetime +COMMENT: time-units +TYPE: time_t +LOC: Config.shutdownLifetime +DEFAULT: 30 seconds +DOC_START + When SIGTERM or SIGHUP is received, the cache is put into + "shutdown pending" mode until all active sockets are closed. + This value is the lifetime to set for all open descriptors + during shutdown mode. Any active clients after this many + seconds will receive a 'timeout' message. +DOC_END + +COMMENT_START + ACCESS CONTROLS + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: acl +TYPE: acl +LOC: Config.aclList +DEFAULT: none +DOC_START + Defining an Access List + + acl aclname acltype string1 ... + acl aclname acltype "file" ... + + when using "file", the file should contain one item per line + + acltype is one of the types described below + + By default, regular expressions are CASE-SENSITIVE. To make + them case-insensitive, use the -i option. + + acl aclname src ip-address/netmask ... (clients IP address) + acl aclname src addr1-addr2/netmask ... (range of addresses) + acl aclname dst ip-address/netmask ... (URL host's IP address) + acl aclname myip ip-address/netmask ... (local socket IP address) + + acl aclname arp mac-address ... (xx:xx:xx:xx:xx:xx notation) + # The arp ACL requires the special configure option --enable-arp-acl. + # Furthermore, the arp ACL code is not portable to all operating systems. + # It works on Linux, Solaris, FreeBSD and some other *BSD variants. + # + # NOTE: Squid can only determine the MAC address for clients that are on + # the same subnet. If the client is on a different subnet, then Squid cannot + # find out its MAC address. + + acl aclname srcdomain .foo.com ... # reverse lookup, client IP + acl aclname dstdomain .foo.com ... # Destination server from URL + acl aclname srcdom_regex [-i] xxx ... # regex matching client name + acl aclname dstdom_regex [-i] xxx ... # regex matching server + # For dstdomain and dstdom_regex a reverse lookup is tried if a IP + # based URL is used and no match is found. The name "none" is used + # if the reverse lookup fails. + + acl aclname time [day-abbrevs] [h1:m1-h2:m2] + day-abbrevs: + S - Sunday + M - Monday + T - Tuesday + W - Wednesday + H - Thursday + F - Friday + A - Saturday + h1:m1 must be less than h2:m2 + acl aclname url_regex [-i] ^http:// ... # regex matching on whole URL + acl aclname urlpath_regex [-i] \.gif$ ... # regex matching on URL path + acl aclname urllogin [-i] [^a-zA-Z0-9] ... # regex matching on URL login field + acl aclname port 80 70 21 ... + acl aclname port 0-1024 ... # ranges allowed + acl aclname myport 3128 ... # (local socket TCP port) + acl aclname proto HTTP FTP ... + acl aclname method GET POST ... + acl aclname browser [-i] regexp ... + # pattern match on User-Agent header (see also req_header below) + acl aclname referer_regex [-i] regexp ... + # pattern match on Referer header + # Referer is highly unreliable, so use with care + acl aclname ident username ... + acl aclname ident_regex [-i] pattern ... + # string match on ident output. + # use REQUIRED to accept any non-null ident. + acl aclname src_as number ... + acl aclname dst_as number ... + # Except for access control, AS numbers can be used for + # routing of requests to specific caches. Here's an + # example for routing all requests for AS#1241 and only + # those to mycache.mydomain.net: + # acl asexample dst_as 1241 + # cache_peer_access mycache.mydomain.net allow asexample + # cache_peer_access mycache_mydomain.net deny all + + acl aclname proxy_auth username ... + acl aclname proxy_auth_regex [-i] pattern ... + # list of valid usernames + # use REQUIRED to accept any valid username. + # + # NOTE: when a Proxy-Authentication header is sent but it is not + # needed during ACL checking the username is NOT logged + # in access.log. + # + # NOTE: proxy_auth requires a EXTERNAL authentication program + # to check username/password combinations (see + # auth_param directive). + # + # WARNING: proxy_auth can't be used in a transparent proxy. It + # collides with any authentication done by origin servers. It may + # seem like it works at first, but it doesn't. + + acl aclname snmp_community string ... + # A community string to limit access to your SNMP Agent + # Example: + # + # acl snmppublic snmp_community public + + acl aclname maxconn number + # This will be matched when the client's IP address has + # more than HTTP connections established. + + acl aclname max_user_ip [-s] number + # This will be matched when the user attempts to log in from more + # than different ip addresses. The authenticate_ip_ttl + # parameter controls the timeout on the ip entries. + # If -s is specified the limit is strict, denying browsing + # from any further IP addresses until the ttl has expired. Without + # -s Squid will just annoy the user by "randomly" denying requests. + # (the counter is reset each time the limit is reached and a + # request is denied) + # NOTE: in acceleration mode or where there is mesh of child proxies, + # clients may appear to come from multiple addresses if they are + # going through proxy farms, so a limit of 1 may cause user problems. + + acl aclname req_mime_type mime-type1 ... + # regex match against the mime type of the request generated + # by the client. Can be used to detect file upload or some + # types HTTP tunneling requests. + # NOTE: This does NOT match the reply. You cannot use this + # to match the returned file type. + + acl aclname req_header header-name [-i] any\.regex\.here + # regex match against any of the known request headers. May be + # thought of as a superset of "browser", "referer" and "mime-type" + # ACLs. + + acl aclname rep_mime_type mime-type1 ... + # regex match against the mime type of the reply received by + # squid. Can be used to detect file download or some + # types HTTP tunneling requests. + # NOTE: This has no effect in http_access rules. It only has + # effect in rules that affect the reply data stream such as + # http_reply_access. + + acl aclname rep_header header-name [-i] any\.regex\.here + # regex match against any of the known response headers. + # Example: + # + # acl many_spaces rep_header Content-Disposition -i [[:space:]]{3,} + + acl acl_name external class_name [arguments...] + # external ACL lookup via a helper class defined by the + # external_acl_type directive. + + acl urlgroup group1 ... + # match against the urlgroup as indicated by redirectors + + acl aclname user_cert attribute values... + # match against attributes in a user SSL certificate + # attribute is one of DN/C/O/CN/L/ST + + acl aclname ca_cert attribute values... + # match against attributes a users issuing CA SSL certificate + # attribute is one of DN/C/O/CN/L/ST + + acl aclname ext_user username ... + acl aclname ext_user_regex [-i] pattern ... + # string match on username returned by external acl + # use REQUIRED to accept any user name. +Examples: +acl macaddress arp 09:00:2b:23:45:67 +acl myexample dst_as 1241 +acl password proxy_auth REQUIRED +acl fileupload req_mime_type -i ^multipart/form-data$ +acl javascript rep_mime_type -i ^application/x-javascript$ + +NOCOMMENT_START +#Recommended minimum configuration: +acl all src 0.0.0.0/0.0.0.0 +acl manager proto cache_object +acl localhost src 127.0.0.1/255.255.255.255 +acl to_localhost dst 127.0.0.0/8 +acl SSL_ports port 443 +acl Safe_ports port 80 # http +acl Safe_ports port 21 # ftp +acl Safe_ports port 443 # https +acl Safe_ports port 70 # gopher +acl Safe_ports port 210 # wais +acl Safe_ports port 1025-65535 # unregistered ports +acl Safe_ports port 280 # http-mgmt +acl Safe_ports port 488 # gss-http +acl Safe_ports port 591 # filemaker +acl Safe_ports port 777 # multiling http +acl CONNECT method CONNECT +NOCOMMENT_END +DOC_END + +NAME: follow_x_forwarded_for +TYPE: acl_access +IFDEF: FOLLOW_X_FORWARDED_FOR +LOC: Config.accessList.followXFF +DEFAULT: none +DEFAULT_IF_NONE: deny all +DOC_START + Allowing or Denying the X-Forwarded-For header to be followed to + find the original source of a request. + + Requests may pass through a chain of several other proxies + before reaching us. The X-Forwarded-For header will contain a + comma-separated list of the IP addresses in the chain, with the + rightmost address being the most recent. + + If a request reaches us from a source that is allowed by this + configuration item, then we consult the X-Forwarded-For header + to see where that host received the request from. If the + X-Forwarded-For header contains multiple addresses, and if + acl_uses_indirect_client is on, then we continue backtracking + until we reach an address for which we are not allowed to + follow the X-Forwarded-For header, or until we reach the first + address in the list. (If acl_uses_indirect_client is off, then + it's impossible to backtrack through more than one level of + X-Forwarded-For addresses.) + + The end result of this process is an IP address that we will + refer to as the indirect client address. This address may + be treated as the client address for access control, delay + pools and logging, depending on the acl_uses_indirect_client, + delay_pool_uses_indirect_client and log_uses_indirect_client + options. + + SECURITY CONSIDERATIONS: + + Any host for which we follow the X-Forwarded-For header + can place incorrect information in the header, and Squid + will use the incorrect information as if it were the + source address of the request. This may enable remote + hosts to bypass any access control restrictions that are + based on the client's source addresses. + + For example: + + acl localhost src 127.0.0.1 + acl my_other_proxy srcdomain .proxy.example.com + follow_x_forwarded_for allow localhost + follow_x_forwarded_for allow my_other_proxy +DOC_END + +NAME: acl_uses_indirect_client +COMMENT: on|off +TYPE: onoff +IFDEF: FOLLOW_X_FORWARDED_FOR +DEFAULT: on +LOC: Config.onoff.acl_uses_indirect_client +DOC_START + Controls whether the indirect client address + (see follow_x_forwarded_for) is used instead of the + direct client address in acl matching. +DOC_END + +NAME: delay_pool_uses_indirect_client +COMMENT: on|off +TYPE: onoff +IFDEF: FOLLOW_X_FORWARDED_FOR && DELAY_POOLS +DEFAULT: on +LOC: Config.onoff.delay_pool_uses_indirect_client +DOC_START + Controls whether the indirect client address + (see follow_x_forwarded_for) is used instead of the + direct client address in delay pools. +DOC_END + +NAME: log_uses_indirect_client +COMMENT: on|off +TYPE: onoff +IFDEF: FOLLOW_X_FORWARDED_FOR +DEFAULT: on +LOC: Config.onoff.log_uses_indirect_client +DOC_START + Controls whether the indirect client address + (see follow_x_forwarded_for) is used instead of the + direct client address in the access log. +DOC_END + +NAME: http_access +TYPE: acl_access +LOC: Config.accessList.http +DEFAULT: none +DEFAULT_IF_NONE: deny all +DOC_START + Allowing or Denying access based on defined access lists + + Access to the HTTP port: + http_access allow|deny [!]aclname ... + + NOTE on default values: + + If there are no "access" lines present, the default is to deny + the request. + + If none of the "access" lines cause a match, the default is the + opposite of the last line in the list. If the last line was + deny, the default is allow. Conversely, if the last line + is allow, the default will be deny. For these reasons, it is a + good idea to have an "deny all" or "allow all" entry at the end + of your access lists to avoid potential confusion. + +NOCOMMENT_START +#Recommended minimum configuration: +# +# Only allow cachemgr access from localhost +http_access allow manager localhost +http_access deny manager +# Deny requests to unknown ports +http_access deny !Safe_ports +# Deny CONNECT to other than SSL ports +http_access deny CONNECT !SSL_ports +# +# We strongly recommend the following be uncommented to protect innocent +# web applications running on the proxy server who think the only +# one who can access services on "localhost" is a local user +#http_access deny to_localhost +# +# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS + +# Example rule allowing access from your local networks. Adapt +# to list your (internal) IP networks from where browsing should +# be allowed +#acl our_networks src 192.168.1.0/24 192.168.2.0/24 +#http_access allow our_networks + +# And finally deny all other access to this proxy +http_access deny all +NOCOMMENT_END +DOC_END + +NAME: http_access2 +TYPE: acl_access +LOC: Config.accessList.http2 +DEFAULT: none +DOC_START + Allowing or Denying access based on defined access lists + + Identical to http_access, but runs after redirectors. If not set + then only http_access is used. +DOC_END + +NAME: http_reply_access +TYPE: acl_access +LOC: Config.accessList.reply +DEFAULT: none +DEFAULT_IF_NONE: allow all +DOC_START + Allow replies to client requests. This is complementary to http_access. + + http_reply_access allow|deny [!] aclname ... + + NOTE: if there are no access lines present, the default is to allow + all replies + + If none of the access lines cause a match the opposite of the + last line will apply. Thus it is good practice to end the rules + with an "allow all" or "deny all" entry. + +NOCOMMENT_START +#Recommended minimum configuration: +# +# Insert your own rules here. +# +# +# and finally allow by default +http_reply_access allow all +NOCOMMENT_END +DOC_END + + +NAME: icp_access +TYPE: acl_access +LOC: Config.accessList.icp +DEFAULT: none +DEFAULT_IF_NONE: deny all +DOC_START + Allowing or Denying access to the ICP port based on defined + access lists + + icp_access allow|deny [!]aclname ... + + See http_access for details + +NOCOMMENT_START +#Allow ICP queries from everyone +icp_access allow all +NOCOMMENT_END +DOC_END + +NAME: htcp_access +IFDEF: USE_HTCP +TYPE: acl_access +LOC: Config.accessList.htcp +DEFAULT: none +DEFAULT_IF_NONE: deny all +DOC_START + Allowing or Denying access to the HTCP port based on defined + access lists + + htcp_access allow|deny [!]aclname ... + + See http_access for details + +#Allow HTCP queries from everyone +htcp_access allow all +DOC_END + +NAME: htcp_clr_access +IFDEF: USE_HTCP +TYPE: acl_access +LOC: Config.accessList.htcp_clr +DEFAULT: none +DEFAULT_IF_NONE: deny all +DOC_START + Allowing or Denying access to purge content using HTCP based + on defined access lists + + htcp_clr_access allow|deny [!]aclname ... + + See http_access for details + +#Allow HTCP CLR requests from trusted peers +acl htcp_clr_peer src 172.16.1.2 +htcp_clr_access allow htcp_clr_peer +DOC_END + + +NAME: miss_access +TYPE: acl_access +LOC: Config.accessList.miss +DEFAULT: none +DOC_START + Use to force your neighbors to use you as a sibling instead of + a parent. For example: + + acl localclients src 172.16.0.0/16 + miss_access allow localclients + miss_access deny !localclients + + This means only your local clients are allowed to fetch + MISSES and all other clients can only fetch HITS. + + By default, allow all clients who passed the http_access rules + to fetch MISSES from us. + +NOCOMMENT_START +#Default setting: +# miss_access allow all +NOCOMMENT_END +DOC_END + + +NAME: cache_peer_access +TYPE: peer_access +DEFAULT: none +LOC: none +DOC_START + Similar to 'cache_peer_domain' but provides more flexibility by + using ACL elements. + + cache_peer_access cache-host allow|deny [!]aclname ... + + The syntax is identical to 'http_access' and the other lists of + ACL elements. See the comments for 'http_access' below, or + the Squid FAQ (http://www.squid-cache.org/FAQ/FAQ-10.html). +DOC_END + +NAME: ident_lookup_access +TYPE: acl_access +IFDEF: USE_IDENT +DEFAULT: none +DEFAULT_IF_NONE: deny all +LOC: Config.accessList.identLookup +DOC_START + A list of ACL elements which, if matched, cause an ident + (RFC931) lookup to be performed for this request. For + example, you might choose to always perform ident lookups + for your main multi-user Unix boxes, but not for your Macs + and PCs. By default, ident lookups are not performed for + any requests. + + To enable ident lookups for specific client addresses, you + can follow this example: + + acl ident_aware_hosts src 198.168.1.0/255.255.255.0 + ident_lookup_access allow ident_aware_hosts + ident_lookup_access deny all + + Only src type ACL checks are fully supported. A src_domain + ACL might work at times, but it will not always provide + the correct result. +DOC_END + +NAME: tcp_outgoing_tos tcp_outgoing_ds tcp_outgoing_dscp +TYPE: acl_tos +DEFAULT: none +LOC: Config.accessList.outgoing_tos +DOC_START + Allows you to select a TOS/Diffserv value to mark outgoing + connections with, based on the username or source address + making the request. + + tcp_outgoing_tos ds-field [!]aclname ... + + Example where normal_service_net uses the TOS value 0x00 + and normal_service_net uses 0x20 + + acl normal_service_net src 10.0.0.0/255.255.255.0 + acl good_service_net src 10.0.1.0/255.255.255.0 + tcp_outgoing_tos 0x00 normal_service_net 0x00 + tcp_outgoing_tos 0x20 good_service_net + + TOS/DSCP values really only have local significance - so you should + know what you're specifying. For more information, see RFC2474 and + RFC3260. + + The TOS/DSCP byte must be exactly that - a octet value 0 - 255, or + "default" to use whatever default your host has. Note that in + practice often only values 0 - 63 is usable as the two highest bits + have been redefined for use by ECN (RFC3168). + + Processing proceeds in the order specified, and stops at first fully + matching line. + + Note: The use of this directive using client dependent ACLs is + incompatible with the use of server side persistent connections. To + ensure correct results it is best to set server_persisten_connections + to off when using this directive in such configurations. +DOC_END + +NAME: tcp_outgoing_address +TYPE: acl_address +DEFAULT: none +LOC: Config.accessList.outgoing_address +DOC_START + Allows you to map requests to different outgoing IP addresses + based on the username or source address of the user making + the request. + + tcp_outgoing_address ipaddr [[!]aclname] ... + + Example where requests from 10.0.0.0/24 will be forwarded + with source address 10.1.0.1, 10.0.2.0/24 forwarded with + source address 10.1.0.2 and the rest will be forwarded with + source address 10.1.0.3. + + acl normal_service_net src 10.0.0.0/255.255.255.0 + acl good_service_net src 10.0.1.0/255.255.255.0 + tcp_outgoing_address 10.0.0.1 normal_service_net + tcp_outgoing_address 10.0.0.2 good_service_net + tcp_outgoing_address 10.0.0.3 + + Processing proceeds in the order specified, and stops at first fully + matching line. + + Note: The use of this directive using client dependent ACLs is + incompatible with the use of server side persistent connections. To + 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 +DEFAULT: 20 KB +LOC: Config.maxReplyHeaderSize +DOC_START + This specifies the maximum size for HTTP headers in a reply. + Reply headers are usually relatively small (about 512 bytes). + Placing a limit on the reply header size will catch certain + bugs (for example with persistent connections) and possibly + buffer-overflow or denial-of-service attacks. +DOC_END + +NAME: reply_body_max_size +COMMENT: bytes allow|deny acl acl... +TYPE: body_size_t +DEFAULT: none +DEFAULT_IF_NONE: 0 allow all +LOC: Config.ReplyBodySize +DOC_START + This option specifies the maximum size of a reply body in bytes. + It can be used to prevent users from downloading very large files, + such as MP3's and movies. When the reply headers are received, + the reply_body_max_size lines are processed, and the first line with + a result of "allow" is used as the maximum body size for this reply. + This size is checked twice. First when we get the reply headers, + we check the content-length value. If the content length value exists + and is larger than the allowed size, the request is denied and the + user receives an error message that says "the request or reply + is too large." If there is no content-length, and the reply + size exceeds this limit, the client's connection is just closed + and they will receive a partial reply. + + WARNING: downstream caches probably can not detect a partial reply + if there is no content-length header, so they will cache + partial responses and give them out as hits. You should NOT + use this option if you have downstream caches. + + If you set this parameter to zero (the default), there will be + no limit imposed. +DOC_END + +NAME: log_access +TYPE: acl_access +LOC: Config.accessList.log +DEFAULT: none +COMMENT: allow|deny acl acl... +DOC_START + This options allows you to control which requests gets logged + to access.log (see access_log directive). Requests denied for + logging will also not be accounted for in performance counters. +DOC_END + +COMMENT_START + ADMINISTRATIVE PARAMETERS + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: cache_mgr +TYPE: string +DEFAULT: webmaster +LOC: Config.adminEmail +DOC_START + Email-address of local cache manager who will receive + mail if the cache dies. The default is "webmaster". +DOC_END + + +NAME: mail_from +TYPE: string +DEFAULT: none +LOC: Config.EmailFrom +DOC_START + From: email-address for mail sent when the cache dies. + The default is to use 'appname@unique_hostname'. + Default appname value is "squid", can be changed into + src/globals.h before building squid. +DOC_END + + +NAME: mail_program +TYPE: eol +DEFAULT: mail +LOC: Config.EmailProgram +DOC_START + Email program used to send mail if the cache dies. + The default is "mail". The specified program must complain + with the standard Unix mail syntax: + mail_program recipient < mailfile + Optional command line options can be specified. +DOC_END + + +NAME: cache_effective_user +TYPE: string +DEFAULT: nobody +LOC: Config.effectiveUser +DOC_START + If you start Squid as root, it will change its effective/real + UID/GID to the user specified below. The default is to change + to UID to nobody. If you define cache_effective_user, but not + cache_effective_group, Squid sets the GID to the effective + user's default group ID (taken from the password file) and + supplementary group list from the from groups membership of + cache_effective_user. +DOC_END + + +NAME: cache_effective_group +TYPE: string +DEFAULT: none +LOC: Config.effectiveGroup +DOC_START + If you want Squid to run with a specific GID regardless of + the group memberships of the effective user then set this + to the group (or GID) you want Squid to run as. When set + all other group privileges of the effective user is ignored + and only this GID is effective. If Squid is not started as + root the user starting Squid must be member of the specified + group. +DOC_END + + +NAME: httpd_suppress_version_string +COMMENT: on|off +TYPE: onoff +DEFAULT: off +LOC: Config.onoff.httpd_suppress_version_string +DOC_START + Suppress Squid version string info in HTTP headers and HTML error pages. +DOC_END + + +NAME: visible_hostname +TYPE: string +LOC: Config.visibleHostname +DEFAULT: none +DOC_START + If you want to present a special hostname in error messages, etc, + define this. Otherwise, the return value of gethostname() + will be used. If you have multiple caches in a cluster and + get errors about IP-forwarding you must set them to have individual + names with this setting. +DOC_END + + +NAME: unique_hostname +TYPE: string +LOC: Config.uniqueHostname +DEFAULT: none +DOC_START + If you want to have multiple machines with the same + 'visible_hostname' you must give each machine a different + 'unique_hostname' so forwarding loops can be detected. +DOC_END + + +NAME: hostname_aliases +TYPE: wordlist +LOC: Config.hostnameAliases +DEFAULT: none +DOC_START + A list of other DNS names your cache has. +DOC_END + +NAME: umask +TYPE: int +LOC: Config.umask +DEFAULT: 027 +DOC_START + Minimum umask which should be enforced while the proxy + is running, in addition to the umask set at startup. + + Note: Should start with a 0 to indicate the normal octal + representation of umasks +DOC_END + +COMMENT_START + OPTIONS FOR THE CACHE REGISTRATION SERVICE + ----------------------------------------------------------------------------- + + This section contains parameters for the (optional) cache + announcement service. This service is provided to help + cache administrators locate one another in order to join or + create cache hierarchies. + + An 'announcement' message is sent (via UDP) to the registration + service by Squid. By default, the announcement message is NOT + SENT unless you enable it with 'announce_period' below. + + The announcement message includes your hostname, plus the + following information from this configuration file: + + http_port + icp_port + cache_mgr + + All current information is processed regularly and made + available on the Web at http://www.ircache.net/Cache/Tracker/. +COMMENT_END + +NAME: announce_period +TYPE: time_t +LOC: Config.Announce.period +DEFAULT: 0 +DOC_START + This is how frequently to send cache announcements. The + default is `0' which disables sending the announcement + messages. + + To enable announcing your cache, just uncomment the line + below. + +NOCOMMENT_START +#To enable announcing your cache, just uncomment the line below. +#announce_period 1 day +NOCOMMENT_END +DOC_END + + +NAME: announce_host +TYPE: string +DEFAULT: tracker.ircache.net +LOC: Config.Announce.host +DOC_NONE + +NAME: announce_file +TYPE: string +DEFAULT: none +LOC: Config.Announce.file +DOC_NONE + +NAME: announce_port +TYPE: ushort +DEFAULT: 3131 +LOC: Config.Announce.port +DOC_START + announce_host and announce_port set the hostname and port + number where the registration message will be sent. + + Hostname will default to 'tracker.ircache.net' and port will + default default to 3131. If the 'filename' argument is given, + the contents of that file will be included in the announce + message. +DOC_END + +COMMENT_START + HTTPD-ACCELERATOR OPTIONS + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: httpd_accel_no_pmtu_disc +COMMENT: on|off +TYPE: onoff +DEFAULT: off +LOC: Config.onoff.accel_no_pmtu_disc +DOC_START + In many setups of transparently intercepting proxies Path-MTU + discovery can not work on traffic towards the clients. This is + the case when the intercepting device does not fully track + connections and fails to forward ICMP must fragment messages + to the cache server. + + If you have such setup and experience that certain clients + sporadically hang or never complete requests set this to on. +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 + +NAME: dns_testnames +TYPE: wordlist +LOC: Config.dns_testname_list +DEFAULT: none +DEFAULT_IF_NONE: netscape.com internic.net nlanr.net microsoft.com +DOC_START + The DNS tests exit as soon as the first site is successfully looked up + + This test can be disabled with the -D command line option. +DOC_END + + +NAME: logfile_rotate +TYPE: int +DEFAULT: 10 +LOC: Config.Log.rotateNumber +DOC_START + Specifies the number of logfile rotations to make when you + type 'squid -k rotate'. The default is 10, which will rotate + with extensions 0 through 9. Setting logfile_rotate to 0 will + disable the rotation, but the logfiles are still closed and + re-opened. This will enable you to rename the logfiles + yourself just before sending the rotate signal. + + Note, the 'squid -k rotate' command normally sends a USR1 + signal to the running squid process. In certain situations + (e.g. on Linux with Async I/O), USR1 is used for other + purposes, so -k rotate uses another signal. It is best to get + in the habit of using 'squid -k rotate' instead of 'kill -USR1 + '. +DOC_END + + +NAME: append_domain +TYPE: string +LOC: Config.appendDomain +DEFAULT: none +DOC_START + Appends local domain name to hostnames without any dots in + them. append_domain must begin with a period. + + Be warned there are now Internet names with no dots in + them using only top-domain names, so setting this may + cause some Internet sites to become unavailable. + +Example: + append_domain .yourdomain.com +DOC_END + + +NAME: tcp_recv_bufsize +COMMENT: (bytes) +TYPE: b_size_t +DEFAULT: 0 bytes +LOC: Config.tcpRcvBufsz +DOC_START + Size of receive buffer to set for TCP sockets. Probably just + as easy to change your kernel's default. Set to zero to use + the default buffer size. +DOC_END + +NAME: error_map +TYPE: errormap +LOC: Config.errorMapList +DEFAULT: none +DOC_START + Map errors to custom messages + + error_map message_url http_status ... + + http_status ... is a list of HTTP status codes or Squid error + messages. + + Use in accelerators to substitute the error messages returned + by servers with other custom errors. + + error_map http://your.server/error/404.shtml 404 + + Requests for error messages is a GET request for the configured + URL with the following special headers + + X-Error-Status: The received HTTP status code (i.e. 404) + X-Request-URI: The requested URI where the error occurred + + In Addition the following headers are forwarded from the client + request: + + User-Agent, Cookie, X-Forwarded-For, Via, Authorization, + Accept, Referer + + And the following headers from the server reply: + + Server, Via, Location, Content-Location + + The reply returned to the client will carry the original HTTP + headers from the real error message, but with the reply body + of the configured error message. + +DOC_END + +NAME: err_html_text +TYPE: eol +LOC: Config.errHtmlText +DEFAULT: none +DOC_START + HTML text to include in error messages. Make this a "mailto" + URL to your admin address, or maybe just a link to your + organizations Web page. + + To include this in your error messages, you must rewrite + the error template files (found in the "errors" directory). + Wherever you want the 'err_html_text' line to appear, + insert a %L tag in the error template file. +DOC_END + +NAME: deny_info +TYPE: denyinfo +LOC: Config.denyInfoList +DEFAULT: none +DOC_START + Usage: deny_info err_page_name acl + or deny_info http://... acl + Example: deny_info ERR_CUSTOM_ACCESS_DENIED bad_guys + + This can be used to return a ERR_ page for requests which + do not pass the 'http_access' rules. A single ACL will cause + the http_access check to fail. If a 'deny_info' line exists + for that ACL Squid returns a corresponding error page. + + You may use ERR_ pages that come with Squid or create your own pages + and put them into the configured errors/ directory. + + Alternatively you can specify an error URL. The browsers will + get redirected (302) to the specified URL. %s in the redirection + URL will be replaced by the requested URL. + + Alternatively you can tell Squid to reset the TCP connection + by specifying TCP_RESET. +DOC_END + +NAME: memory_pools +COMMENT: on|off +TYPE: onoff +DEFAULT: on +LOC: Config.onoff.mem_pools +DOC_START + If set, Squid will keep pools of allocated (but unused) memory + available for future use. If memory is a premium on your + system and you believe your malloc library outperforms Squid + routines, disable this. +DOC_END + +NAME: memory_pools_limit +COMMENT: (bytes) +TYPE: b_size_t +DEFAULT: 5 MB +LOC: Config.MemPools.limit +DOC_START + Used only with memory_pools on: + memory_pools_limit 50 MB + + If set to a non-zero value, Squid will keep at most the specified + limit of allocated (but unused) memory in memory pools. All free() + requests that exceed this limit will be handled by your malloc + library. Squid does not pre-allocate any memory, just safe-keeps + objects that otherwise would be free()d. Thus, it is safe to set + memory_pools_limit to a reasonably high value even if your + configuration will use less memory. + + If set to zero, Squid will keep all memory it can. That is, there + will be no limit on the total amount of memory used for safe-keeping. + + To disable memory allocation optimization, do not set + memory_pools_limit to 0. Set memory_pools to "off" instead. + + An overhead for maintaining memory pools is not taken into account + when the limit is checked. This overhead is close to four bytes per + object kept. However, pools may actually _save_ memory because of + reduced memory thrashing in your malloc library. +DOC_END + +NAME: via +IFDEF: HTTP_VIOLATIONS +COMMENT: on|off +TYPE: onoff +DEFAULT: on +LOC: Config.onoff.via +DOC_START + If set (default), Squid will include a Via header in requests and + replies. +DOC_END + +NAME: forwarded_for +COMMENT: on|off +TYPE: onoff +DEFAULT: on +LOC: opt_forwarded_for +DOC_START + If set, Squid will include your system's IP address or name + in the HTTP requests it forwards. By default it looks like + this: + + X-Forwarded-For: 192.1.2.3 + + If you disable this, it will appear as + + X-Forwarded-For: unknown +DOC_END + + +NAME: log_icp_queries +COMMENT: on|off +TYPE: onoff +DEFAULT: on +LOC: Config.onoff.log_udp +DOC_START + If set, ICP queries are logged to access.log. You may wish + do disable this if your ICP load is VERY high to speed things + up or to simplify log analysis. +DOC_END + +NAME: icp_hit_stale +COMMENT: on|off +TYPE: onoff +DEFAULT: off +LOC: Config.onoff.icp_hit_stale +DOC_START + If you want to return ICP_HIT for stale cache objects, set this + option to 'on'. If you have sibling relationships with caches + in other administrative domains, this should be 'off'. If you only + have sibling relationships with caches under your control, + it is probably okay to set this to 'on'. + If set to 'on', your siblings should use the option "allow-miss" + on their cache_peer lines for connecting to you. +DOC_END + + +NAME: minimum_direct_hops +TYPE: int +DEFAULT: 4 +LOC: Config.minDirectHops +DOC_START + If using the ICMP pinging stuff, do direct fetches for sites + which are no more than this many hops away. +DOC_END + +NAME: minimum_direct_rtt +TYPE: int +DEFAULT: 400 +LOC: Config.minDirectRtt +DOC_START + If using the ICMP pinging stuff, do direct fetches for sites + which are no more than this many rtt milliseconds away. +DOC_END + +NAME: cachemgr_passwd +TYPE: cachemgrpasswd +DEFAULT: none +LOC: Config.passwd_list +DOC_START + Specify passwords for cachemgr operations. + + Usage: cachemgr_passwd password action action ... + + Some valid actions are (see cache manager menu for a full list): + 5min + 60min + asndb + authenticator + cbdata + client_list + comm_incoming + config * + counters + delay + digest_stats + dns + events + filedescriptors + fqdncache + histograms + http_headers + info + io + ipcache + mem + menu + netdb + non_peers + objects + offline_toggle * + pconn + peer_select + redirector + refresh + server_list + shutdown * + store_digest + storedir + utilization + via_headers + vm_objects + + * Indicates actions which will not be performed without a + valid password, others can be performed if not listed here. + + To disable an action, set the password to "disable". + To allow performing an action without a password, set the + password to "none". + + Use the keyword "all" to set the same password for all actions. + +Example: + cachemgr_passwd secret shutdown + cachemgr_passwd lesssssssecret info stats/objects + cachemgr_passwd disable all +DOC_END + +NAME: store_avg_object_size +COMMENT: (kbytes) +TYPE: kb_size_t +DEFAULT: 13 KB +LOC: Config.Store.avgObjectSize +DOC_START + Average object size, used to estimate number of objects your + cache can hold. The default is 13 KB. +DOC_END + +NAME: store_objects_per_bucket +TYPE: int +DEFAULT: 20 +LOC: Config.Store.objectsPerBucket +DOC_START + Target number of objects per bucket in the store hash table. + Lowering this value increases the total number of buckets and + also the storage maintenance rate. The default is 50. +DOC_END + +NAME: client_db +COMMENT: on|off +TYPE: onoff +DEFAULT: on +LOC: Config.onoff.client_db +DOC_START + If you want to disable collecting per-client statistics, + turn off client_db here. +DOC_END + + +NAME: netdb_low +TYPE: int +DEFAULT: 900 +LOC: Config.Netdb.low +DOC_NONE + +NAME: netdb_high +TYPE: int +DEFAULT: 1000 +LOC: Config.Netdb.high +DOC_START + The low and high water marks for the ICMP measurement + database. These are counts, not percents. The defaults are + 900 and 1000. When the high water mark is reached, database + entries will be deleted until the low mark is reached. +DOC_END + + +NAME: netdb_ping_period +TYPE: time_t +LOC: Config.Netdb.period +DEFAULT: 5 minutes +DOC_START + The minimum period for measuring a site. There will be at + least this much delay between successive pings to the same + network. The default is five minutes. +DOC_END + + +NAME: query_icmp +COMMENT: on|off +TYPE: onoff +DEFAULT: off +LOC: Config.onoff.query_icmp +DOC_START + If you want to ask your peers to include ICMP data in their ICP + replies, enable this option. + + If your peer has configured Squid (during compilation) with + '--enable-icmp' that peer will send ICMP pings to origin server + sites of the URLs it receives. If you enable this option the + ICP replies from that peer will include the ICMP data (if available). + Then, when choosing a parent cache, Squid will choose the parent with + the minimal RTT to the origin server. When this happens, the + hierarchy field of the access.log will be + "CLOSEST_PARENT_MISS". This option is off by default. +DOC_END + +NAME: test_reachability +COMMENT: on|off +TYPE: onoff +DEFAULT: off +LOC: Config.onoff.test_reachability +DOC_START + When this is 'on', ICP MISS replies will be ICP_MISS_NOFETCH + instead of ICP_MISS if the target host is NOT in the ICMP + database, or has a zero RTT. +DOC_END + +NAME: buffered_logs +COMMENT: on|off +TYPE: onoff +DEFAULT: off +LOC: Config.onoff.buffered_logs +DOC_START + cache.log log file is written with stdio functions, and as such + it can be buffered or unbuffered. By default it will be unbuffered. + Buffering it can speed up the writing slightly (though you are + unlikely to need to worry unless you run with tons of debugging + enabled in which case performance will suffer badly anyway..). +DOC_END + +NAME: reload_into_ims +IFDEF: HTTP_VIOLATIONS +COMMENT: on|off +TYPE: onoff +DEFAULT: off +LOC: Config.onoff.reload_into_ims +DOC_START + When you enable this option, client no-cache or ``reload'' + requests will be changed to If-Modified-Since requests. + Doing this VIOLATES the HTTP standard. Enabling this + feature could make you liable for problems which it + causes. + + see also refresh_pattern for a more selective approach. +DOC_END + +NAME: always_direct +TYPE: acl_access +LOC: Config.accessList.AlwaysDirect +DEFAULT: none +DOC_START + Usage: always_direct allow|deny [!]aclname ... + + Here you can use ACL elements to specify requests which should + ALWAYS be forwarded by Squid to the origin servers without using + any peers. For example, to always directly forward requests for + local servers ignoring any parents or siblings you may have use + something like: + + acl local-servers dstdomain my.domain.net + always_direct allow local-servers + + To always forward FTP requests directly, use + + acl FTP proto FTP + always_direct allow FTP + + NOTE: There is a similar, but opposite option named + 'never_direct'. You need to be aware that "always_direct deny + foo" is NOT the same thing as "never_direct allow foo". You + may need to use a deny rule to exclude a more-specific case of + some other rule. Example: + + acl local-external dstdomain external.foo.net + acl local-servers dstdomain .foo.net + always_direct deny local-external + always_direct allow local-servers + + NOTE: If your goal is to make the client forward the request + directly to the origin server bypassing Squid then this needs + to be done in the client configuration. Squid configuration + can only tell Squid how Squid should fetch the object. + + NOTE: This directive is not related to caching. The replies + is cached as usual even if you use always_direct. To not cache + the replies see no_cache. + + This option replaces some v1.1 options such as local_domain + and local_ip. +DOC_END + +NAME: never_direct +TYPE: acl_access +LOC: Config.accessList.NeverDirect +DEFAULT: none +DOC_START + Usage: never_direct allow|deny [!]aclname ... + + never_direct is the opposite of always_direct. Please read + the description for always_direct if you have not already. + + With 'never_direct' you can use ACL elements to specify + requests which should NEVER be forwarded directly to origin + servers. For example, to force the use of a proxy for all + requests, except those in your local domain use something like: + + acl local-servers dstdomain .foo.net + acl all src 0.0.0.0/0.0.0.0 + never_direct deny local-servers + never_direct allow all + + or if Squid is inside a firewall and there are local intranet + servers inside the firewall use something like: + + acl local-intranet dstdomain .foo.net + acl local-external dstdomain external.foo.net + always_direct deny local-external + always_direct allow local-intranet + never_direct allow all + + This option replaces some v1.1 options such as inside_firewall + and firewall_ip. +DOC_END + +NAME: header_access +IFDEF: HTTP_VIOLATIONS +TYPE: http_header_access[] +LOC: Config.header_access +DEFAULT: none +DOC_START + Usage: header_access header_name allow|deny [!]aclname ... + + WARNING: Doing this VIOLATES the HTTP standard. Enabling + this feature could make you liable for problems which it + causes. + + This option replaces the old 'anonymize_headers' and the + older 'http_anonymizer' option with something that is much + more configurable. This new method creates a list of ACLs + for each header, allowing you very fine-tuned header + mangling. + + You can only specify known headers for the header name. + Other headers are reclassified as 'Other'. You can also + refer to all the headers with 'All'. + + For example, to achieve the same behavior as the old + 'http_anonymizer standard' option, you should use: + + header_access From deny all + header_access Referer deny all + header_access Server deny all + header_access User-Agent deny all + header_access WWW-Authenticate deny all + header_access Link deny all + + Or, to reproduce the old 'http_anonymizer paranoid' feature + you should use: + + header_access Allow allow all + header_access Authorization allow all + header_access WWW-Authenticate allow all + header_access Proxy-Authorization allow all + header_access Proxy-Authenticate allow all + header_access Cache-Control allow all + header_access Content-Encoding allow all + header_access Content-Length allow all + header_access Content-Type allow all + header_access Date allow all + header_access Expires allow all + header_access Host allow all + header_access If-Modified-Since allow all + header_access Last-Modified allow all + header_access Location allow all + header_access Pragma allow all + header_access Accept allow all + header_access Accept-Charset allow all + header_access Accept-Encoding allow all + header_access Accept-Language allow all + header_access Content-Language allow all + header_access Mime-Version allow all + header_access Retry-After allow all + header_access Title allow all + header_access Connection allow all + header_access Proxy-Connection allow all + header_access All deny all + + By default, all headers are allowed (no anonymizing is + performed). +DOC_END + +NAME: header_replace +IFDEF: HTTP_VIOLATIONS +TYPE: http_header_replace[] +LOC: Config.header_access +DEFAULT: none +DOC_START + Usage: header_replace header_name message + Example: header_replace User-Agent Nutscrape/1.0 (CP/M; 8-bit) + + This option allows you to change the contents of headers + denied with header_access above, by replacing them with + some fixed string. This replaces the old fake_user_agent + option. + + By default, headers are removed if denied. +DOC_END + +NAME: icon_directory +TYPE: string +LOC: Config.icons.directory +DEFAULT: @DEFAULT_ICON_DIR@ +DOC_START + Where the icons are stored. These are normally kept in + @DEFAULT_ICON_DIR@ +DOC_END + +NAME: global_internal_static +TYPE: onoff +LOC: Config.onoff.global_internal_static +DEFAULT: on +DOC_START + This directive controls is Squid should intercept all requests for + /squid-internal-static/ no matter which host the URL is requesting + (default on setting), or if nothing special should be done for + such URLs (off setting). The purpose of this directive is to make + icons etc work better in complex cache hierarchies where it may + not always be possible for all corners in the cache mesh to reach + the server generating a directory listing. +DOC_END + +NAME: short_icon_urls +TYPE: onoff +LOC: Config.icons.use_short_names +DEFAULT: off +DOC_START + If this is enabled Squid will use short URLs for icons. + + If off the URLs for icons will always be absolute URLs + including the proxy name and port. +DOC_END + +NAME: error_directory +TYPE: string +LOC: Config.errorDirectory +DEFAULT: @DEFAULT_ERROR_DIR@ +DOC_START + If you wish to create your own versions of the default + (English) error files, either to customize them to suit your + language or company copy the template English files to another + directory and point this tag at them. +DOC_END + +NAME: maximum_single_addr_tries +TYPE: int +LOC: Config.retry.maxtries +DEFAULT: 1 +DOC_START + This sets the maximum number of connection attempts for a + host that only has one address (for multiple-address hosts, + each address is tried once). + + The default value is one attempt, the (not recommended) + maximum is 255 tries. A warning message will be generated + if it is set to a value greater than ten. + + Note: This is in addition to the request re-forwarding which + takes place if Squid fails to get a satisfying response. +DOC_END + +NAME: retry_on_error +TYPE: onoff +LOC: Config.retry.onerror +DEFAULT: off +DOC_START + If set to on Squid will automatically retry requests when + receiving an error response. This is mainly useful if you + are in a complex cache hierarchy to work around access + control errors. +DOC_END + +NAME: snmp_port +TYPE: ushort +LOC: Config.Port.snmp +DEFAULT: 3401 +IFDEF: SQUID_SNMP +DOC_START + Squid can now serve statistics and status information via SNMP. + By default it listens to port 3401 on the machine. If you don't + wish to use SNMP, set this to "0". +DOC_END + +NAME: snmp_access +TYPE: acl_access +LOC: Config.accessList.snmp +DEFAULT: none +DEFAULT_IF_NONE: deny all +IFDEF: SQUID_SNMP +DOC_START + Allowing or denying access to the SNMP port. + + All access to the agent is denied by default. + usage: + + snmp_access allow|deny [!]aclname ... + +Example: + snmp_access allow snmppublic localhost + snmp_access deny all +DOC_END + +NAME: snmp_incoming_address +TYPE: address +LOC: Config.Addrs.snmp_incoming +DEFAULT: 0.0.0.0 +IFDEF: SQUID_SNMP +DOC_NONE +NAME: snmp_outgoing_address +TYPE: address +LOC: Config.Addrs.snmp_outgoing +DEFAULT: 255.255.255.255 +IFDEF: SQUID_SNMP +DOC_START + Just like 'udp_incoming_address' above, but for the SNMP port. + + snmp_incoming_address is used for the SNMP socket receiving + messages from SNMP agents. + snmp_outgoing_address is used for SNMP packets returned to SNMP + agents. + + The default snmp_incoming_address (0.0.0.0) is to listen on all + available network interfaces. + + If snmp_outgoing_address is set to 255.255.255.255 (the default) + it will use the same socket as snmp_incoming_address. Only + change this if you want to have SNMP replies sent using another + address than where this Squid listens for SNMP queries. + + NOTE, snmp_incoming_address and snmp_outgoing_address can not have + the same value since they both use port 3401. +DOC_END + +NAME: as_whois_server +TYPE: string +LOC: Config.as_whois_server +DEFAULT: whois.ra.net +DEFAULT_IF_NONE: whois.ra.net +DOC_START + WHOIS server to query for AS numbers. NOTE: AS numbers are + queried only when Squid starts up, not for every request. +DOC_END + +NAME: wccp_router +TYPE: address +LOC: Config.Wccp.router +DEFAULT: 0.0.0.0 +IFDEF: USE_WCCP +DOC_NONE +NAME: wccp2_router +TYPE: sockaddr_in_list +LOC: Config.Wccp2.router +DEFAULT: none +IFDEF: USE_WCCPv2 +DOC_START + Use this option to define your WCCP ``home'' router for + Squid. + + wccp_router supports a single WCCP(v1) router + + wccp2_router supports multiple WCCPv2 routers + + only one of the two may be used at the same time and defines + which version of WCCP to use. +DOC_END + +NAME: wccp_version +TYPE: int +LOC: Config.Wccp.version +DEFAULT: 4 +IFDEF: USE_WCCP +DOC_START + This directive is only relevant if you need to set up WCCP(v1) + to some very old and end-of-life Cisco routers. In all other + setups it must be left unset or at the default setting. + It defines an internal version in the WCCP(v1) protocol, + with version 4 being the officially documented protocol. + + According to some users, Cisco IOS 11.2 and earlier only + support WCCP version 3. If you're using that or an earlier + version of IOS, you may need to change this value to 3, otherwise + do not specify this parameter. +DOC_END + +NAME: wccp2_rebuild_wait +TYPE: onoff +LOC: Config.Wccp2.rebuildwait +DEFAULT: on +IFDEF: USE_WCCPv2 +DOC_START + If this is enabled Squid will wait for the cache dir rebuild to finish + before sending the first wccp2 HereIAm packet +DOC_END + +NAME: wccp2_forwarding_method +TYPE: int +LOC: Config.Wccp2.forwarding_method +DEFAULT: 1 +IFDEF: USE_WCCPv2 +DOC_START + WCCP2 allows the setting of forwarding methods between the + router/switch and the cache. Valid values are as follows: + + 1 - GRE encapsulation (forward the packet in a GRE/WCCP tunnel) + 2 - L2 redirect (forward the packet using Layer 2/MAC rewriting) + + Currently (as of IOS 12.4) cisco routers only support GRE. + Cisco switches only support the L2 redirect assignment method. +DOC_END + +NAME: wccp2_return_method +TYPE: int +LOC: Config.Wccp2.return_method +DEFAULT: 1 +IFDEF: USE_WCCPv2 +DOC_START + WCCP2 allows the setting of return methods between the + router/switch and the cache for packets that the cache + decides not to handle. Valid values are as follows: + + 1 - GRE encapsulation (forward the packet in a GRE/WCCP tunnel) + 2 - L2 redirect (forward the packet using Layer 2/MAC rewriting) + + Currently (as of IOS 12.4) cisco routers only support GRE. + Cisco switches only support the L2 redirect assignment. + + If the "ip wccp redirect exclude in" command has been + enabled on the cache interface, then it is still safe for + the proxy server to use a l2 redirect method even if this + option is set to GRE. +DOC_END + +NAME: wccp2_assignment_method +TYPE: int +LOC: Config.Wccp2.assignment_method +DEFAULT: 1 +IFDEF: USE_WCCPv2 +DOC_START + WCCP2 allows the setting of methods to assign the WCCP hash + Valid values are as follows: + + 1 - Hash assignment + 2 - Mask assignment + + As a general rule, cisco routers support the hash assignment method + and cisco switches support the mask assignment method. +DOC_END + +NAME: wccp2_service +TYPE: wccp2_service +LOC: Config.Wccp2.info +DEFAULT: none +DEFAULT_IF_NONE: standard 0 +IFDEF: USE_WCCPv2 +DOC_START + WCCP2 allows for multiple traffic services. There are two + types: "standard" and "dynamic". The standard type defines + one service id - http (id 0). The dynamic service ids can be from + 51 to 255 inclusive. In order to use a dynamic service id + one must define the type of traffic to be redirected; this is done + using the wccp2_service_info option. + + The "standard" type does not require a wccp2_service_info option, + just specifying the service id will suffice. + + MD5 service authentication can be enabled by adding + "password=" to the end of this service declaration. + + Examples: + + wccp2_service standard 0 # for the 'web-cache' standard service + wccp2_service dynamic 80 # a dynamic service type which will be + # fleshed out with subsequent options. + wccp2_service standard 0 password=foo + +DOC_END + +NAME: wccp2_service_info +TYPE: wccp2_service_info +LOC: Config.Wccp2.info +DEFAULT: none +IFDEF: USE_WCCPv2 +DOC_START + Dynamic WCCPv2 services require further information to define the + traffic you wish to have diverted. + + The format is: + + wccp2_service_info protocol= flags=,.. + priority= ports=,.. + + The relevant WCCPv2 flags: + + src_ip_hash, dst_ip_hash + + source_port_hash, dest_port_hash + + src_ip_alt_hash, dst_ip_alt_hash + + src_port_alt_hash, dst_port_alt_hash + + ports_source + + The port list can be one to eight entries. + + Example: + + wccp2_service_info 80 protocol=tcp flags=src_ip_hash,ports_source + priority=240 ports=80 + + Note: the service id must have been defined by a previous + 'wccp2_service dynamic ' entry. +DOC_END + +NAME: wccp2_weight +TYPE: int +LOC: Config.Wccp2.weight +DEFAULT: 10000 +IFDEF: USE_WCCPv2 +DOC_START + Each cache server gets assigned a set of the destination + hash proportional to their weight. +DOC_END + +NAME: wccp_address +TYPE: address +LOC: Config.Wccp.address +DEFAULT: 0.0.0.0 +IFDEF: USE_WCCP +DOC_NONE +NAME: wccp2_address +TYPE: address +LOC: Config.Wccp2.address +DEFAULT: 0.0.0.0 +IFDEF: USE_WCCPv2 +DOC_START + Use this option if you require WCCP to use a specific + interface address. + + The default behavior is to not bind to any specific address. +DOC_END + + +COMMENT_START + DELAY POOL PARAMETERS (all require DELAY_POOLS compilation option) + ----------------------------------------------------------------------------- +COMMENT_END + +NAME: delay_pools +TYPE: delay_pool_count +DEFAULT: 0 +IFDEF: DELAY_POOLS +LOC: Config.Delay +DOC_START + This represents the number of delay pools to be used. For example, + if you have one class 2 delay pool and one class 3 delays pool, you + have a total of 2 delay pools. +DOC_END + +NAME: delay_class +TYPE: delay_pool_class +DEFAULT: none +IFDEF: DELAY_POOLS +LOC: Config.Delay +DOC_START + This defines the class of each delay pool. There must be exactly one + delay_class line for each delay pool. For example, to define two + delay pools, one of class 2 and one of class 3, the settings above + and here would be: + +Example: + delay_pools 2 # 2 delay pools + delay_class 1 2 # pool 1 is a class 2 pool + delay_class 2 3 # pool 2 is a class 3 pool + + The delay pool classes are: + + class 1 Everything is limited by a single aggregate + bucket. + + class 2 Everything is limited by a single aggregate + bucket as well as an "individual" bucket chosen + from bits 25 through 32 of the IP address. + + class 3 Everything is limited by a single aggregate + bucket as well as a "network" bucket chosen + from bits 17 through 24 of the IP address and a + "individual" bucket chosen from bits 17 through + 32 of the IP address. + + NOTE: If an IP address is a.b.c.d + -> bits 25 through 32 are "d" + -> bits 17 through 24 are "c" + -> bits 17 through 32 are "c * 256 + d" +DOC_END + +NAME: delay_access +TYPE: delay_pool_access +DEFAULT: none +IFDEF: DELAY_POOLS +LOC: Config.Delay +DOC_START + This is used to determine which delay pool a request falls into. + + delay_access is sorted per pool and the matching starts with pool 1, + then pool 2, ..., and finally pool N. The first delay pool where the + request is allowed is selected for the request. If it does not allow + the request to any pool then the request is not delayed (default). + + For example, if you want some_big_clients in delay + pool 1 and lotsa_little_clients in delay pool 2: + +Example: + delay_access 1 allow some_big_clients + delay_access 1 deny all + delay_access 2 allow lotsa_little_clients + delay_access 2 deny all +DOC_END + +NAME: delay_parameters +TYPE: delay_pool_rates +DEFAULT: none +IFDEF: DELAY_POOLS +LOC: Config.Delay +DOC_START + This defines the parameters for a delay pool. Each delay pool has + a number of "buckets" associated with it, as explained in the + description of delay_class. For a class 1 delay pool, the syntax is: + +delay_parameters pool aggregate + + For a class 2 delay pool: + +delay_parameters pool aggregate individual + + For a class 3 delay pool: + +delay_parameters pool aggregate network individual + + The variables here are: + + pool a pool number - ie, a number between 1 and the + number specified in delay_pools as used in + delay_class lines. + + aggregate the "delay parameters" for the aggregate bucket + (class 1, 2, 3). + + individual the "delay parameters" for the individual + buckets (class 2, 3). + + network the "delay parameters" for the network buckets + (class 3). + + A pair of delay parameters is written restore/maximum, where restore is + the number of bytes (not bits - modem and network speeds are usually + quoted in bits) per second placed into the bucket, and maximum is the + maximum number of bytes which can be in the bucket at any time. + + For example, if delay pool number 1 is a class 2 delay pool as in the + above example, and is being used to strictly limit each host to 64kbps + (plus overheads), with no overall limit, the line is: + +delay_parameters 1 -1/-1 8000/8000 + + Note that the figure -1 is used to represent "unlimited". + + And, if delay pool number 2 is a class 3 delay pool as in the above + example, and you want to limit it to a total of 256kbps (strict limit) + with each 8-bit network permitted 64kbps (strict limit) and each + individual host permitted 4800bps with a bucket maximum size of 64kb + to permit a decent web page to be downloaded at a decent speed + (if the network is not being limited due to overuse) but slow down + large downloads more significantly: + +delay_parameters 2 32000/32000 8000/8000 600/8000 + + There must be one delay_parameters line for each delay pool. +DOC_END + +NAME: delay_initial_bucket_level +COMMENT: (percent, 0-100) +TYPE: ushort +DEFAULT: 50 +IFDEF: DELAY_POOLS +LOC: Config.Delay.initial +DOC_START + The initial bucket percentage is used to determine how much is put + in each bucket when squid starts, is reconfigured, or first notices + a host accessing it (in class 2 and class 3, individual hosts and + networks only have buckets associated with them once they have been + "seen" by squid). +DOC_END + +NAME: incoming_icp_average +TYPE: int +DEFAULT: 6 +LOC: Config.comm_incoming.icp_average +DOC_NONE + +NAME: incoming_http_average +TYPE: int +DEFAULT: 4 +LOC: Config.comm_incoming.http_average +DOC_NONE + +NAME: incoming_dns_average +TYPE: int +DEFAULT: 4 +LOC: Config.comm_incoming.dns_average +DOC_NONE + +NAME: min_icp_poll_cnt +TYPE: int +DEFAULT: 8 +LOC: Config.comm_incoming.icp_min_poll +DOC_NONE + +NAME: min_dns_poll_cnt +TYPE: int +DEFAULT: 8 +LOC: Config.comm_incoming.dns_min_poll +DOC_NONE + +NAME: min_http_poll_cnt +TYPE: int +DEFAULT: 8 +LOC: Config.comm_incoming.http_min_poll +DOC_START + Heavy voodoo here. I can't even believe you are reading this. + Are you crazy? Don't even think about adjusting these unless + you understand the algorithms in comm_select.c first! +DOC_END + +NAME: max_open_disk_fds +TYPE: int +LOC: Config.max_open_disk_fds +DEFAULT: 0 +DOC_START + To avoid having disk as the I/O bottleneck Squid can optionally + bypass the on-disk cache if more than this amount of disk file + descriptors are open. + + A value of 0 indicates no limit. +DOC_END + +NAME: offline_mode +TYPE: onoff +LOC: Config.onoff.offline +DEFAULT: off +DOC_START + Enable this option and Squid will never try to validate cached + objects. +DOC_END + +NAME: uri_whitespace +TYPE: uri_whitespace +LOC: Config.uri_whitespace +DEFAULT: strip +DOC_START + What to do with requests that have whitespace characters in the + URI. Options: + + strip: The whitespace characters are stripped out of the URL. + This is the behavior recommended by RFC2396. + deny: The request is denied. The user receives an "Invalid + Request" message. + allow: The request is allowed and the URI is not changed. The + whitespace characters remain in the URI. Note the + whitespace is passed to redirector processes if they + are in use. + encode: The request is allowed and the whitespace characters are + encoded according to RFC1738. This could be considered + a violation of the HTTP/1.1 + RFC because proxies are not allowed to rewrite URI's. + chop: The request is allowed and the URI is chopped at the + first whitespace. This might also be considered a + violation. +DOC_END + +NAME: broken_posts +TYPE: acl_access +DEFAULT: none +LOC: Config.accessList.brokenPosts +DOC_START + A list of ACL elements which, if matched, causes Squid to send + an extra CRLF pair after the body of a PUT/POST request. + + Some HTTP servers has broken implementations of PUT/POST, + and rely on an extra CRLF pair sent by some WWW clients. + + Quote from RFC2068 section 4.1 on this matter: + + Note: certain buggy HTTP/1.0 client implementations generate an + extra CRLF's after a POST request. To restate what is explicitly + forbidden by the BNF, an HTTP/1.1 client must not preface or follow + a request with an extra CRLF. + +Example: + acl buggy_server url_regex ^http://.... + broken_posts allow buggy_server +DOC_END + +NAME: mcast_miss_addr +IFDEF: MULTICAST_MISS_STREAM +TYPE: address +LOC: Config.mcast_miss.addr +DEFAULT: 255.255.255.255 +DOC_START + If you enable this option, every "cache miss" URL will + be sent out on the specified multicast address. + + Do not enable this option unless you are are absolutely + certain you understand what you are doing. +DOC_END + +NAME: mcast_miss_ttl +IFDEF: MULTICAST_MISS_STREAM +TYPE: ushort +LOC: Config.mcast_miss.ttl +DEFAULT: 16 +DOC_START + This is the time-to-live value for packets multicasted + when multicasting off cache miss URLs is enabled. By + default this is set to 'site scope', i.e. 16. +DOC_END + +NAME: mcast_miss_port +IFDEF: MULTICAST_MISS_STREAM +TYPE: ushort +LOC: Config.mcast_miss.port +DEFAULT: 3135 +DOC_START + This is the port number to be used in conjunction with + 'mcast_miss_addr'. +DOC_END + +NAME: mcast_miss_encode_key +IFDEF: MULTICAST_MISS_STREAM +TYPE: string +LOC: Config.mcast_miss.encode_key +DEFAULT: XXXXXXXXXXXXXXXX +DOC_START + The URLs that are sent in the multicast miss stream are + encrypted. This is the encryption key. +DOC_END + +NAME: nonhierarchical_direct +TYPE: onoff +LOC: Config.onoff.nonhierarchical_direct +DEFAULT: on +DOC_START + By default, Squid will send any non-hierarchical requests + (matching hierarchy_stoplist or not cacheable request type) direct + to origin servers. + + If you set this to off, Squid will prefer to send these + requests to parents. + + Note that in most configurations, by turning this off you will only + add latency to these request without any improvement in global hit + ratio. + + If you are inside an firewall see never_direct instead of + this directive. +DOC_END + +NAME: prefer_direct +TYPE: onoff +LOC: Config.onoff.prefer_direct +DEFAULT: off +DOC_START + Normally Squid tries to use parents for most requests. If you for some + reason like it to first try going direct and only use a parent if + going direct fails set this to on. + + By combining nonhierarchical_direct off and prefer_direct on you + can set up Squid to use a parent as a backup path if going direct + fails. + + Note: If you want Squid to use parents for all requests see + the never_direct directive. prefer_direct only modifies how Squid + acts on cacheable requests. +DOC_END + +NAME: strip_query_terms +TYPE: onoff +LOC: Config.onoff.strip_query_terms +DEFAULT: on +DOC_START + By default, Squid strips query terms from requested URLs before + logging. This protects your user's privacy. +DOC_END + +NAME: coredump_dir +TYPE: string +LOC: Config.coredump_dir +DEFAULT: none +DEFAULT_IF_NONE: none +DOC_START + By default Squid leaves core files in the directory from where + it was started. If you set 'coredump_dir' to a directory + that exists, Squid will chdir() to that directory at startup + and coredump files will be left there. + +NOCOMMENT_START +# Leave coredumps in the first cache dir +coredump_dir @DEFAULT_SWAP_DIR@ +NOCOMMENT_END +DOC_END + +NAME: redirector_bypass +TYPE: onoff +LOC: Config.onoff.redirector_bypass +DEFAULT: off +DOC_START + When this is 'on', a request will not go through the + redirector if all redirectors are busy. If this is 'off' + and the redirector queue grows too large, Squid will exit + with a FATAL error and ask you to increase the number of + redirectors. You should only enable this if the redirectors + are not critical to your caching system. If you use + redirectors for access control, and you enable this option, + users may have access to pages they should not + be allowed to request. +DOC_END + +NAME: ignore_unknown_nameservers +TYPE: onoff +LOC: Config.onoff.ignore_unknown_nameservers +DEFAULT: on +DOC_START + By default Squid checks that DNS responses are received + from the same IP addresses they are sent to. If they + don't match, Squid ignores the response and writes a warning + message to cache.log. You can allow responses from unknown + nameservers by setting this option to 'off'. +DOC_END + +NAME: digest_generation +IFDEF: USE_CACHE_DIGESTS +TYPE: onoff +LOC: Config.onoff.digest_generation +DEFAULT: on +DOC_START + This controls whether the server will generate a Cache Digest + of its contents. By default, Cache Digest generation is + enabled if Squid is compiled with USE_CACHE_DIGESTS defined. +DOC_END + +NAME: digest_bits_per_entry +IFDEF: USE_CACHE_DIGESTS +TYPE: int +LOC: Config.digest.bits_per_entry +DEFAULT: 5 +DOC_START + This is the number of bits of the server's Cache Digest which + will be associated with the Digest entry for a given HTTP + Method and URL (public key) combination. The default is 5. +DOC_END + +NAME: digest_rebuild_period +IFDEF: USE_CACHE_DIGESTS +COMMENT: (seconds) +TYPE: time_t +LOC: Config.digest.rebuild_period +DEFAULT: 1 hour +DOC_START + This is the number of seconds between Cache Digest rebuilds. +DOC_END + +NAME: digest_rewrite_period +COMMENT: (seconds) +IFDEF: USE_CACHE_DIGESTS +TYPE: time_t +LOC: Config.digest.rewrite_period +DEFAULT: 1 hour +DOC_START + This is the number of seconds between Cache Digest writes to + disk. +DOC_END + +NAME: digest_swapout_chunk_size +COMMENT: (bytes) +TYPE: b_size_t +IFDEF: USE_CACHE_DIGESTS +LOC: Config.digest.swapout_chunk_size +DEFAULT: 4096 bytes +DOC_START + This is the number of bytes of the Cache Digest to write to + disk at a time. It defaults to 4096 bytes (4KB), the Squid + default swap page. +DOC_END + +NAME: digest_rebuild_chunk_percentage +COMMENT: (percent, 0-100) +IFDEF: USE_CACHE_DIGESTS +TYPE: int +LOC: Config.digest.rebuild_chunk_percentage +DEFAULT: 10 +DOC_START + This is the percentage of the Cache Digest to be scanned at a + time. By default it is set to 10% of the Cache Digest. +DOC_END + +NAME: chroot +TYPE: string +LOC: Config.chroot_dir +DEFAULT: none +DOC_START + Use this to have Squid do a chroot() while initializing. This + also causes Squid to fully drop root privileges after + initializing. This means, for example, that if you use a HTTP + port less than 1024 and try to reconfigure, you will get an + error. +DOC_END + +NAME: client_persistent_connections +TYPE: onoff +LOC: Config.onoff.client_pconns +DEFAULT: on +DOC_NONE + +NAME: server_persistent_connections +TYPE: onoff +LOC: Config.onoff.server_pconns +DEFAULT: on +DOC_START + Persistent connection support for clients and servers. By + default, Squid uses persistent connections (when allowed) + with its clients and servers. You can use these options to + disable persistent connections with clients and/or servers. +DOC_END + +NAME: persistent_connection_after_error +TYPE: onoff +LOC: Config.onoff.error_pconns +DEFAULT: off +DOC_START + With this directive the use of persistent connections after + HTTP errors can be disabled. Useful if you have clients + who fail to handle errors on persistent connections proper. +DOC_END + +NAME: detect_broken_pconn +TYPE: onoff +LOC: Config.onoff.detect_broken_server_pconns +DEFAULT: off +DOC_START + Some servers have been found to incorrectly signal the use + of HTTP/1.0 persistent connections even on replies not + compatible, causing significant delays. This server problem + has mostly been seen on redirects. + + By enabling this directive Squid attempts to detect such + broken replies and automatically assume the reply is finished + after 10 seconds timeout. +DOC_END + +NAME: balance_on_multiple_ip +TYPE: onoff +LOC: Config.onoff.balance_on_multiple_ip +DEFAULT: on +DOC_START + Some load balancing servers based on round robin DNS have been + found not to preserve user session state across requests + to different IP addresses. + + By default Squid rotates IP's per request. By disabling + this directive only connection failure triggers rotation. +DOC_END + +NAME: pipeline_prefetch +TYPE: onoff +LOC: Config.onoff.pipeline_prefetch +DEFAULT: off +DOC_START + To boost the performance of pipelined requests to closer + match that of a non-proxied environment Squid can try to fetch + up to two requests in parallel from a pipeline. + + Defaults to off for bandwidth management and access logging + reasons. +DOC_END + +NAME: extension_methods +TYPE: extension_method +LOC: RequestMethodStr +DEFAULT: none +DOC_START + Squid only knows about standardized HTTP request methods. + You can add up to 20 additional "extension" methods here. +DOC_END + +NAME: request_entities +TYPE: onoff +LOC: Config.onoff.request_entities +DEFAULT: off +DOC_START + Squid defaults to deny GET and HEAD requests with request entities, + as the meaning of such requests are undefined in the HTTP standard + even if not explicitly forbidden. + + Set this directive to on if you have clients which insists + on sending request entities in GET or HEAD requests. But be warned + that there is server software (both proxies and web servers) which + can fail to properly process this kind of request which may make you + vulnerable to cache pollution attacks if enabled. +DOC_END + +NAME: high_response_time_warning +TYPE: int +COMMENT: (msec) +LOC: Config.warnings.high_rptm +DEFAULT: 0 +DOC_START + If the one-minute median response time exceeds this value, + Squid prints a WARNING with debug level 0 to get the + administrators attention. The value is in milliseconds. +DOC_END + +NAME: high_page_fault_warning +TYPE: int +LOC: Config.warnings.high_pf +DEFAULT: 0 +DOC_START + If the one-minute average page fault rate exceeds this + value, Squid prints a WARNING with debug level 0 to get + the administrators attention. The value is in page faults + per second. +DOC_END + +NAME: high_memory_warning +TYPE: b_size_t +LOC: Config.warnings.high_memory +DEFAULT: 0 +DOC_START + If the memory usage (as determined by mallinfo) exceeds + value, Squid prints a WARNING with debug level 0 to get + the administrators attention. +DOC_END + +NAME: store_dir_select_algorithm +TYPE: string +LOC: Config.store_dir_select_algorithm +DEFAULT: least-load +DOC_START + Set this to 'round-robin' as an alternative. +DOC_END + +NAME: forward_log +IFDEF: WIP_FWD_LOG +TYPE: string +DEFAULT: none +LOC: Config.Log.forward +DOC_START + Logs the server-side requests. + + This is currently work in progress. +DOC_END + +NAME: ie_refresh +COMMENT: on|off +TYPE: onoff +LOC: Config.onoff.ie_refresh +DEFAULT: off +DOC_START + Microsoft Internet Explorer up until version 5.5 Service + Pack 1 has an issue with transparent proxies, wherein it + is impossible to force a refresh. Turning this on provides + a partial fix to the problem, by causing all IMS-REFRESH + requests from older IE versions to check the origin server + for fresh content. This reduces hit ratio by some amount + (~10% in my experience), but allows users to actually get + fresh content when they want it. Note that because Squid + cannot tell if the user is using 5.5 or 5.5SP1, the behavior + of 5.5 is unchanged from old versions of Squid (i.e. a + forced refresh is impossible). Newer versions of IE will, + hopefully, continue to have the new behavior and will be + handled based on that assumption. This option defaults to + the old Squid behavior, which is better for hit ratios but + worse for clients using IE, if they need to be able to + force fresh content. +DOC_END + +NAME: vary_ignore_expire +COMMENT: on|off +TYPE: onoff +LOC: Config.onoff.vary_ignore_expire +DEFAULT: off +DOC_START + Many HTTP servers supporting Vary gives such objects + immediate expiry time with no cache-control header + when requested by a HTTP/1.0 client. This option + enables Squid to ignore such expiry times until + HTTP/1.1 is fully implemented. + WARNING: This may eventually cause some varying + objects not intended for caching to get cached. +DOC_END + +NAME: sleep_after_fork +COMMENT: (microseconds) +TYPE: int +LOC: Config.sleep_after_fork +DEFAULT: 0 +DOC_START + When this is set to a non-zero value, the main Squid process + sleeps the specified number of microseconds after a fork() + system call. This sleep may help the situation where your + system reports fork() failures due to lack of (virtual) + memory. Note, however, that if you have a lot of child + processes, these sleep delays will add up and your + Squid will not service requests for some amount of time + until all the child processes have been started. + On Windows value less then 1000 (1 milliseconds) are + rounded to 1000. +DOC_END + +NAME: minimum_expiry_time +COMMENT: (seconds) +TYPE: time_t +LOC: Config.minimum_expiry_time +DEFAULT: 60 seconds +DOC_START + The minimum caching time according to (Expires - Date) + Headers Squid honors if the object can't be revalidated + defaults to 60 seconds. In reverse proxy enorinments it + might be desirable to honor shorter object lifetimes. It + is most likely better to make your server return a + meaningful Last-Modified header however. +DOC_END + +NAME: relaxed_header_parser +COMMENT: on|off|warn +TYPE: tristate +LOC: Config.onoff.relaxed_header_parser +DEFAULT: on +DOC_START + In the default "on" setting Squid accepts certain forms + of non-compliant HTTP messages where it is unambiguous + what the sending application intended even if the message + is not correctly formatted. The messages is then normalized + to the correct form when forwarded by Squid. + + If set to "warn" then a warning will be emitted in cache.log + each time such HTTP error is encountered. + + If set to "off" then such HTTP errors will cause the request + or response to be rejected. +DOC_END + +EOF diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/f7/f0fca1315c88001b16add8656805b17d squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/f7/f0fca1315c88001b16add8656805b17d --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/f7/f0fca1315c88001b16add8656805b17d 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.history/f7/f0fca1315c88001b16add8656805b17d 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,29 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/repl/lru/store_repl_lru.c + +OBJS += \ +./repl/lru/store_repl_lru.o + +DEPS += \ +${addprefix ./repl/lru/, \ +store_repl_lru.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +repl/lru/%.o: $(ROOT)/repl/lru/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/6f/af/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/6f/af/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/6f/ec/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/6f/ec/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/a8/2e/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/a8/2e/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/a8/3c/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/a8/3c/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/a8/82/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/a8/82/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/a8/c7/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/a8/c7/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/cd/1/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/cd/1/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/cd/2/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/cd/2/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/cd/67/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/cd/67/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/cd/87/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/cd/87/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/cd/cc/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/cd/cc/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/33/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/af/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/af/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/history.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/history.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/properties.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.indexes/properties.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.markers.snap and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.markers.snap differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.syncinfo.snap and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.projects/src/.syncinfo.snap differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.root/.markers.snap and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.root/.markers.snap differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources differ Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.snap and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.resources/.snap differ diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.debug.core.prefs squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.debug.core.prefs --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.debug.core.prefs 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.debug.core.prefs 2006-12-10 16:11:20.000000000 +0200 @@ -0,0 +1,3 @@ +#Sun Dec 10 16:11:20 IST 2006 +org.eclipse.cdt.debug.core.cDebug.common_source_containers=\n\n\n\n +eclipse.preferences.version=1 diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs 2006-12-10 16:45:02.000000000 +0200 @@ -0,0 +1,4 @@ +#Sun Dec 10 16:45:02 IST 2006 +version=1 +eclipse.preferences.version=1 +description.autobuilding=false diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs 2006-12-10 16:10:23.000000000 +0200 @@ -0,0 +1,3 @@ +#Sun Dec 10 16:10:23 IST 2006 +eclipse.preferences.version=1 +showIntro=false diff -ruN squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.wst.server.core.prefs squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.wst.server.core.prefs --- squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.wst.server.core.prefs 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.wst.server.core.prefs 2006-12-10 16:10:19.000000000 +0200 @@ -0,0 +1,3 @@ +#Sun Dec 10 16:10:19 IST 2006 +module-start-timeout=300000 +eclipse.preferences.version=1 Binary files squid-2.6.STABLE5/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat and squid-icap-2.6.STABLE5/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat differ diff -ruN squid-2.6.STABLE5/.metadata/version.ini squid-icap-2.6.STABLE5/.metadata/version.ini --- squid-2.6.STABLE5/.metadata/version.ini 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/.metadata/version.ini 2006-12-10 16:10:13.000000000 +0200 @@ -0,0 +1 @@ +org.eclipse.core.runtime=1 \ No newline at end of file diff -ruN squid-2.6.STABLE5/PSMakefile squid-icap-2.6.STABLE5/PSMakefile --- squid-2.6.STABLE5/PSMakefile 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/PSMakefile 2006-12-10 18:13:17.000000000 +0200 @@ -0,0 +1,6 @@ +#!/bin/sh + +cat PURE_SIGHT_CONFIGURE_PARAMS | xargs ./configure +make +strip src/squid + diff -ruN squid-2.6.STABLE5/PURE_SIGHT_CONFIGURE_PARAMS squid-icap-2.6.STABLE5/PURE_SIGHT_CONFIGURE_PARAMS --- squid-2.6.STABLE5/PURE_SIGHT_CONFIGURE_PARAMS 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/PURE_SIGHT_CONFIGURE_PARAMS 2006-12-10 18:14:21.000000000 +0200 @@ -0,0 +1 @@ +'--host=i686-pc-linux-gnu' '--build=i686-pc-linux-gnu' '--target=i386-redhat-linux-gnu' '--program-prefix=' '--prefix=/usr' '--exec-prefix=/usr' '--bindir=/usr/bin' '--sbindir=/usr/sbin' '--sysconfdir=/etc' '--datadir=/usr/share' '--includedir=/usr/include' '--libdir=/usr/lib' '--libexecdir=/usr/libexec' '--sharedstatedir=/usr/com' '--mandir=/usr/share/man' '--infodir=/usr/share/info' '--exec_prefix=/usr' '--bindir=/usr/sbin' '--libexecdir=/usr/lib/squid' '--localstatedir=/var' '--sysconfdir=/etc/squid' '--enable-poll' '--enable-snmp' '--enable-removal-policies=heap,lru' '--enable-storeio=aufs,coss,diskd,null,ufs' '--enable-ssl' '--with-openssl=/usr/kerberos' '--enable-delay-pools' '--enable-linux-netfilter' '--with-pthreads' '--enable-basic-auth-helpers=LDAP,NCSA,PAM,SMB,SASL,MSNT' '--enable-ntlm-auth-helpers=SMB,fakeauth' '--enable-external-acl-helpers=ip_user,ldap_group,unix_group,wbinfo_group' '--enable-auth=basic,ntlm' '--enable-useragent-log' '--enable-referer-log' '--enable-icap-support' 'build_alias=i686-pc-linux-gnu' 'host_alias=i686-pc-linux-gnu' 'target_alias=i386-redhat-linux-gnu' diff -ruN squid-2.6.STABLE5/src/.cdtbuild squid-icap-2.6.STABLE5/src/.cdtbuild --- squid-2.6.STABLE5/src/.cdtbuild 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/.cdtbuild 2006-12-10 16:10:49.000000000 +0200 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff -ruN squid-2.6.STABLE5/src/.cdtproject squid-icap-2.6.STABLE5/src/.cdtproject --- squid-2.6.STABLE5/src/.cdtproject 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/.cdtproject 2006-12-10 16:45:24.000000000 +0200 @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff -ruN squid-2.6.STABLE5/src/cf.data.pre squid-icap-2.6.STABLE5/src/cf.data.pre --- squid-2.6.STABLE5/src/cf.data.pre 2006-12-10 18:50:18.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/cf.data.pre 2006-12-10 18:07:07.000000000 +0200 @@ -3632,6 +3632,55 @@ icap_access class_1 allow all DOC_END +NAME: icap_req_mod_direct_reply +TYPE: onoff +IFDEF: HS_FEAT_ICAP +COMMENT: on|off +LOC: Config.icapcfg.icap_req_mod_direct_reply +DEFAULT: off +DOC_START + classification based direct reply. + ICAP Server reply at request mod contains classification that + will define if the origin server reply should be send directly + to the client. + Usually used in content filtering, in case that classification is known + at the request mod, and there is no need to recheck at resp mod. +DOC_END + +NAME: icap_req_mod_direct_reply_resp_info_tag_name +TYPE: string +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg.resp_info_tag_name +DEFAULT: X-Response-Info +DOC_START + req mod reply classification tag name +DOC_END + +NAME: icap_req_mod_direct_reply_values +TYPE: wordlist +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg.icap_req_mod_direct_reply_values +DEFAULT: ii_ALLOW_ALLOW_ii +DOC_START + the values that couse a direct reply, + in case of a contant filter it will be any value, + that marks that the requested URL is valid, at + request mod. + in some cases you may know that in the request stage. +DOC_END + +NAME: icap_session_context_tag_name +TYPE: string +IFDEF: HS_FEAT_ICAP +LOC: Config.icapcfg.icap_session_context_tag_name +DEFAULT: ICAP-Session-Info +DOC_START + a string that will be passed in the response, + with the same tag name. +Example: + icap_session_context_tag_name ICAP-Session-Info +DOC_END + COMMENT_START MISCELLANEOUS ----------------------------------------------------------------------------- diff -ruN squid-2.6.STABLE5/src/Debug/auth/basic/subdir.mk squid-icap-2.6.STABLE5/src/Debug/auth/basic/subdir.mk --- squid-2.6.STABLE5/src/Debug/auth/basic/subdir.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/auth/basic/subdir.mk 2006-12-10 16:39:09.000000000 +0200 @@ -0,0 +1,29 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/auth/basic/auth_basic.c + +OBJS += \ +./auth/basic/auth_basic.o + +DEPS += \ +${addprefix ./auth/basic/, \ +auth_basic.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +auth/basic/%.o: $(ROOT)/auth/basic/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/src/Debug/auth/digest/subdir.mk squid-icap-2.6.STABLE5/src/Debug/auth/digest/subdir.mk --- squid-2.6.STABLE5/src/Debug/auth/digest/subdir.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/auth/digest/subdir.mk 2006-12-10 16:39:09.000000000 +0200 @@ -0,0 +1,29 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/auth/digest/auth_digest.c + +OBJS += \ +./auth/digest/auth_digest.o + +DEPS += \ +${addprefix ./auth/digest/, \ +auth_digest.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +auth/digest/%.o: $(ROOT)/auth/digest/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/src/Debug/auth/negotiate/subdir.mk squid-icap-2.6.STABLE5/src/Debug/auth/negotiate/subdir.mk --- squid-2.6.STABLE5/src/Debug/auth/negotiate/subdir.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/auth/negotiate/subdir.mk 2006-12-10 16:39:09.000000000 +0200 @@ -0,0 +1,29 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/auth/negotiate/auth_negotiate.c + +OBJS += \ +./auth/negotiate/auth_negotiate.o + +DEPS += \ +${addprefix ./auth/negotiate/, \ +auth_negotiate.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +auth/negotiate/%.o: $(ROOT)/auth/negotiate/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/src/Debug/auth/ntlm/subdir.mk squid-icap-2.6.STABLE5/src/Debug/auth/ntlm/subdir.mk --- squid-2.6.STABLE5/src/Debug/auth/ntlm/subdir.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/auth/ntlm/subdir.mk 2006-12-10 16:39:09.000000000 +0200 @@ -0,0 +1,29 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/auth/ntlm/auth_ntlm.c + +OBJS += \ +./auth/ntlm/auth_ntlm.o + +DEPS += \ +${addprefix ./auth/ntlm/, \ +auth_ntlm.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +auth/ntlm/%.o: $(ROOT)/auth/ntlm/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/src/Debug/fs/aufs/subdir.mk squid-icap-2.6.STABLE5/src/Debug/fs/aufs/subdir.mk --- squid-2.6.STABLE5/src/Debug/fs/aufs/subdir.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/fs/aufs/subdir.mk 2006-12-10 16:39:09.000000000 +0200 @@ -0,0 +1,41 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/fs/aufs/aiops.c \ +$(ROOT)/fs/aufs/aiops_win32.c \ +$(ROOT)/fs/aufs/async_io.c \ +$(ROOT)/fs/aufs/store_dir_aufs.c \ +$(ROOT)/fs/aufs/store_io_aufs.c + +OBJS += \ +./fs/aufs/aiops.o \ +./fs/aufs/aiops_win32.o \ +./fs/aufs/async_io.o \ +./fs/aufs/store_dir_aufs.o \ +./fs/aufs/store_io_aufs.o + +DEPS += \ +${addprefix ./fs/aufs/, \ +aiops.d \ +aiops_win32.d \ +async_io.d \ +store_dir_aufs.d \ +store_io_aufs.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +fs/aufs/%.o: $(ROOT)/fs/aufs/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/src/Debug/fs/coss/subdir.mk squid-icap-2.6.STABLE5/src/Debug/fs/coss/subdir.mk --- squid-2.6.STABLE5/src/Debug/fs/coss/subdir.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/fs/coss/subdir.mk 2006-12-10 16:39:09.000000000 +0200 @@ -0,0 +1,35 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/fs/coss/async_io.c \ +$(ROOT)/fs/coss/store_dir_coss.c \ +$(ROOT)/fs/coss/store_io_coss.c + +OBJS += \ +./fs/coss/async_io.o \ +./fs/coss/store_dir_coss.o \ +./fs/coss/store_io_coss.o + +DEPS += \ +${addprefix ./fs/coss/, \ +async_io.d \ +store_dir_coss.d \ +store_io_coss.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +fs/coss/%.o: $(ROOT)/fs/coss/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/src/Debug/fs/diskd/subdir.mk squid-icap-2.6.STABLE5/src/Debug/fs/diskd/subdir.mk --- squid-2.6.STABLE5/src/Debug/fs/diskd/subdir.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/fs/diskd/subdir.mk 2006-12-10 16:39:09.000000000 +0200 @@ -0,0 +1,35 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/fs/diskd/diskd.c \ +$(ROOT)/fs/diskd/store_dir_diskd.c \ +$(ROOT)/fs/diskd/store_io_diskd.c + +OBJS += \ +./fs/diskd/diskd.o \ +./fs/diskd/store_dir_diskd.o \ +./fs/diskd/store_io_diskd.o + +DEPS += \ +${addprefix ./fs/diskd/, \ +diskd.d \ +store_dir_diskd.d \ +store_io_diskd.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +fs/diskd/%.o: $(ROOT)/fs/diskd/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/src/Debug/fs/null/subdir.mk squid-icap-2.6.STABLE5/src/Debug/fs/null/subdir.mk --- squid-2.6.STABLE5/src/Debug/fs/null/subdir.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/fs/null/subdir.mk 2006-12-10 16:39:09.000000000 +0200 @@ -0,0 +1,29 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/fs/null/store_null.c + +OBJS += \ +./fs/null/store_null.o + +DEPS += \ +${addprefix ./fs/null/, \ +store_null.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +fs/null/%.o: $(ROOT)/fs/null/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/src/Debug/fs/ufs/subdir.mk squid-icap-2.6.STABLE5/src/Debug/fs/ufs/subdir.mk --- squid-2.6.STABLE5/src/Debug/fs/ufs/subdir.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/fs/ufs/subdir.mk 2006-12-10 16:39:09.000000000 +0200 @@ -0,0 +1,32 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/fs/ufs/store_dir_ufs.c \ +$(ROOT)/fs/ufs/store_io_ufs.c + +OBJS += \ +./fs/ufs/store_dir_ufs.o \ +./fs/ufs/store_io_ufs.o + +DEPS += \ +${addprefix ./fs/ufs/, \ +store_dir_ufs.d \ +store_io_ufs.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +fs/ufs/%.o: $(ROOT)/fs/ufs/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/src/Debug/makefile squid-icap-2.6.STABLE5/src/Debug/makefile --- squid-2.6.STABLE5/src/Debug/makefile 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/makefile 2006-12-10 16:39:09.000000000 +0200 @@ -0,0 +1,43 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +ROOT := .. + +-include $(ROOT)/makefile.init + +RM := rm -rf + +# All of the sources participating in the build are defined here +-include sources.mk +-include $(SUBDIRS:%=%/subdir.mk) +-include objects.mk +ifneq ($(strip $(DEPS)),) +-include $(DEPS) +endif + +-include $(ROOT)/makefile.defs + +# Add inputs and outputs from these tool invocations to the build variables + +# All Target +all: src + +# Tool invocations +src: $(OBJS) $(USER_OBJS) + @echo 'Building target: $@' + @echo 'Invoking: GCC C Linker' + @echo gcc -osrc $(OBJS) $(USER_OBJS) $(LIBS) + @gcc -osrc $(OBJS) $(USER_OBJS) $(LIBS) + @echo 'Finished building target: $@' + @echo ' ' + +# Other Targets +clean: + -$(RM) $(OBJS)$(DEPS)$(EXECUTABLES) src + -@echo ' ' + +.PHONY: all clean dependents +.SECONDARY: + +-include $(ROOT)/makefile.targets diff -ruN squid-2.6.STABLE5/src/Debug/objects.mk squid-icap-2.6.STABLE5/src/Debug/objects.mk --- squid-2.6.STABLE5/src/Debug/objects.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/objects.mk 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,7 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +LIBS := + +USER_OBJS := \ No newline at end of file diff -ruN squid-2.6.STABLE5/src/Debug/repl/heap/subdir.mk squid-icap-2.6.STABLE5/src/Debug/repl/heap/subdir.mk --- squid-2.6.STABLE5/src/Debug/repl/heap/subdir.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/repl/heap/subdir.mk 2006-12-10 16:39:08.000000000 +0200 @@ -0,0 +1,32 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/repl/heap/store_heap_replacement.c \ +$(ROOT)/repl/heap/store_repl_heap.c + +OBJS += \ +./repl/heap/store_heap_replacement.o \ +./repl/heap/store_repl_heap.o + +DEPS += \ +${addprefix ./repl/heap/, \ +store_heap_replacement.d \ +store_repl_heap.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +repl/heap/%.o: $(ROOT)/repl/heap/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/src/Debug/repl/lru/subdir.mk squid-icap-2.6.STABLE5/src/Debug/repl/lru/subdir.mk --- squid-2.6.STABLE5/src/Debug/repl/lru/subdir.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/repl/lru/subdir.mk 2006-12-10 16:39:08.000000000 +0200 @@ -0,0 +1,29 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/repl/lru/store_repl_lru.c + +OBJS += \ +./repl/lru/store_repl_lru.o + +DEPS += \ +${addprefix ./repl/lru/, \ +store_repl_lru.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +repl/lru/%.o: $(ROOT)/repl/lru/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/src/Debug/sources.mk squid-icap-2.6.STABLE5/src/Debug/sources.mk --- squid-2.6.STABLE5/src/Debug/sources.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/sources.mk 2006-12-10 16:39:08.000000000 +0200 @@ -0,0 +1,28 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +C_SRCS := +O_SRCS := +ASM_SRCS := +S_SRCS := +OBJ_SRCS := +OBJS := +DEPS := +EXECUTABLES := + +# Every subdirectory with source files must be described here +SUBDIRS := \ +. \ +repl/lru \ +repl/heap \ +fs/ufs \ +fs/null \ +fs/diskd \ +fs/coss \ +fs/aufs \ +auth/ntlm \ +auth/negotiate \ +auth/digest \ +auth/basic \ + diff -ruN squid-2.6.STABLE5/src/Debug/subdir.mk squid-icap-2.6.STABLE5/src/Debug/subdir.mk --- squid-2.6.STABLE5/src/Debug/subdir.mk 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/Debug/subdir.mk 2006-12-10 16:39:08.000000000 +0200 @@ -0,0 +1,365 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +C_SRCS += \ +$(ROOT)/CacheDigest.c \ +$(ROOT)/HttpBody.c \ +$(ROOT)/HttpHdrCc.c \ +$(ROOT)/HttpHdrContRange.c \ +$(ROOT)/HttpHdrRange.c \ +$(ROOT)/HttpHeader.c \ +$(ROOT)/HttpHeaderTools.c \ +$(ROOT)/HttpMsg.c \ +$(ROOT)/HttpReply.c \ +$(ROOT)/HttpRequest.c \ +$(ROOT)/HttpStatusLine.c \ +$(ROOT)/MemBuf.c \ +$(ROOT)/MemPool.c \ +$(ROOT)/Packer.c \ +$(ROOT)/StatHist.c \ +$(ROOT)/String.c \ +$(ROOT)/access_log.c \ +$(ROOT)/acl.c \ +$(ROOT)/asn.c \ +$(ROOT)/authenticate.c \ +$(ROOT)/cache_cf.c \ +$(ROOT)/cache_manager.c \ +$(ROOT)/carp.c \ +$(ROOT)/cbdata.c \ +$(ROOT)/cf_gen.c \ +$(ROOT)/client_db.c \ +$(ROOT)/client_side.c \ +$(ROOT)/comm.c \ +$(ROOT)/comm_epoll.c \ +$(ROOT)/comm_generic.c \ +$(ROOT)/comm_kqueue.c \ +$(ROOT)/comm_poll.c \ +$(ROOT)/comm_select.c \ +$(ROOT)/comm_select_simple.c \ +$(ROOT)/comm_select_win32.c \ +$(ROOT)/debug.c \ +$(ROOT)/delay_pools.c \ +$(ROOT)/disk.c \ +$(ROOT)/dns.c \ +$(ROOT)/dns_internal.c \ +$(ROOT)/dnsserver.c \ +$(ROOT)/errormap.c \ +$(ROOT)/errorpage.c \ +$(ROOT)/event.c \ +$(ROOT)/external_acl.c \ +$(ROOT)/fd.c \ +$(ROOT)/filemap.c \ +$(ROOT)/forward.c \ +$(ROOT)/fqdncache.c \ +$(ROOT)/ftp.c \ +$(ROOT)/gopher.c \ +$(ROOT)/helper.c \ +$(ROOT)/htcp.c \ +$(ROOT)/http.c \ +$(ROOT)/icap_common.c \ +$(ROOT)/icap_opt.c \ +$(ROOT)/icap_reqmod.c \ +$(ROOT)/icap_respmod.c \ +$(ROOT)/icmp.c \ +$(ROOT)/icp_v2.c \ +$(ROOT)/icp_v3.c \ +$(ROOT)/ident.c \ +$(ROOT)/internal.c \ +$(ROOT)/ipc.c \ +$(ROOT)/ipc_win32.c \ +$(ROOT)/ipcache.c \ +$(ROOT)/leakfinder.c \ +$(ROOT)/locrewrite.c \ +$(ROOT)/logfile.c \ +$(ROOT)/main.c \ +$(ROOT)/mem.c \ +$(ROOT)/mime.c \ +$(ROOT)/multicast.c \ +$(ROOT)/neighbors.c \ +$(ROOT)/net_db.c \ +$(ROOT)/pconn.c \ +$(ROOT)/peer_digest.c \ +$(ROOT)/peer_monitor.c \ +$(ROOT)/peer_select.c \ +$(ROOT)/peer_sourcehash.c \ +$(ROOT)/peer_userhash.c \ +$(ROOT)/pinger.c \ +$(ROOT)/redirect.c \ +$(ROOT)/referer.c \ +$(ROOT)/refresh.c \ +$(ROOT)/send-announce.c \ +$(ROOT)/snmp_agent.c \ +$(ROOT)/snmp_core.c \ +$(ROOT)/ssl.c \ +$(ROOT)/ssl_support.c \ +$(ROOT)/stat.c \ +$(ROOT)/stmem.c \ +$(ROOT)/store.c \ +$(ROOT)/store_client.c \ +$(ROOT)/store_digest.c \ +$(ROOT)/store_dir.c \ +$(ROOT)/store_io.c \ +$(ROOT)/store_key_md5.c \ +$(ROOT)/store_log.c \ +$(ROOT)/store_rebuild.c \ +$(ROOT)/store_swapin.c \ +$(ROOT)/store_swapmeta.c \ +$(ROOT)/store_swapout.c \ +$(ROOT)/tools.c \ +$(ROOT)/unlinkd.c \ +$(ROOT)/url.c \ +$(ROOT)/urn.c \ +$(ROOT)/useragent.c \ +$(ROOT)/wais.c \ +$(ROOT)/wccp.c \ +$(ROOT)/wccp2.c \ +$(ROOT)/whois.c \ +$(ROOT)/win32.c + +OBJS += \ +./CacheDigest.o \ +./HttpBody.o \ +./HttpHdrCc.o \ +./HttpHdrContRange.o \ +./HttpHdrRange.o \ +./HttpHeader.o \ +./HttpHeaderTools.o \ +./HttpMsg.o \ +./HttpReply.o \ +./HttpRequest.o \ +./HttpStatusLine.o \ +./MemBuf.o \ +./MemPool.o \ +./Packer.o \ +./StatHist.o \ +./String.o \ +./access_log.o \ +./acl.o \ +./asn.o \ +./authenticate.o \ +./cache_cf.o \ +./cache_manager.o \ +./carp.o \ +./cbdata.o \ +./cf_gen.o \ +./client_db.o \ +./client_side.o \ +./comm.o \ +./comm_epoll.o \ +./comm_generic.o \ +./comm_kqueue.o \ +./comm_poll.o \ +./comm_select.o \ +./comm_select_simple.o \ +./comm_select_win32.o \ +./debug.o \ +./delay_pools.o \ +./disk.o \ +./dns.o \ +./dns_internal.o \ +./dnsserver.o \ +./errormap.o \ +./errorpage.o \ +./event.o \ +./external_acl.o \ +./fd.o \ +./filemap.o \ +./forward.o \ +./fqdncache.o \ +./ftp.o \ +./gopher.o \ +./helper.o \ +./htcp.o \ +./http.o \ +./icap_common.o \ +./icap_opt.o \ +./icap_reqmod.o \ +./icap_respmod.o \ +./icmp.o \ +./icp_v2.o \ +./icp_v3.o \ +./ident.o \ +./internal.o \ +./ipc.o \ +./ipc_win32.o \ +./ipcache.o \ +./leakfinder.o \ +./locrewrite.o \ +./logfile.o \ +./main.o \ +./mem.o \ +./mime.o \ +./multicast.o \ +./neighbors.o \ +./net_db.o \ +./pconn.o \ +./peer_digest.o \ +./peer_monitor.o \ +./peer_select.o \ +./peer_sourcehash.o \ +./peer_userhash.o \ +./pinger.o \ +./redirect.o \ +./referer.o \ +./refresh.o \ +./send-announce.o \ +./snmp_agent.o \ +./snmp_core.o \ +./ssl.o \ +./ssl_support.o \ +./stat.o \ +./stmem.o \ +./store.o \ +./store_client.o \ +./store_digest.o \ +./store_dir.o \ +./store_io.o \ +./store_key_md5.o \ +./store_log.o \ +./store_rebuild.o \ +./store_swapin.o \ +./store_swapmeta.o \ +./store_swapout.o \ +./tools.o \ +./unlinkd.o \ +./url.o \ +./urn.o \ +./useragent.o \ +./wais.o \ +./wccp.o \ +./wccp2.o \ +./whois.o \ +./win32.o + +DEPS += \ +${addprefix ./, \ +CacheDigest.d \ +HttpBody.d \ +HttpHdrCc.d \ +HttpHdrContRange.d \ +HttpHdrRange.d \ +HttpHeader.d \ +HttpHeaderTools.d \ +HttpMsg.d \ +HttpReply.d \ +HttpRequest.d \ +HttpStatusLine.d \ +MemBuf.d \ +MemPool.d \ +Packer.d \ +StatHist.d \ +String.d \ +access_log.d \ +acl.d \ +asn.d \ +authenticate.d \ +cache_cf.d \ +cache_manager.d \ +carp.d \ +cbdata.d \ +cf_gen.d \ +client_db.d \ +client_side.d \ +comm.d \ +comm_epoll.d \ +comm_generic.d \ +comm_kqueue.d \ +comm_poll.d \ +comm_select.d \ +comm_select_simple.d \ +comm_select_win32.d \ +debug.d \ +delay_pools.d \ +disk.d \ +dns.d \ +dns_internal.d \ +dnsserver.d \ +errormap.d \ +errorpage.d \ +event.d \ +external_acl.d \ +fd.d \ +filemap.d \ +forward.d \ +fqdncache.d \ +ftp.d \ +gopher.d \ +helper.d \ +htcp.d \ +http.d \ +icap_common.d \ +icap_opt.d \ +icap_reqmod.d \ +icap_respmod.d \ +icmp.d \ +icp_v2.d \ +icp_v3.d \ +ident.d \ +internal.d \ +ipc.d \ +ipc_win32.d \ +ipcache.d \ +leakfinder.d \ +locrewrite.d \ +logfile.d \ +main.d \ +mem.d \ +mime.d \ +multicast.d \ +neighbors.d \ +net_db.d \ +pconn.d \ +peer_digest.d \ +peer_monitor.d \ +peer_select.d \ +peer_sourcehash.d \ +peer_userhash.d \ +pinger.d \ +redirect.d \ +referer.d \ +refresh.d \ +send-announce.d \ +snmp_agent.d \ +snmp_core.d \ +ssl.d \ +ssl_support.d \ +stat.d \ +stmem.d \ +store.d \ +store_client.d \ +store_digest.d \ +store_dir.d \ +store_io.d \ +store_key_md5.d \ +store_log.d \ +store_rebuild.d \ +store_swapin.d \ +store_swapmeta.d \ +store_swapout.d \ +tools.d \ +unlinkd.d \ +url.d \ +urn.d \ +useragent.d \ +wais.d \ +wccp.d \ +wccp2.d \ +whois.d \ +win32.d \ +} + + +# Each subdirectory must supply rules for building sources it contributes +%.o: $(ROOT)/%.c + @echo 'Building file: $<' + @echo 'Invoking: GCC C Compiler' + @echo gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< + @gcc -O0 -g3 -Wall -c -fmessage-length=0 -o$@ $< && \ + echo -n $(@:%.o=%.d) $(dir $@) > $(@:%.o=%.d) && \ + gcc -MM -MG -P -w -O0 -g3 -Wall -c -fmessage-length=0 $< >> $(@:%.o=%.d) + @echo 'Finished building: $<' + @echo ' ' + + diff -ruN squid-2.6.STABLE5/src/http.c squid-icap-2.6.STABLE5/src/http.c --- squid-2.6.STABLE5/src/http.c 2006-12-10 18:50:18.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/http.c 2006-12-10 16:57:56.000000000 +0200 @@ -1025,7 +1025,9 @@ httpState->icap_writer = icapRespModStart( ICAP_SERVICE_RESPMOD_PRECACHE, httpState->orig_request, httpState->entry, httpState->flags); - if (-1 == (int) httpState->icap_writer) { + if (-2 == (int) httpState->icap_writer) { // no need to call respmod + httpState->icap_writer = 0; + } else if (-1 == (int) httpState->icap_writer) { /* TODO: send error here and exit */ ErrorState *err; httpState->icap_writer = 0; @@ -1566,6 +1568,10 @@ 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 == -2 ) + httpState->icap_writer = 0; + if (httpState->icap_writer) { return; } diff -ruN squid-2.6.STABLE5/src/icap_common.c squid-icap-2.6.STABLE5/src/icap_common.c --- squid-2.6.STABLE5/src/icap_common.c 2006-12-10 18:50:18.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/icap_common.c 2006-12-10 17:00:46.000000000 +0200 @@ -261,7 +261,7 @@ icap->enc.null_body = -1; icap->chunk_size = -1; memBufDefInit(&icap->icap_hdr); - + cap->respmod.icap_server_session_context = NULL; debug(81, 3) ("New ICAP state\n"); return icap; } @@ -284,6 +284,12 @@ storeUnlockObject(icap->respmod.entry); icap->respmod.entry = NULL; } + if( icap->respmod.icap_server_session_context ) + { + void *p = icap->respmod.icap_server_session_context; + xfree( p ); + icap->respmod.icap_server_session_context = NULL; + } requestUnlink(icap->request); icap->request = NULL; if (!memBufIsNull(&icap->icap_hdr)) diff -ruN squid-2.6.STABLE5/src/icap_opt.c squid-icap-2.6.STABLE5/src/icap_opt.c --- squid-2.6.STABLE5/src/icap_opt.c 2006-12-10 18:50:18.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/icap_opt.c 2006-12-10 18:09:21.000000000 +0200 @@ -517,7 +517,9 @@ icapOptDataFree(IcapOptData * i) { if (i) { + if( i->buf) { // condition added by moshe beeri, potential crash memFreeBuf(i->size, i->buf); memFree(i, MEM_ICAP_OPT_DATA); } + } } diff -ruN squid-2.6.STABLE5/src/icap_reqmod.c squid-icap-2.6.STABLE5/src/icap_reqmod.c --- squid-2.6.STABLE5/src/icap_reqmod.c 2006-12-10 18:50:18.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/icap_reqmod.c 2006-12-10 16:39:08.000000000 +0200 @@ -568,6 +568,84 @@ return; } icapSetKeepAlive(icap, icap->icap_hdr.buf); + + int icap_direct_response = 0; + + if( Config.icapcfg.icap_req_mod_direct_reply == 1 ) + { + debug(81, 5)( "PSICAP Config.icapcfg.icap_req_mod_direct_reply = 1\n" ); + debug(81, 5)( "PSICAP Config.icapcfg.resp_info_tag_name=[%s]\n", Config.icapcfg.resp_info_tag_name ); + if (icapFindHeader(icap->icap_hdr.buf, Config.icapcfg.resp_info_tag_name, &start, &end)) { + wordlist *w = NULL; + char buf[256]; + buf[end-start] = '\0'; + strncpy(buf,start, end-start); + icap_direct_response = 0; + + debug(81, 5)( "PSICAP %s was found [%s]\n",Config.icapcfg.resp_info_tag_name,buf ); + + if ((w = Config.icapcfg.icap_req_mod_direct_reply_values) != NULL) + { + for (; w; w = w->next){ + if ( w->key != NULL ){ + debug(81, 5)( "PSICAP testing key [%s]\n", w->key ); + if( strstr( buf + strlen(Config.icapcfg.resp_info_tag_name) + 1, w->key ) != NULL ){ + + debug(81, 5)( "PSICAP key found [%s]\n", w->key ); + + // we have the direct reply value, no need to check for more, just break + icap_direct_response = 1; + break; + } + } + } + debug(81, 5)( "\n"); + } + } + } + + fde *F = &fd_table[icap->reqmod.client_fd]; + + F->context_salt = CONTEXT_SALT; + + F->icap_server_session_context = NULL; + debug(81, 5)( "PSICAP icap_direct_response %d\n",icap_direct_response); + F->icap_response_info = icap_direct_response; + //set the data that need to be passed from req mod replay to resp mod request + debug(81, 5)( "PSICAP icap_conext_debug looking for tag %s \n",Config.icapcfg.icap_session_context_tag_name); + if ((F->icap_response_info != 1 ) + && + (icapFindHeader(icap->icap_hdr.buf, Config.icapcfg.icap_session_context_tag_name, &start, &end)) ) + { + debug(81, 5)( "PSICAP icap_conext_debug tag [%s] was found... \n", Config.icapcfg.icap_session_context_tag_name ); + //copy the string to fde + int info_length = end-start; + if( info_length > 0 ) + { + if( F->icap_server_session_context != NULL ) + { + xfree( F->icap_server_session_context ); + F->icap_server_session_context = NULL; + } + int tag_len = strlen( Config.icapcfg.icap_session_context_tag_name ); + int additionat_len = 2; //2=strlen( ": " ); + int context_len = info_length - tag_len - additionat_len; + if( context_len > 0 ) + { + F->icap_server_session_context = xmalloc(context_len+1); + strncpy( F->icap_server_session_context, + start + tag_len + additionat_len, + context_len ); + F->icap_server_session_context[context_len] = '\0'; + debug(81, 5)( "PSICAP icap_conext_debug context is [%s]\n",F->icap_server_session_context ); + } + + } + else + { + debug(81, 5)( "PSICAP icap_conext_debug context length is 0" ); + } + } if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { icapParseEncapsulated(icap, start, end); } else { diff -ruN squid-2.6.STABLE5/src/icap_respmod.c squid-icap-2.6.STABLE5/src/icap_respmod.c --- squid-2.6.STABLE5/src/icap_respmod.c 2006-12-10 18:50:18.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/icap_respmod.c 2006-12-10 17:09:04.000000000 +0200 @@ -64,6 +64,14 @@ if (service->flags.need_x_client_ip && Config.icapcfg.send_client_ip) { memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); } + if (icap->respmod.icap_server_session_context != NULL ) + { + debug(81, 5)( "sending icap context to server %s: %s\r\n", + Config.icapcfg.icap_session_context_tag_name, + icap->respmod.icap_server_session_context); + memBufPrintf(mb, "%s: %s\r\n", Config.icapcfg.icap_session_context_tag_name, + icap->respmod.icap_server_session_context); + } if (service->flags.need_x_server_ip && Config.icapcfg.send_server_ip) icapAddOriginIP(mb, icap->request->host); @@ -710,6 +718,54 @@ return (IcapStateData *) - 1; } } + + int i = 0; + MemObject *mem = entry->mem_obj; + store_client *sc; + dlink_node *nx = NULL; + dlink_node *node; + int client_fd = -1; + + // walk the entire list looking for valid callbacks, and get the http client fd + for (node = mem->clients.head; node; node = nx) { + sc = node->data; + nx = node->next; + if (sc->callback_data == NULL) + continue; +// if (sc->callback == NULL) +// continue; +// if (sc->flags.disk_io_pending) +// continue; + clientHttpRequest *http = sc->callback_data; + ConnStateData *conn = http->conn; + client_fd = conn->fd; + + debug(81, 6)( "fd found at icapRespModStart %d\n", client_fd ); + } + + //check to see if there is need to use ICAP resp mod at all + fde *F = NULL; + if( client_fd > 0 ) + { + F = &fd_table[client_fd]; + debug(81, 5)( "icapRespModStart fd_table[fd=%d] icap_response_info=%d \n", client_fd, F->icap_response_info); + + if( F->icap_response_info == 1 ) + { + // go direct, context is not needed any longer + if( F->icap_server_session_context != NULL ) + { + xfree( F->icap_server_session_context ); + F->icap_server_session_context = NULL; + } + //zero thr direct respose flag. + F->icap_response_info = 0; + debug(81, 6)( "goint direct on client_fd %d\n", client_fd ); + return (IcapStateData *) -2; + } + } + + switch (type) { /* TODO: When we support more than ICAP_SERVICE_RESPMOD_PRECACHE, we needs to change * this switch, because callbacks isn't keep */ @@ -727,6 +783,23 @@ debug(81, 3) ("icapRespModStart: icapAllocate() failed\n"); return NULL; } + if( F != NULL ) + { + //for safty check the salt if it is not well, null the context. + if( F->context_salt != 0xABCDABCD ) + F->icap_server_session_context = NULL; + + //4 Debug + if( F->icap_server_session_context != NULL ) + { + debug(81, 6)( "info %s: %s\r\n", Config.icapcfg.icap_session_context_tag_name, + F->icap_server_session_context); + debug(81, 6)( "context ok on client_fd %d\n", client_fd ); + } + icap->respmod.icap_server_session_context = F->icap_server_session_context; + F->icap_server_session_context = NULL; + } + icap->request = requestLink(request); icap->respmod.entry = entry; if (entry) diff -ruN squid-2.6.STABLE5/src/main.c squid-icap-2.6.STABLE5/src/main.c --- squid-2.6.STABLE5/src/main.c 2006-12-10 18:50:18.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/main.c 2006-12-10 18:04:59.000000000 +0200 @@ -260,7 +260,7 @@ icpPortNumOverride = 0; break; case 'v': - printf("Squid Cache: Version %s\nconfigure options: %s\n", version_string, SQUID_CONFIGURE_OPTIONS); + printf("Squid Cache: Version %s\nPureSight: ICAP Enabled v 1.26.0\nconfigure options: %s\n", version_string, SQUID_CONFIGURE_OPTIONS); #if defined(USE_WIN32_SERVICE) && defined(_SQUID_WIN32_) printf("Compiled as Windows System Service.\n"); #endif diff -ruN squid-2.6.STABLE5/src/.project squid-icap-2.6.STABLE5/src/.project --- squid-2.6.STABLE5/src/.project 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/.project 2006-12-10 16:10:49.000000000 +0200 @@ -0,0 +1,18 @@ + + + src + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + + diff -ruN squid-2.6.STABLE5/src/.settings/org.eclipse.cdt.managedbuilder.core.prefs squid-icap-2.6.STABLE5/src/.settings/org.eclipse.cdt.managedbuilder.core.prefs --- squid-2.6.STABLE5/src/.settings/org.eclipse.cdt.managedbuilder.core.prefs 1970-01-01 02:00:00.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/.settings/org.eclipse.cdt.managedbuilder.core.prefs 2006-12-10 16:10:54.000000000 +0200 @@ -0,0 +1,4 @@ +#Sun Dec 10 16:10:54 IST 2006 +eclipse.preferences.version=1 +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.exe.debug.1102957643=\n\n\n\n\n +environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.exe.debug.1102957643=\n\n\n\n diff -ruN squid-2.6.STABLE5/src/structs.h squid-icap-2.6.STABLE5/src/structs.h --- squid-2.6.STABLE5/src/structs.h 2006-12-10 18:50:18.000000000 +0200 +++ squid-icap-2.6.STABLE5/src/structs.h 2006-12-10 18:02:19.000000000 +0200 @@ -436,6 +436,11 @@ int send_server_ip; int send_auth_user; char *auth_scheme; + int icap_req_mod_direct_reply; /*onoff*/ + char *resp_info_tag_name; /*icap tag name, that represents the response info*/ + wordlist *icap_req_mod_direct_reply_values; /*what values */ + char *icap_session_context_tag_name; /*tag name that will mark the session data*/ + }; #endif /* HS_FEAT_ICAP */ @@ -885,6 +890,8 @@ int weak; /* true if it is a weak validator */ }; +#define CONTEXT_SALT 0xABCDABCD; + struct _fde { unsigned int type; u_short local_port; @@ -948,6 +955,9 @@ #if DELAY_POOLS int slow_id; #endif + char *icap_server_session_context; + unsigned int context_salt; + }; struct _fileMap { @@ -1165,6 +1175,7 @@ MemBuf req_hdr_copy; /* XXX barf */ MemBuf resp_copy; /* XXX barf^max */ squid_off_t res_body_sz; + char *icap_server_session_context; } respmod; struct { unsigned int connect_requested:1;