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