This patch is generated from the icap-2_6 branch of HEAD in squid Wed Dec 13 01:16:11 2006 GMT See http://devel.squid-cache.org/ Index: squid/configure.in diff -u squid/configure.in:1.169 squid/configure.in:1.87.4.13 --- squid/configure.in:1.169 Mon Dec 11 15:53:58 2006 +++ squid/configure.in Tue Dec 12 14:49:40 2006 @@ -530,6 +530,17 @@ fi ]) +dnl Enable ICAP Support +AM_CONDITIONAL(USE_ICAP, false) +AC_ARG_ENABLE(icap-support, +[ --enable-icap-support Enable ICAP client capability], +[ if test "$enableval" = "yes" ; then + echo "ICAP support enabled" + AC_DEFINE(HS_FEAT_ICAP, 1, [Content filtering via ICAP servers.]) + AM_CONDITIONAL(USE_ICAP, true) + fi +]) + dnl This is a developer only option. Developers know how to set defines dnl dnl AC_ARG_ENABLE(mem-gen-trace, @@ -2346,6 +2357,8 @@ srand48 \ srandom \ statfs \ + strnstr \ + strcasestr \ strsep \ strtoll \ sysconf \ @@ -2455,6 +2468,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/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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>FEHLER: Der angeforderte URL konnte nicht geholt werden</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>FEHLER</H1> +<H2>Der angeforderte URL konnte nicht geholt werden</H2> +<HR noshade size="1px"> +<P> +Während des Versuches, den URL<BR> +<A HREF="%U">%U</A> + +<BR> +zu laden, trat der folgende Fehler auf: +<UL> +<LI> +<STRONG> +ICAP-Protokollfehler +</STRONG> +</UL> + +<P> +<P> +Es trat ein Problem bei der ICAP-Kommunikation auf. Mögliche Gründe: +<UL> +<LI>Nicht erreichbarer ICAP-Server +<LI>Ungültige Antwort vom ICAP-Server + +</UL> +</P> + +<P>Ihr Cache Administrator ist <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML><HEAD> +<TITLE>ERROR: The requested URL could not be retrieved</TITLE> +<STYLE type="text/css"><!--BODY{background-color:#ffffff; font-family:verdana,sans-serif}--></STYLE> +</HEAD><BODY> +<H1>ERROR</H1> +<H2>The requested URL could not be retrieved</H2> +<HR noshade size="1px"> +<P> +While attempting to retrieve the URL: +<A HREF="%U">%U</A> +<P> +the following error was encountered: +<UL> +<LI> +<STRONG> +ICAP protocol error. +</STRONG> +</UL> + +<P> +<P> +Some aspect of the ICAP communication failed. Possible problems: +<UL> +<LI>ICAP server is not reachable. +<LI>Illegal response from ICAP server. +</UL> +</P> + +<P>Your cache administrator is <A HREF="mailto:%w">%w</A>. + 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 <ctype.h> + +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 <string.h> +#include <util.h> + +char *strnstr(const char *haystack, const char *needle, size_t haystacklen) +{ + char *p; + size_t plen; + size_t len = strlen(needle); + + if (*needle == '\0') /* everything matches empty string */ + return (char*) haystack; + + plen = haystacklen; + for (p = (char*) haystack; p != NULL; p = memchr(p + 1, *needle, plen-1)) { + plen = haystacklen - (p - haystack); + + if (plen < len) return NULL; + + if (strncmp(p, needle, len) == 0) + return (p); + } + return NULL; +} +#endif Index: squid/src/Makefile.am diff -u squid/src/Makefile.am:1.48 squid/src/Makefile.am:1.34.4.9 --- squid/src/Makefile.am:1.48 Sun Oct 29 16:51:32 2006 +++ squid/src/Makefile.am Fri Nov 3 10:47:05 2006 @@ -6,6 +6,12 @@ # Uncomment and customize the following to suit your needs: # +if USE_ICAP +ICAPSOURCE = icap_common.c icap_reqmod.c icap_respmod.c icap_opt.c +else +ICAPSOURCE = +endif + if USE_DNSSERVER DNSSOURCE = dns.c DNSSERVER = dnsserver @@ -181,6 +187,7 @@ HttpMsg.c \ HttpReply.c \ HttpRequest.c \ + $(ICAPSOURCE) \ icmp.c \ icp_v2.c \ icp_v3.c \ Index: squid/src/MemBuf.c diff -u squid/src/MemBuf.c:1.11 squid/src/MemBuf.c:1.9.10.4 --- squid/src/MemBuf.c:1.11 Tue Aug 15 17:53:02 2006 +++ squid/src/MemBuf.c Mon Aug 21 12:48:09 2006 @@ -341,3 +341,15 @@ assert(mb); memBufPrintf(mb, "memBufReport is not yet implemented @?@\n"); } + +int +memBufRead(int fd, MemBuf * mb) +{ + int len; + if (mb->capacity == mb->size) + memBufGrow(mb, SQUID_TCP_SO_RCVBUF); + len = FD_READ_METHOD(fd, mb->buf + mb->size, mb->capacity - mb->size); + if (len > 0) + mb->size += len; + return len; +} Index: squid/src/cache_cf.c diff -u squid/src/cache_cf.c:1.86 squid/src/cache_cf.c:1.61.4.12 --- squid/src/cache_cf.c:1.86 Sat Dec 9 21:51:43 2006 +++ squid/src/cache_cf.c Tue Dec 12 14:49:42 2006 @@ -2389,6 +2389,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 <servicename> {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.161 squid/src/cf.data.pre:1.100.4.12 --- squid/src/cf.data.pre:1.161 Tue Nov 28 16:52:57 2006 +++ squid/src/cf.data.pre Tue Dec 12 14:49:42 2006 @@ -3189,7 +3189,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 @@ -3458,6 +3457,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.154 squid/src/client_side.c:1.89.4.14 --- squid/src/client_side.c:1.154 Sun Dec 10 16:53:11 2006 +++ squid/src/client_side.c Tue Dec 12 14:49:44 2006 @@ -109,7 +109,7 @@ static CWCB clientWriteComplete; static CWCB clientWriteBodyComplete; static PF clientReadRequest; -static PF connStateFree; +PF connStateFree; static PF requestTimeout; static PF clientLifetimeTimeout; static int clientCheckTransferDone(clientHttpRequest *); @@ -141,12 +141,12 @@ static void clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb); static void clientPackTermBound(String boundary, MemBuf * mb); static void clientInterpretRequestHeaders(clientHttpRequest *); -static void clientProcessRequest(clientHttpRequest *); +void clientProcessRequest(clientHttpRequest *); static void clientProcessExpired(void *data); static void clientProcessOnlyIfCachedMiss(clientHttpRequest * http); -static int clientCachable(clientHttpRequest * http); -static int clientHierarchical(clientHttpRequest * http); -static int clientCheckContentLength(request_t * r); +int clientCachable(clientHttpRequest * http); +int clientHierarchical(clientHttpRequest * http); +int clientCheckContentLength(request_t * r); static DEFER httpAcceptDefer; static log_type clientProcessRequest2(clientHttpRequest * http); static int clientReplyBodyTooLarge(clientHttpRequest *, squid_off_t clen); @@ -157,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; @@ -638,6 +641,10 @@ if (urlgroup && *urlgroup) http->request->urlgroup = xstrdup(urlgroup); clientInterpretRequestHeaders(http); +#if HS_FEAT_ICAP + if (Config.icapcfg.onoff) + icapCheckAcl(http); +#endif #if HEADERS_LOG headersLog(0, 1, request->method, request); #endif @@ -1352,11 +1359,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; @@ -1376,8 +1394,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); @@ -1575,7 +1594,7 @@ } } -static int +int clientCheckContentLength(request_t * r) { switch (r->method) { @@ -1594,7 +1613,7 @@ /* NOT REACHED */ } -static int +int clientCachable(clientHttpRequest * http) { request_t *req = http->request; @@ -1620,7 +1639,7 @@ } /* Return true if we can query our neighbors for this object */ -static int +int clientHierarchical(clientHttpRequest * http) { const char *url = http->uri; @@ -3307,7 +3326,7 @@ return LOG_TCP_HIT; } -static void +void clientProcessRequest(clientHttpRequest * http) { char *url = http->uri; @@ -3318,6 +3337,11 @@ RequestMethodStr[r->method], url); r->flags.collapsed = 0; +#if HS_FEAT_ICAP + if (clientIcapReqMod(http)) { + return; + } +#endif if (r->method == METHOD_CONNECT && !http->redirect.status) { http->log_type = LOG_TCP_MISS; #if USE_SSL && SSL_CONNECT_INTERCEPT @@ -3812,6 +3836,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) { @@ -3916,7 +3954,8 @@ /* add to the client request queue */ dlinkAddTail(http, &http->node, &conn->reqs); conn->nrequests++; - commSetTimeout(fd, Config.Timeout.lifetime, clientLifetimeTimeout, http); + F->pconn.uses++; + F->pconn.type = 0; 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); err = errorCon(ERR_INVALID_REQ, HTTP_BAD_REQUEST, NULL); @@ -4086,7 +4125,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; @@ -4215,7 +4254,7 @@ } /* Abort a body request */ -static void +void clientAbortBody(request_t * request) { ConnStateData *conn = request->body_reader_data; @@ -4257,7 +4296,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 */ @@ -4978,6 +5017,52 @@ } } +#if HS_FEAT_ICAP +static int +clientIcapReqMod(clientHttpRequest * http) +{ + ErrorState *err; + icap_service *service; + if (http->flags.did_icap_reqmod) + return 0; + if (NULL == (service = icapService(ICAP_SERVICE_REQMOD_PRECACHE, http->request))) + return 0; + debug(33, 3) ("clientIcapReqMod: calling icapReqModStart for %p\n", http); + /* + * Note, we pass 'start' and 'log_addr' to ICAP so the access.log + * entry comes out right. The 'clientHttpRequest' created by + * the ICAP side is the one that gets logged. The first + * 'clientHttpRequest' does not get logged because its out.size + * is zero and log_type is unset. + */ + http->icap_reqmod = icapReqModStart(service, + http->uri, + http->request, + http->conn->fd, + http->start, + http->conn->log_addr, + (void *) http->conn); + if (NULL == http->icap_reqmod) { + return 0; + } else if (-1 == (int) http->icap_reqmod) { + /* produce error */ + http->icap_reqmod = NULL; + debug(33, 2) ("clientIcapReqMod: icap told us to send an error\n"); + http->log_type = LOG_TCP_DENIED; + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, http->orig_request); + err->xerrno = ETIMEDOUT; + err->request = requestLink(http->request); + err->src_addr = http->conn->peer.sin_addr; + http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags); + errorAppendEntry(http->entry, err); + return 1; + } + cbdataLock(http->icap_reqmod); + http->flags.did_icap_reqmod = 1; + return 1; +} +#endif + /* This is a handler normally called by comm_close() */ static void clientPinnedConnectionClosed(int fd, void *data) Index: squid/src/comm.c diff -u squid/src/comm.c:1.49 squid/src/comm.c:1.29.10.9 --- squid/src/comm.c:1.49 Mon Oct 23 04:52:53 2006 +++ squid/src/comm.c Fri Nov 3 10:47:12 2006 @@ -742,8 +742,8 @@ F->flags.closing = 1; CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING); commCallCloseHandlers(fd); - if (F->uses) /* assume persistent connect count */ - pconnHistCount(1, F->uses); + if (F->pconn.uses) + pconnHistCount(F->pconn.type, F->pconn.uses); #if USE_SSL if (F->ssl) { if (!F->flags.close_request) { Index: squid/src/enums.h diff -u squid/src/enums.h:1.57 squid/src/enums.h:1.45.4.6 --- squid/src/enums.h:1.57 Sat Sep 30 14:52:28 2006 +++ squid/src/enums.h Fri Nov 3 10:47:13 2006 @@ -93,6 +93,7 @@ ERR_ONLY_IF_CACHED_MISS, /* failure to satisfy only-if-cached request */ ERR_TOO_BIG, TCP_RESET, + ERR_ICAP_FAILURE, ERR_INVALID_RESP, ERR_MAX } err_type; @@ -455,6 +456,9 @@ PROTO_WHOIS, PROTO_INTERNAL, PROTO_HTTPS, +#if HS_FEAT_ICAP + PROTO_ICAP, +#endif PROTO_MAX } protocol_t; @@ -630,6 +634,12 @@ #if USE_SSL MEM_ACL_CERT_DATA, #endif +#if HS_FEAT_ICAP + MEM_ICAP_OPT_DATA, + MEM_ICAP_SERVICE_LIST, + MEM_ICAP_CLASS, + MEM_ICAP_ACCESS, +#endif MEM_MAX } mem_type; @@ -730,9 +740,14 @@ CBDATA_RemovalPolicyWalker, CBDATA_RemovalPurgeWalker, CBDATA_store_client, +#ifdef HS_FEAT_ICAP + CBDATA_IcapStateData, + CBDATA_icap_service, +#endif CBDATA_FIRST_CUSTOM_TYPE = 1000 } cbdata_type; + /* * Return codes from checkVary(request) */ @@ -781,4 +796,68 @@ ST_OP_CREATE } store_op_t; +#if HS_FEAT_ICAP +typedef enum { + ICAP_STATUS_NONE = 0, + ICAP_STATUS_CONTINUE = 100, + ICAP_STATUS_SWITCHING_PROTOCOLS = 101, + ICAP_STATUS_STATUS_OK = 200, + ICAP_CREATED = 201, + ICAP_STATUS_ACCEPTED = 202, + ICAP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, + ICAP_STATUS_NO_MODIFICATION_NEEDED = 204, + ICAP_STATUS_RESET_CONTENT = 205, + ICAP_STATUS_PARTIAL_CONTENT = 206, + ICAP_STATUS_MULTIPLE_CHOICES = 300, + ICAP_STATUS_MOVED_PERMANENTLY = 301, + ICAP_STATUS_MOVED_TEMPORARILY = 302, + ICAP_STATUS_SEE_OTHER = 303, + ICAP_STATUS_NOT_MODIFIED = 304, + ICAP_STATUS_USE_PROXY = 305, + ICAP_STATUS_BAD_REQUEST = 400, + ICAP_STATUS_UNAUTHORIZED = 401, + ICAP_STATUS_PAYMENT_REQUIRED = 402, + ICAP_STATUS_FORBIDDEN = 403, + ICAP_STATUS_SERVICE_NOT_FOUND = 404, + ICAP_STATUS_METHOD_NOT_ALLOWED = 405, + ICAP_STATUS_NOT_ACCEPTABLE = 406, + ICAP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, + ICAP_STATUS_REQUEST_TIMEOUT = 408, + ICAP_STATUS_CONFLICT = 409, + ICAP_STATUS_GONE = 410, + ICAP_STATUS_LENGTH_REQUIRED = 411, + ICAP_STATUS_PRECONDITION_FAILED = 412, + ICAP_STATUS_REQUEST_ENTITY_TOO_LARGE = 413, + ICAP_STATUS_REQUEST_URI_TOO_LARGE = 414, + ICAP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, + ICAP_STATUS_INTERNAL_SERVER_ERROR = 500, + ICAP_STATUS_NOT_IMPLEMENTED = 501, + ICAP_STATUS_BAD_GATEWAY = 502, + ICAP_STATUS_SERVICE_OVERLOADED = 503, + ICAP_STATUS_GATEWAY_TIMEOUT = 504, + ICAP_STATUS_ICAP_VERSION_NOT_SUPPORTED = 505, + ICAP_STATUS_INVALID_HEADER = 600 +} icap_status; + +/* + * these values are used as index in an array, so it seems to be better to + * assign some numbers + */ +typedef enum { + ICAP_SERVICE_REQMOD_PRECACHE = 0, + ICAP_SERVICE_REQMOD_POSTCACHE = 1, + ICAP_SERVICE_RESPMOD_PRECACHE = 2, + ICAP_SERVICE_RESPMOD_POSTCACHE = 3, + ICAP_SERVICE_MAX = 4 +} icap_service_t; + +typedef enum { + ICAP_METHOD_NONE, + ICAP_METHOD_OPTION, + ICAP_METHOD_REQMOD, + ICAP_METHOD_RESPMOD +} icap_method_t; + +#endif /* HS_FEAT_ICAP */ + #endif /* SQUID_ENUMS_H */ Index: squid/src/forward.c diff -u squid/src/forward.c:1.43 squid/src/forward.c:1.20.4.10 --- squid/src/forward.c:1.43 Sat Dec 9 21:51:43 2006 +++ squid/src/forward.c Tue Dec 12 14:49:45 2006 @@ -358,8 +358,9 @@ } else { debug(17, 3) ("fwdConnectDone: FD %d: '%s'\n", server_fd, storeUrl(fwdState->entry)); fd_note(server_fd, storeUrl(fwdState->entry)); - fd_table[server_fd].uses++; - if (fd_table[server_fd].uses == 1 && fs->peer) + fd_table[server_fd].pconn.uses++; + fd_table[server_fd].pconn.type = 1; + if (fd_table[server_fd].pconn.uses ==1 && fs->peer) peerConnectSucceded(fs->peer); #if USE_SSL if ((fs->peer && fs->peer->use_ssl) || @@ -941,6 +942,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), @@ -979,6 +982,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); @@ -998,7 +1003,10 @@ void fwdComplete(FwdState * fwdState) { - StoreEntry *e = fwdState->entry; + StoreEntry *e; + if (NULL == fwdState) + return; + e = fwdState->entry; assert(e->store_status == STORE_PENDING); debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e), e->mem_obj->reply->sline.status); Index: squid/src/globals.h diff -u squid/src/globals.h:1.27 squid/src/globals.h:1.22.4.5 --- squid/src/globals.h:1.27 Mon Sep 25 12:51:46 2006 +++ squid/src/globals.h Tue Sep 26 15:47:36 2006 @@ -171,6 +171,9 @@ #if HAVE_SBRK extern void *sbrk_start; /* 0 */ #endif +#if HS_FEAT_ICAP +extern char *icap_service_type_str[]; +#endif extern int opt_send_signal; /* -1 */ extern int opt_no_daemon; /* 0 */ #if LINUX_TPROXY Index: squid/src/http.c diff -u squid/src/http.c:1.49 squid/src/http.c:1.28.4.11 --- squid/src/http.c:1.49 Mon Oct 23 14:53:15 2006 +++ squid/src/http.c Fri Nov 3 10:47:13 2006 @@ -47,7 +47,7 @@ static PF httpReadReply; static void httpSendRequest(HttpStateData *); -static PF httpStateFree; +PF httpStateFree; static PF httpTimeout; static void httpCacheNegatively(StoreEntry *); static void httpMakePrivate(StoreEntry *); @@ -56,12 +56,13 @@ static void httpMaybeRemovePublic(StoreEntry *, http_status); static int peer_supports_connection_pinning(HttpStateData * httpState); -static void +void httpStateFree(int fd, void *data) { HttpStateData *httpState = data; #if DELAY_POOLS - delayClearNoDelay(fd); + if (fd >= 0) + delayClearNoDelay(fd); #endif if (httpState == NULL) return; @@ -80,6 +81,9 @@ requestUnlink(httpState->orig_request); httpState->request = NULL; httpState->orig_request = NULL; +#if HS_FEAT_ICAP + cbdataUnlock(httpState->icap_writer); +#endif cbdataFree(httpState); } @@ -409,7 +413,7 @@ } /* rewrite this later using new interfaces @?@ */ -static void +void httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) { StoreEntry *entry = httpState->entry; @@ -552,24 +556,35 @@ MemObject *mem = httpState->entry->mem_obj; HttpReply *reply = mem->reply; squid_off_t clen; + squid_off_t content_bytes_read; debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); debug(11, 5) ("httpPconnTransferDone: content_length=%" PRINTF_OFF_T "\n", reply->content_length); /* If we haven't seen the end of reply headers, we are not done */ - if (httpState->reply_hdr_state < 2) + if (httpState->reply_hdr_state < 2) { + debug(11, 3) ("httpPconnTransferDone: reply_hdr_state=%d, returning 0\n", + httpState->reply_hdr_state); return 0; + } clen = httpReplyBodySize(httpState->request->method, reply); +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) { + content_bytes_read = httpState->icap_writer->fake_content_length; + debug(11, 3) ("using fake conten length %" PRINTF_OFF_T "\n", content_bytes_read); + } else +#endif + content_bytes_read = mem->inmem_hi; /* If the body size is unknown we must wait for EOF */ if (clen < 0) return 0; /* Barf if we got more than we asked for */ - if (mem->inmem_hi > clen + reply->hdr_sz) + if (content_bytes_read > clen + reply->hdr_sz) return -1; /* If there is no message body, we can be persistent */ if (0 == clen) return 1; /* If the body size is known, we must wait until we've gotten all of it. */ - if (mem->inmem_hi < clen + reply->hdr_sz) + if (content_bytes_read < clen + reply->hdr_sz) return 0; /* We got it all */ return 1; @@ -636,6 +651,17 @@ delay_id delay_id; #endif +#if HS_FEAT_ICAP + if (httpState->icap_writer) { + if (!httpState->icap_writer->respmod.entry) { + debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); + comm_close(fd); + return; + } + /*The folowing entry can not be marked as aborted. + * The StoreEntry icap_writer->respmod.entry used when the icap_write used...... */ + } else +#endif if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { comm_close(fd); return; @@ -647,7 +673,35 @@ else delay_id = delayMostBytesAllowed(entry->mem_obj, &read_sz); #endif +#if HS_FEAT_ICAP + if (httpState->icap_writer) { + IcapStateData *icap = httpState->icap_writer; + /* + * Ok we have a received a response from the web server, so try to + * connect the icap server if it's the first attemps. If we try + * to connect to the icap server, defer this request (do not read + * the buffer), and defer until icapConnectOver() is not called. + */ + if (icap->flags.connect_requested == 0) { + debug(81, 2) ("icapSendRespMod: Create a new connection to icap server\n"); + if (!icapConnect(icap, icapConnectOver)) { + debug(81, 2) ("icapSendRespMod: Something strange while creating a socket to icap server\n"); + commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); + return; + } + debug(81, 2) ("icapSendRespMod: new connection to icap server (using FD=%d)\n", icap->icap_fd); + icap->flags.connect_requested = 1; + /* Wait for more data or EOF condition */ + commSetTimeout(fd, httpState->flags.keepalive_broken ? 10 : Config.Timeout.read, NULL, NULL); + commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); + return; + } + if(icap->flags.no_content == 1) { + commSetDefer(fd, fwdCheckDeferRead, icap->respmod.entry); + } + } +#endif errno = 0; statCounter.syscalls.sock.reads++; len = FD_READ_METHOD(fd, buf, read_sz); @@ -664,7 +718,13 @@ clen >>= 1; IOStats.Http.read_hist[bin]++; } - if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].uses > 1) { +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) + (void) 0; + else +#endif + + if (!httpState->reply_hdr.size && len > 0 && fd_table[fd].pconn.uses > 1) { /* Skip whitespace */ while (len > 0 && xisspace(*buf)) xmemmove(buf, buf + 1, len--); @@ -694,6 +754,12 @@ } else if (len == 0) { /* Connection closed; retrieval done. */ httpState->eof = 1; +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer && cbdataValid(httpState->icap_writer)) { + debug(81, 3) ("httpReadReply: EOF for ICAP writer\n"); + icapSendRespMod(httpState->icap_writer, buf, len, 1); + } +#endif if (httpState->reply_hdr_state < 2) /* * Yes Henrik, there is a point to doing this. When we @@ -746,7 +812,28 @@ EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); } } - storeAppend(entry, buf, len); +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) { + debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); + if (cbdataValid(httpState->icap_writer)) { + icapSendRespMod(httpState->icap_writer, buf, len, 0); + httpState->icap_writer->fake_content_length += len; + } + } else +#endif + storeAppend(entry, buf, len); + + + debug(11, 5) ("httpReadReply: after storeAppend FD %d read %d\n", fd, len); +#if HS_FEAT_ICAP + if (httpState->icap_writer) { + if (!httpState->icap_writer->respmod.entry) { + debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd); + comm_close(fd); + return; + } + } else +#endif if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { /* * the above storeAppend() call could ABORT this entry, @@ -793,10 +880,21 @@ ("httpReadReply: Excess data from \"%s %s\"\n", RequestMethodStr[orig_request->method], storeUrl(entry)); - storeAppend(entry, buf, len); +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) { + debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); + icapSendRespMod(httpState->icap_writer, buf, len, 0); + httpState->icap_writer->fake_content_length += len; + } else +#endif + storeAppend(entry, buf, len); keep_alive = 0; } } +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) + icapSendRespMod(httpState->icap_writer, NULL, 0, 1); +#endif if (keep_alive) { int pinned = 0; #if LINUX_TPROXY @@ -852,6 +950,10 @@ ("httpReadReply: Excess data from \"%s %s\"\n", RequestMethodStr[orig_request->method], storeUrl(entry)); +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) + icapSendRespMod(httpState->icap_writer, NULL, 0, 1); +#endif fwdComplete(httpState->fwd); comm_close(fd); return; @@ -862,6 +964,34 @@ } } +#ifdef HS_FEAT_ICAP +static int +httpReadReplyWaitForIcap(int fd, void *data) +{ + HttpStateData *httpState = data; + if (NULL == httpState->icap_writer) + return 0; + /* + * Do not defer when we are not connected to the icap server. + * Defer when the icap server connection is not established but pending + * Defer when the icap server is busy (writing on the socket) + */ + debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_requested=%d\n", + fd, httpState->icap_writer->flags.connect_requested); + if (!httpState->icap_writer->flags.connect_requested) + return 0; + debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, connect_pending=%d\n", + fd, httpState->icap_writer->flags.connect_pending); + if (httpState->icap_writer->flags.connect_pending) + return 1; + debug(11, 5) ("httpReadReplyWaitForIcap: FD %d, write_pending=%d\n", + fd, httpState->icap_writer->flags.write_pending); + if (httpState->icap_writer->flags.write_pending) + return 1; + return 0; +} +#endif + /* This will be called when request write is complete. Schedule read of * reply. */ static void @@ -889,6 +1019,63 @@ comm_close(fd); return; } else { + /* Schedule read reply. */ +#ifdef HS_FEAT_ICAP + if (icapService(ICAP_SERVICE_RESPMOD_PRECACHE, httpState->orig_request)) { + httpState->icap_writer = icapRespModStart( + ICAP_SERVICE_RESPMOD_PRECACHE, + httpState->orig_request, httpState->entry, httpState->flags); + if (-1 == (int) httpState->icap_writer) { + /* TODO: send error here and exit */ + ErrorState *err; + httpState->icap_writer = 0; + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, httpState->fwd->request); + err->xerrno = errno; + err->request = requestLink(httpState->orig_request); + errorAppendEntry(entry, err); + comm_close(fd); + return; + } else if (httpState->icap_writer) { + request_flags fake_flags = httpState->orig_request->flags; + method_t fake_method = entry->mem_obj->method; + const char *fake_msg = "this is a fake entry for " + " response sent to an ICAP RESPMOD server"; + cbdataLock(httpState->icap_writer); + /* + * this httpState will give the data it reads to + * the icap server, rather than put it into + * a StoreEntry + */ + storeClientUnregisterAbort(httpState->entry); + storeUnlockObject(httpState->entry); + /* + * create a bogus entry because the code assumes one is + * always there. + */ + fake_flags.cachable = 0; + fake_flags.hierarchical = 0; /* force private key */ + httpState->entry = storeCreateEntry("fake", "fake", fake_flags, fake_method); + storeAppend(httpState->entry, fake_msg, strlen(fake_msg)); + /* + * pull a switcheroo on fwdState->entry. + */ + storeUnlockObject(httpState->fwd->entry); + httpState->fwd->entry = httpState->entry; + storeLockObject(httpState->fwd->entry); + /* + * Note that we leave fwdState connected to httpState, + * but we changed the entry. So when fwdComplete + * or whatever is called it does no harm -- its + * just the fake entry. + */ + } else { + /* + * failed to open connection to ICAP server. + * But bypass request, so just continue here. + */ + } + } +#endif /* * Set the read timeout here because it hasn't been set yet. * We only set the read timeout after the request has been @@ -897,8 +1084,18 @@ * the timeout for POST/PUT requests that have very large * request bodies. */ + + /* removed in stable5: + * commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); + */ commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); - commSetDefer(fd, fwdCheckDeferRead, entry); +#ifdef HS_FEAT_ICAP + if (httpState->icap_writer) { + debug(11, 5) ("FD %d, setting httpReadReplyWaitForIcap\n", httpState->fd); + commSetDefer(httpState->fd, httpReadReplyWaitForIcap, httpState); + } else +#endif + commSetDefer(httpState->fd, fwdCheckDeferRead, entry); } httpState->flags.request_sent = 1; } @@ -1192,8 +1389,11 @@ if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) { const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request); httpHdrCcSetMaxAge(cc, getMaxAge(url)); +#ifndef HS_FEAT_ICAP + /* Don;t bother - if the url you want to cache is redirected? */ if (strLen(request->urlpath)) assert(strstr(url, strBuf(request->urlpath))); +#endif } /* Set no-cache if determined needed but not found */ if (orig_request->flags.nocache && !httpHeaderHas(hdr_in, HDR_PRAGMA)) @@ -1319,6 +1519,7 @@ int fd = fwd->server_fd; HttpStateData *httpState; request_t *proxy_req; + /* ErrorState *err; */ request_t *orig_req = fwd->request; debug(11, 3) ("httpStart: \"%s %s\"\n", RequestMethodStr[orig_req->method], @@ -1361,12 +1562,22 @@ httpState->request = requestLink(orig_req); httpState->orig_request = requestLink(orig_req); } +#ifdef HS_FEAT_ICAP + if (icapService(ICAP_SERVICE_REQMOD_POSTCACHE, httpState->orig_request)) { + httpState->icap_writer = icapRespModStart(ICAP_SERVICE_REQMOD_POSTCACHE, + httpState->orig_request, httpState->entry, httpState->flags); + if (httpState->icap_writer) { + return; + } + } +#endif /* * register the handler to free HTTP state data when the FD closes */ comm_add_close_handler(fd, httpStateFree, httpState); statCounter.server.all.requests++; statCounter.server.http.requests++; + httpSendRequest(httpState); /* * We used to set the read timeout here, but not any more. Index: squid/src/icap_common.c diff -u /dev/null squid/src/icap_common.c:1.1.14.3 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/icap_common.c Fri May 26 12:24:02 2006 @@ -0,0 +1,815 @@ +/* + * $Id$ + * + * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client + * AUTHOR: Geetha Manjunath, Hewlett Packard Company + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +/* _GNU_SOURCE is required for strcasestr */ +#define _GNU_SOURCE 1 + +#include "squid.h" +#include "util.h" + +extern PF httpStateFree; + +#define EXPECTED_ICAP_HEADER_LEN 256 +#define ICAP_OPTIONS_REQUEST + + +void +icapInit() +{ +#ifdef ICAP_OPTIONS_REQUEST + if (Config.icapcfg.onoff) { + icapOptInit(); + } +#endif +} + +void +icapClose() +{ + icapOptShutdown(); +} + +/* + * search for a HTTP-like header in the buffer. + * Note, buf must be 0-terminated + * + * This function is not very good. It should probably look for + * header tokens only at the start of a line, not just anywhere in + * the buffer. + */ +int +icapFindHeader(const char *buf, const char *hdr, const char **Start, + const char **End) +{ + const char *start = NULL; + const char *end = NULL; + start = strcasestr(buf, hdr); + if (NULL == start) + return 0; + end = start + strcspn(start, "\r\n"); + if (start == end) + return 0; + *Start = start; + *End = end; + return 1; +} + +/* + * parse the contents of the encapsulated header (buffer between enc_start + * and enc_end) and put the result into IcapStateData + */ +void +icapParseEncapsulated(IcapStateData * icap, const char *enc_start, + const char *enc_end) +{ + char *current, *end; + + assert(icap); + assert(enc_start); + assert(enc_end); + + current = strchr(enc_start, ':'); + current++; + while (current < enc_end) { + while (isspace(*current)) + current++; + if (!strncmp(current, "res-hdr=", 8)) { + current += 8; + icap->enc.res_hdr = strtol(current, &end, 10); + } else if (!strncmp(current, "req-hdr=", 8)) { + current += 8; + icap->enc.req_hdr = strtol(current, &end, 10); + } else if (!strncmp(current, "null-body=", 10)) { + current += 10; + icap->enc.null_body = strtol(current, &end, 10); + } else if (!strncmp(current, "res-body=", 9)) { + current += 9; + icap->enc.res_body = strtol(current, &end, 10); + } else if (!strncmp(current, "req-body=", 9)) { + current += 9; + icap->enc.req_body = strtol(current, &end, 10); + } else if (!strncmp(current, "opt-body=", 9)) { + current += 9; + icap->enc.opt_body = strtol(current, &end, 10); + } else { + /* invalid header */ + debug(81, 5) ("icapParseEncapsulated: error in: %s\n", current); + return; + } + current = end; + current = strchr(current, ','); + if (current == NULL) + break; + else + current++; /* skip ',' */ + } + debug(81, + 3) ("icapParseEncapsulated: res-hdr=%d, req-hdr=%d, null-body=%d, " + "res-body=%d, req-body=%d, opt-body=%d\n", icap->enc.res_hdr, + icap->enc.req_hdr, icap->enc.null_body, icap->enc.res_body, + icap->enc.req_body, icap->enc.opt_body); + +} + +icap_service * +icapService(icap_service_t type, request_t * r) +{ + icap_service_list *isl_iter; + int is_iter; + int nb_unreachable = 0; + icap_service *unreachable_one = NULL; + + debug(81, 8) ("icapService: type=%s\n", icapServiceToStr(type)); + if (NULL == r) { + debug(81, 8) ("icapService: no request_t\n"); + return NULL; + } + if (NULL == r->class) { + debug(81, 8) ("icapService: no class\n"); + return NULL; + } + for (isl_iter = r->class->isl; isl_iter; isl_iter = isl_iter->next) { + /* TODO:luc: Do a round-robin, choose a random value ? + * For now, we use a simple round robin with checking is the + * icap server is available */ + is_iter = isl_iter->last_service_used; + do { + is_iter = (is_iter + 1) % isl_iter->nservices; + debug(81, 8) ("icapService: checking service %s/id=%d\n", + isl_iter->services[is_iter]->name, is_iter); + if (type == isl_iter->services[is_iter]->type) { + if (!isl_iter->services[is_iter]->unreachable) { + debug(81, 8) ("icapService: found service %s/id=%d\n", + isl_iter->services[is_iter]->name, is_iter); + isl_iter->last_service_used = is_iter; + return isl_iter->services[is_iter]; + } + debug(81, + 8) + ("icapService: found service %s/id=%d, but it's unreachable. I don't want to use it\n", + isl_iter->services[is_iter]->name, is_iter); + unreachable_one = isl_iter->services[is_iter]; + nb_unreachable++; + /* FIXME:luc: in response mod, if we return an NULL pointer, user can bypass + * the filter, is it normal ? */ + } + } while (is_iter != isl_iter->last_service_used); + } + debug(81, 8) ("icapService: no service found\n"); + isl_iter = r->class->isl; + + if (nb_unreachable > 0) { + debug(81, + 8) + ("All the services are unreachable, returning an unreachable one\n"); + return unreachable_one; + } else { + return NULL; + } +} + +int +icapConnect(IcapStateData * icap, CNCB * theCallback) +{ + int rc; + icap->icap_fd = pconnPop(icap->current_service->hostname, + icap->current_service->port, NULL, NULL, 0); + if (icap->icap_fd >= 0) { + debug(81, 3) ("icapConnect: reused pconn FD %d\n", icap->icap_fd); + fd_note(icap->icap_fd, icap->current_service->uri); + comm_add_close_handler(icap->icap_fd, icapStateFree, icap); + theCallback(icap->icap_fd, 0, icap); + return 1; + } + icap->icap_fd = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), 0, + COMM_NONBLOCKING, icap->current_service->uri); + debug(81, 5) ("icapConnect: new socket, FD %d, local address %s\n", + icap->icap_fd, inet_ntoa(getOutgoingAddr(NULL))); + if (icap->icap_fd < 0) { + icapStateFree(-1, icap); /* XXX test */ + return 0; + } + icap->flags.connect_pending = 1; + /* + * Configure timeout and close handler before calling + * connect because commConnectStart() might get an error + * immediately and close the descriptor before it returns. + */ + commSetTimeout(icap->icap_fd, Config.Timeout.connect, + icapConnectTimeout, icap); + comm_add_close_handler(icap->icap_fd, icapStateFree, icap); + /* + * This sucks. commConnectStart() may fail before returning, + * so lets lock the data and check its validity afterwards. + */ + cbdataLock(icap); + commConnectStart(icap->icap_fd, + icap->current_service->hostname, + icap->current_service->port, theCallback, icap); + rc = cbdataValid(icap); + cbdataUnlock(icap); + debug(81, 3) ("icapConnect: returning %d\n", rc); + return rc; +} + +IcapStateData * +icapAllocate(void) +{ + IcapStateData *icap; + + if (!Config.icapcfg.onoff) + return 0; + + icap = cbdataAlloc(IcapStateData); + icap->icap_fd = -1; + icap->enc.res_hdr = -1; + icap->enc.res_body = -1; + icap->enc.req_hdr = -1; + icap->enc.req_body = -1; + icap->enc.opt_body = -1; + icap->enc.null_body = -1; + icap->chunk_size = -1; + memBufDefInit(&icap->icap_hdr); + + debug(81, 3) ("New ICAP state\n"); + return icap; +} + +void +icapStateFree(int fd, void *data) +{ + IcapStateData *icap = data; + debug(81, 3) ("icapStateFree: FD %d, icap %p\n", fd, icap); + assert(icap); + assert(-1 == fd || fd == icap->icap_fd); + if (icap->respmod.entry) { + /* + * If we got some error on this side (like ECONNRESET) + * we must signal the other side(s) with a storeAbort() + * call. + */ + if (icap->respmod.entry->store_status != STORE_OK) + storeAbort(icap->respmod.entry); + storeUnlockObject(icap->respmod.entry); + icap->respmod.entry = NULL; + } + requestUnlink(icap->request); + icap->request = NULL; + if (!memBufIsNull(&icap->icap_hdr)) + memBufClean(&icap->icap_hdr); + if (!memBufIsNull(&icap->respmod.buffer)) + memBufClean(&icap->respmod.buffer); + if (!memBufIsNull(&icap->respmod.req_hdr_copy)) + memBufClean(&icap->respmod.req_hdr_copy); + if (!memBufIsNull(&icap->respmod.resp_copy)) + memBufClean(&icap->respmod.resp_copy); + if (!memBufIsNull(&icap->reqmod.hdr_buf)) + memBufClean(&icap->reqmod.hdr_buf); + if (!memBufIsNull(&icap->reqmod.http_entity.buf)) + memBufClean(&icap->reqmod.http_entity.buf); + if (!memBufIsNull(&icap->chunk_buf)) + memBufClean(&icap->chunk_buf); + if (icap->httpState) + httpStateFree(-1, icap->httpState); + cbdataUnlock(icap->reqmod.client_cookie); + cbdataFree(icap); +} + +void +icapConnectTimeout(int fd, void *data) +{ + IcapStateData *icap = data; + debug(81, 3) ("icapConnectTimeout: FD %d, unreachable=1\n", fd); + assert(fd == icap->icap_fd); + icapOptSetUnreachable(icap->current_service); + comm_close(fd); +} + +void +icapReadTimeout(int fd, void *data) +{ + IcapStateData *icap = data; + assert(fd == icap->icap_fd); + if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { + debug(81, 3) ("icapReadTimeout: FD %d, unreachable=1\n", fd); + icapOptSetUnreachable(icap->current_service); + } else + debug(81, 3) ("icapReadTimeout: FD %d, still reachable\n", fd); + comm_close(fd); +} + +icap_service_t +icapServiceToType(const char *s) +{ + if (!strcmp(s, "reqmod_precache")) + return ICAP_SERVICE_REQMOD_PRECACHE; + if (!strcmp(s, "reqmod_postcache")) + return ICAP_SERVICE_REQMOD_POSTCACHE; + if (!strcmp(s, "respmod_precache")) + return ICAP_SERVICE_RESPMOD_PRECACHE; + if (!strcmp(s, "respmod_postcache")) + return ICAP_SERVICE_RESPMOD_POSTCACHE; + return ICAP_SERVICE_MAX; +} + +const char * +icapServiceToStr(const icap_service_t type) +{ + if (type >= 0 && type < ICAP_SERVICE_MAX) + return icap_service_type_str[type]; + else + return "error"; +} + + +/* copied from clientAclChecklistCreate */ +static aclCheck_t * +icapAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http) +{ + aclCheck_t *ch; + ConnStateData *conn = http->conn; + ch = aclChecklistCreate(acl, http->request, 0); + ch->conn = conn; + cbdataLock(ch->conn); + return ch; +} + +/* + * check wether we do icap for a request + */ +int +icapCheckAcl(clientHttpRequest * http) +{ + icap_access *iter; + aclCheck_t *icapChecklist; + + for (iter = Config.icapcfg.access_head; iter; iter = iter->next) { + acl_access *A = iter->access; + icapChecklist = icapAclChecklistCreate(A, http); + if (aclMatchAclList(A->acl_list, icapChecklist)) { + debug(81, 5) ("icapCheckAcl: match for class=%s\n", + iter->class->name); + if (A->allow) { + /* allow rule, do icap and use associated class */ + http->request->class = iter->class; + aclChecklistFree(icapChecklist); + return 1; + } else { + /* deny rule, stop processing */ + aclChecklistFree(icapChecklist); + return 0; + } + } + aclChecklistFree(icapChecklist); + } + return 0; +} + +/* icapLineLength + * + * returns the amount of data until lineending ( \r\n ) + * This function is NOT tolerant of variations of \r\n. + */ +size_t +icapLineLength(const char *start, int len) +{ + size_t lineLen = 0; + char *end = (char *) memchr(start, '\r', len); + if (NULL == end) + return 0; + end++; /* advance to where '\n' should be */ + lineLen = end - start + 1; + if (lineLen > len) { + debug(0, 0) ("icapLineLength: warning lineLen (%d) > len (%d)\n", + lineLen, len); + return 0; + } + if (*end != '\n') { + debug(0, 0) ("icapLineLength: warning *end (%x) != '\\n'\n", *end); + return 0; + } + debug(81, 7) ("icapLineLength: returning %d\n", lineLen); + return lineLen; +} + +/* + * return: + * -1 if EOF before getting end of ICAP header + * 0 if we don't have the entire ICAP header yet + * 1 if we got the whole header + */ +int +icapReadHeader(int fd, IcapStateData * icap, int *isIcap) +{ + int headlen = 0; + int len = 0; + int peek_sz = EXPECTED_ICAP_HEADER_LEN; + int read_sz = 0; + LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); + for (;;) { + len = recv(fd, tmpbuf, peek_sz, MSG_PEEK); + debug(81, 5) ("recv(FD %d, ..., MSG_PEEK) ret %d\n", fd, len); + if (len < 0) { + debug(81, 1) ("icapReadHeader: FD %d recv error: %s\n", fd, + xstrerror()); + return -1; + } + if (len == 0) { + debug(81, 2) ("icapReadHeader: FD %d recv EOF\n", fd); + return -1; + } + headlen = headersEnd(tmpbuf, len); + debug(81, 3) ("headlen=%d\n", headlen); + /* + * break if we now know where the ICAP headers end + */ + if (headlen) + break; + /* + * break if we know there is no more data to read + */ + if (len < peek_sz) + break; + /* + * The ICAP header is larger than (or equal to) our read + * buffer, so double it and try to peek again. + */ + peek_sz *= 2; + if (peek_sz >= SQUID_TCP_SO_RCVBUF) { + debug(81, + 1) ("icapReadHeader: Failed to find end of ICAP header\n"); + debug(81, 1) ("\twithin first %d bytes of response\n", + SQUID_TCP_SO_RCVBUF); + debug(81, 1) ("\tpossible persistent connection bug/confusion\n"); + return -1; + } + } + /* + * Now actually read the data from the kernel + */ + if (headlen) + read_sz = headlen; + else + read_sz = len; + len = FD_READ_METHOD(fd, tmpbuf, read_sz); + assert(len == read_sz); + fd_bytes(fd, len, FD_READ); + memBufAppend(&icap->icap_hdr, tmpbuf, len); + if (headlen) { + /* End of ICAP header found */ + if (icap->icap_hdr.size < 4) + *isIcap = 0; + else if (0 == strncmp(icap->icap_hdr.buf, "ICAP", 4)) + *isIcap = 1; + else + *isIcap = 0; + return 1; + } + /* + * We don't have all the headers yet + */ + return 0; +} + +static int +icapParseConnectionClose(const IcapStateData * icap, const char *s, + const char *e) +{ + char *t; + char *q; + /* + * s points to the start of the line "Connection: ... " + * e points to *after* the last character on the line + */ + s += 11; /* skip past Connection: */ + while (s < e && isspace(*s)) + s++; + if (e - s < 5) + return 0; + /* + * create a buffer that we can use strtok on + */ + t = xmalloc(e - s + 1); + strncpy(t, s, e - s); + *(t + (e - s)) = '\0'; + for (q = strtok(t, ","); q; q = strtok(NULL, ",")) { + if (0 == strcasecmp(q, "close")) { + xfree(t); + return 1; + } + } + xfree(t); + return 0; +} + +/* returns icap status, version and subversion extracted from status line or -1 on parsing failure + * The str_status pointr points to the text returned from the icap server. + * sline probably is NOT terminated with '\0' + */ +int +icapParseStatusLine(const char *sline, int slinesize, int *version_major, + int *version_minor, const char **str_status) +{ + char *sp, *stmp, *ep = (char *) sline + slinesize; + int status; + if (slinesize < 14) /*The format of this line is: "ICAP/x.x xxx[ msg....]\r\n" */ + return -1; + + if (strncmp(sline, "ICAP/", 5) != 0) + return -1; + if (sscanf(sline + 5, "%d.%d", version_major, version_minor) != 2) + return -1; + + if (!(sp = memchr(sline, ' ', slinesize))) + return -1; + + while (sp < ep && xisspace(*++sp)); + + if (!xisdigit(*sp) || sp >= ep) + return -1; + + if ((status = strtol(sp, &stmp, 10)) <= 0) + return -1; + sp = stmp; + + while (sp < ep && xisspace(*++sp)); + *str_status = sp; + /*Must add a test for "\r\n" end headers .... */ + return status; +} + + +void +icapSetKeepAlive(IcapStateData * icap, const char *hdrs) +{ + const char *start; + const char *end; + if (0 == icap->flags.keep_alive) + return; + if (0 == icapFindHeader(hdrs, "Connection:", &start, &end)) { + icap->flags.keep_alive = 1; + return; + } + if (icapParseConnectionClose(icap, start, end)) + icap->flags.keep_alive = 0; + else + icap->flags.keep_alive = 1; +} + +/* + * icapParseChunkSize + * + * Returns the offset where the next chunk starts + * return parameter chunk_size; + */ +static int +icapParseChunkSize(const char *buf, int len, int *chunk_size) +{ + int chunkSize = 0; + char c; + size_t start; + size_t end; + size_t nextStart = 0; + debug(81, 3) ("icapParseChunkSize: buf=%p, len=%d\n", buf, len); + do { + start = nextStart; + debug(81, 3) ("icapParseChunkSize: start=%d\n", start); + if (len <= start) { + /* + * end of buffer, so far no lines or only empty lines, + * wait for more data. read chunk size with next buffer. + */ + *chunk_size = 0; + return 0; + } + end = start + icapLineLength(buf + start, len - start); + nextStart = end; + if (end <= start) { + /* + * no line found, need more code here, now we are in + * deep trouble, buffer stops with half a chunk size + * line. For now stop here. + */ + debug(81, 1) ("icapParseChunkSize: WARNING in mid-line, ret 0\n"); + *chunk_size = 0; + return 0; + } + while (start < end) { + if (NULL == strchr(w_space, buf[start])) + break; + start++; + } + while (start < end) { + if (NULL == strchr(w_space, buf[end - 1])) + break; + end--; + } + /* + * if now end <= start we got an empty line. The previous + * chunk data should stop with a CRLF. In case that the + * other end does not follow the specs and sends no CRLF + * or too many empty lines, just continue till we have a + * non-empty line. + */ + } while (end <= start); + debug(81, 3) ("icapParseChunkSize: start=%d, end=%d\n", start, end); + + /* Non-empty line: Parse the chunk size */ + while (start < end) { + c = buf[start++]; + if (c >= 'a' && c <= 'f') { + chunkSize = chunkSize * 16 + c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + chunkSize = chunkSize * 16 + c - 'A' + 10; + } else if (c >= '0' && c <= '9') { + chunkSize = chunkSize * 16 + c - '0'; + } else { + if (!(c == ';' || c == ' ' || c == '\t')) { + /*Syntax error: Chunksize expected. */ + *chunk_size = -2; /* we are done */ + return nextStart; + } + /* Next comes a chunk extension */ + break; + } + } + /* + * if we read a zero chunk, we reached the end. Mark this for + * icapPconnTransferDone + */ + *chunk_size = (chunkSize > 0) ? chunkSize : -2; + debug(81, 3) ("icapParseChunkSize: return nextStart=%d\n", nextStart); + return nextStart; +} + +/* + * icapParseChunkedBody + * + * De-chunk an HTTP entity received from the ICAP server. + * The 'store' function pointer is storeAppend() or memBufAppend(). + */ +size_t +icapParseChunkedBody(IcapStateData * icap, STRCB * store, void *store_data) +{ + int bufOffset = 0; + size_t bw = 0; + MemBuf *cb = &icap->chunk_buf; + const char *buf = cb->buf; + int len = cb->size; + + if (icap->chunk_size == -2) { + debug(81, 3) ("zero end chunk reached\n"); + return 0; + } + debug(81, 3) ("%s:%d: chunk_size=%d\n", __FILE__, __LINE__, + icap->chunk_size); + if (icap->chunk_size < 0) { + store(store_data, buf, len); + cb->size = 0; + return (size_t) len; + } + debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, + bufOffset, len); + while (bufOffset < len) { + debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, + bufOffset, len); + if (icap->chunk_size == 0) { + int x; + x = icapParseChunkSize(buf + bufOffset, + len - bufOffset, &icap->chunk_size); + if (x < 1) { + /* didn't find a valid chunk spec */ + break; + } + bufOffset += x; + debug(81, 3) ("got chunksize %d, new offset %d\n", + icap->chunk_size, bufOffset); + if (icap->chunk_size == -2) { + debug(81, 3) ("zero end chunk reached\n"); + break; + } + } + debug(81, 3) ("%s:%d: X\n", __FILE__, __LINE__); + if (icap->chunk_size > 0) { + if (icap->chunk_size >= len - bufOffset) { + store(store_data, buf + bufOffset, len - bufOffset); + bw += (len - bufOffset); + icap->chunk_size -= (len - bufOffset); + bufOffset = len; + } else { + store(store_data, buf + bufOffset, icap->chunk_size); + bufOffset += icap->chunk_size; + bw += icap->chunk_size; + icap->chunk_size = 0; + } + } + } + if (0 == bufOffset) { + (void) 0; + } else if (bufOffset == cb->size) { + cb->size = 0; + } else { + assert(bufOffset <= cb->size); + xmemmove(cb->buf, cb->buf + bufOffset, cb->size - bufOffset); + cb->size -= bufOffset; + } + return bw; +} + +/* + * icapAddAuthUserHeader + * + * Builds and adds the X-Authenticated-User header to an ICAP request headers. + */ +void +icapAddAuthUserHeader(MemBuf * mb, auth_user_request_t * auth_user_request) +{ + char *user = authenticateUserRequestUsername(auth_user_request); + char *authuser; + size_t len, userlen, schemelen, userofslen; + char *userofs; + + if (user == NULL) { + debug(81, 5) ("icapAddAuthUserHeader: NULL username\n"); + return; + } + userlen = strlen(user); + schemelen = strlen(Config.icapcfg.auth_scheme); + len = userlen + schemelen + 1; + authuser = xcalloc(len, 1); + + if ((userofs = strstr(Config.icapcfg.auth_scheme, "%u")) == NULL) { + /* simply add user at end of string */ + snprintf(authuser, len, "%s%s", Config.icapcfg.auth_scheme, user); + } else { + userofslen = userofs - Config.icapcfg.auth_scheme; + xmemcpy(authuser, Config.icapcfg.auth_scheme, userofslen); + xmemcpy(authuser + userofslen, user, userlen); + xmemcpy(authuser + userofslen + userlen, + userofs + 2, schemelen - (userofslen + 2) + 1); + } + + memBufPrintf(mb, "X-Authenticated-User: %s\r\n", base64_encode(authuser)); + xfree(authuser); +} + +/* + * icapAddOriginIP + * + * Builds and adds the X-Server-IP header to an ICAP request headers. + */ +void +icapAddOriginIP(MemBuf * mb, const char *host) +{ + const ipcache_addrs *addrs; + struct in_addr s; + + if (host == NULL) { + debug(81, 5) ("icapAddOriginIP: NULL host\n"); + return; + } + addrs = ipcache_gethostbyname(host, IP_LOOKUP_IF_MISS); + if (addrs == NULL) { + /* + * http://www.i-cap.org/spec/draft-stecher-icap-subid-00.txt : + * + * [...] If the meta information for some header is not available, + * the header field MUST be omitted. + */ + debug(81, 5) ("icapAddOriginIP: can't tell IP address\n"); + return; + } + s = addrs->in_addrs[0]; + memBufPrintf(mb, "X-Server-IP: %s\r\n", inet_ntoa(s)); +} Index: squid/src/icap_opt.c diff -u /dev/null squid/src/icap_opt.c:1.1.16.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/icap_opt.c Wed May 17 10:58:01 2006 @@ -0,0 +1,523 @@ + +/* + * $Id$ + * + * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client OPTIONS + * AUTHOR: Ralf Horstmann + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + +/*************************************************************/ + +/* + * network related functions for OPTIONS request + */ +static void icapOptStart(void *data); +static void icapOptTimeout(int fd, void *data); +static void icapOptConnectDone(int server_fd, int status, void *data); +static void icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data); +static void icapOptReadReply(int fd, void *data); + +/* + * reply parsing functions + */ +static int icapOptParseReply(icap_service * s, IcapOptData * i); +static void icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end); +static int icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end); + +/* + * helper functions + */ +static void icapOptDataInit(IcapOptData * i); +static void icapOptDataFree(IcapOptData * i); + +/*************************************************************/ + +#define TIMEOUT 10 + +void +icapOptInit() +{ + icap_service *s; + + /* iterate over configured services */ + s = Config.icapcfg.service_head; + while (s) { + eventAdd("icapOptStart", icapOptStart, s, 5.0, 1); + s = s->next; + } +} + +void +icapOptShutdown() +{ + icap_service *s; + + s = Config.icapcfg.service_head; + while (s) { + if (eventFind(icapOptStart, s)) { + eventDelete(icapOptStart, s); + } + s = s->next; + } +} + +/* + * mark a service as unreachable + */ +void +icapOptSetUnreachable(icap_service * s) +{ + s->unreachable = 1; + debug(81, 5) ("icapOptSetUnreachable: got called for %s\n", s->uri); + /* + * if there is an options request scheduled, delete it and add + * it again to reset the time to the default check_interval. + */ + if (eventFind(icapOptStart, s)) { + eventDelete(icapOptStart, s); + eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); + } +} + +static void +icapOptStart(void *data) +{ + icap_service *s = data; + int fd; + int ctimeout = TIMEOUT; + const char *host = s->hostname; + unsigned short port = s->port; + debug(81, 3) ("icapOptStart: starting OPTIONS request for %s (%s)\n", s->name, s->uri); + fd = comm_open(SOCK_STREAM, + 0, + getOutgoingAddr(NULL), + 0, + COMM_NONBLOCKING, + "ICAP OPTIONS connection"); + if (fd < 0) { + debug(81, 4) ("icapConnectStart: %s\n", xstrerror()); + eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); + return; + } + assert(s->opt == NULL); /* if not null, another options request might be running, which should not happen */ + s->opt = memAllocate(MEM_ICAP_OPT_DATA); + icapOptDataInit(s->opt); + cbdataLock(s); + commSetTimeout(fd, ctimeout, icapOptTimeout, s); + commConnectStart(fd, host, port, icapOptConnectDone, s); +} + +static void +icapOptTimeout(int fd, void *data) +{ + icap_service *s = data; + IcapOptData *i = s->opt; + int valid; + + debug(81, 4) ("icapOptConnectTimeout: fd=%d, service=%s\n", fd, s->uri); + + comm_close(fd); + valid = cbdataValid(s); + cbdataUnlock(s); + if (!valid) { + icapOptDataFree(i); + s->opt = NULL; + return; + } + /* try again later */ + icapOptDataFree(i); + s->opt = NULL; + s->unreachable = 1; + debug(81, 3) ("icapOptConnectTimeout: unreachable=1, service=%s\n", s->uri); + eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); + +} + +static void +icapOptConnectDone(int server_fd, int status, void *data) +{ + icap_service *s = data; + IcapOptData *i = s->opt; + MemBuf request; + int valid; + + valid = cbdataValid(s); + cbdataUnlock(s); + if (!valid) { + comm_close(server_fd); + icapOptDataFree(i); + s->opt = NULL; + return; + } + if (status != COMM_OK) { + debug(81, 3) ("icapOptConnectDone: unreachable=1, service=%s\n", s->uri); + comm_close(server_fd); + icapOptDataFree(i); + s->opt = NULL; + s->unreachable = 1; + eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); + return; + } + debug(81, 3) ("icapOptConnectDone: Connection ok. Sending Options request for %s\n", s->name); + memBufDefInit(&request); + memBufPrintf(&request, "OPTIONS %s ICAP/1.0\r\n", s->uri); + memBufPrintf(&request, "Host: %s\r\n", s->hostname); + memBufPrintf(&request, "Connection: close\r\n"); + memBufPrintf(&request, "User-Agent: ICAP-Client-Squid/1.2\r\n"); + memBufPrintf(&request, "\r\n"); + cbdataLock(s); + commSetTimeout(server_fd, TIMEOUT, icapOptTimeout, s); + comm_write_mbuf(server_fd, request, icapOptWriteComplete, s); +} + +static void +icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) +{ + icap_service *s = data; + IcapOptData *i = s->opt; + int valid; + + valid = cbdataValid(s); + cbdataUnlock(s); + if (!valid) { + comm_close(fd); + icapOptDataFree(i); + s->opt = NULL; + return; + } + debug(81, 5) ("icapOptWriteComplete: FD %d: size %d: errflag %d.\n", + fd, size, errflag); + if (size > 0) { + fd_bytes(fd, size, FD_WRITE); + kb_incr(&statCounter.icap.all.kbytes_out, size); + } + if (errflag) { + /* cancel this for now */ + debug(81, 3) ("icapOptWriteComplete: unreachable=1, service=%s\n", s->uri); + icapOptDataFree(i); + s->opt = NULL; + s->unreachable = 1; + eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); + comm_close(fd); + return; + } + cbdataLock(s); + commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, s, 0); +} + +static void +icapOptReadReply(int fd, void *data) +{ + icap_service *s = data; + IcapOptData *i = s->opt; + int size; + int len = i->size - i->offset - 1; + int valid; + + valid = cbdataValid(s); + cbdataUnlock(s); + if (!valid) { + comm_close(fd); + icapOptDataFree(i); + s->opt = NULL; + return; + } + if (len == 0) { + /* Grow the request memory area to accomodate for a large request */ + printf("PANIC: not enough memory\n"); +#if 0 + i->buf = memReallocBuf(i->buf, i->size * 2, &i->size); + debug(81, 2) ("icapoptReadReply: growing reply buffer: offset=%ld size=%ld\n", + (long) i->offset, (long) i->size); + len = i->size - i->offset - 1; +#endif + } + size = FD_READ_METHOD(fd, i->buf + i->offset, len); + i->offset += size; + debug(81, 3) ("icapOptReadReply: Got %d bytes of data\n", size); + if (size > 0) { + /* do some statistics */ + fd_bytes(fd, size, FD_READ); + kb_incr(&statCounter.icap.all.kbytes_in, size); + + /* + * some icap servers seem to ignore the "Connection: close" header. so + * after getting the complete option reply we close the connection + * ourself. + */ + if ((i->headlen = headersEnd(i->buf, i->offset))) { + debug(81, 3) ("icapOptReadReply: EndOfResponse\n"); + size = 0; + } + } + if (size < 0) { + debug(81, 3) ("icapOptReadReply: FD %d: read failure: %s.\n", fd, xstrerror()); + debug(81, 3) ("icapOptReadReply: unreachable=1, service=%s.\n", s->uri); + s->unreachable = 1; + icapOptDataFree(i); + s->opt = NULL; + eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); + comm_close(fd); + } else if (size == 0) { + /* no more data, now we can parse the reply */ + debug(81, 3) ("icapOptReadReply: FD %d: connection closed\n", fd); + i->buf[i->offset] = '\0'; /* for string functions */ + debug(81, 3) ("icapOptReadReply: unreachable=0, service=%s\n", s->uri); + + if (!icapOptParseReply(s, i)) { + debug(81, 3) ("icapOptReadReply: OPTIONS request not successful. scheduling again in %d seconds\n", Config.icapcfg.check_interval); + s->unreachable = 1; + } else + s->unreachable = 0; + + if (s->options_ttl <= 0) + s->options_ttl = Config.icapcfg.check_interval; + eventAdd("icapOptStart", icapOptStart, s, s->options_ttl, 1); + + icapOptDataFree(i); + s->opt = NULL; + comm_close(fd); + } else { + /* data received */ + /* commSetSelect(fd, Type, handler, client_data, timeout) */ + cbdataLock(s); + commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, data, 0); + } +} + +static int +icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end) +{ + int slen = strcspn(*parse_start, "\r\n"); + + if (!(*parse_start)[slen]) /* no crlf */ + return 0; + + if (slen == 0) /* empty line */ + return 0; + + *blk_start = *parse_start; + *blk_end = *blk_start + slen; + + /* set it to the beginning of next line */ + *parse_start = *blk_end; + while (**parse_start == '\r') /* CR */ + (*parse_start)++; + if (**parse_start == '\n') /* LF */ + (*parse_start)++; + return 1; +} + +/* process a single header entry between blk_start and blk_end */ +static void +icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end) +{ + const char *name_end = strchr(blk_start, ':'); + const int name_len = name_end ? name_end - blk_start : 0; + const char *value_start = blk_start + name_len + 1; /* skip ':' */ + int value_len; + int new; + + if (!name_len || name_end > blk_end) { + debug(81, 5) ("icapOptParseEntry: strange header. skipping\n"); + return; + } + if (name_len > 65536) { + debug(81, 5) ("icapOptParseEntry: unusual long header item. skipping.\n"); + return; + } + while (xisspace(*value_start) && value_start < blk_end) { + value_start++; + } + if (value_start >= blk_end) { + debug(81, 5) ("icapOptParseEntry: no value found\n"); + return; + } + value_len = blk_end - value_start; + + + /* extract information */ + if (!strncasecmp("Allow", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Allow\n"); + if (!strncmp("204", value_start, 3)) { + s->flags.allow_204 = 1; + } else { + debug(81, 3) ("icapOptParseEntry: Allow value unknown"); + } + } else if (!strncasecmp("Connection", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Connection\n"); + } else if (!strncasecmp("Encapsulated", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Encapsulated\n"); + } else if (!strncasecmp("ISTAG", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found ISTAG\n"); + stringClean(&s->istag); + stringLimitInit(&s->istag, value_start, value_len); + } else if (!strncasecmp("Max-Connections", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Max-Connections\n"); + errno = 0; + new = strtol(value_start, NULL, 10); + if (errno) { + debug(81, 5) ("icapOptParseEntry: Max-Connections: could not parse value\n"); + } else { + debug(81, 5) ("icapOptParseEntry: Max-Connections: new value=%d\n", new); + s->max_connections = new; + } + } else if (!strncasecmp("Methods", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Methods\n"); + } else if (!strncasecmp("Options-TTL", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Options-TTL\n"); + errno = 0; + new = strtol(value_start, NULL, 10); + if (errno) { + debug(81, 5) ("icapOptParseEntry: Options-TTL: could not parse value\n"); + } else { + debug(81, 5) ("icapOptParseEntry: Options-TTL: new value=%d\n", new); + s->options_ttl = new; + } + } else if (!strncasecmp("Preview", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Preview\n"); + errno = 0; + new = strtol(value_start, NULL, 10); + if (errno) { + debug(81, 5) ("icapOptParseEntry: Preview: could not parse value\n"); + } else { + debug(81, 5) ("icapOptParseEntry: Preview: new value=%d\n", new); + s->preview = new; + } + } else if (!strncasecmp("Service", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Service\n"); + } else if (!strncasecmp("Service-ID", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Service-ID\n"); + } else if (!strncasecmp("Transfer-Preview", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Transfer-Preview\n"); + stringClean(&s->transfer_preview); + stringLimitInit(&s->transfer_preview, value_start, value_len); + } else if (!strncasecmp("Transfer-Ignore", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Transfer-Ignore\n"); + stringClean(&s->transfer_ignore); + stringLimitInit(&s->transfer_ignore, value_start, value_len); + } else if (!strncasecmp("Transfer-Complete", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found Transfer-Complete\n"); + stringClean(&s->transfer_complete); + stringLimitInit(&s->transfer_complete, value_start, value_len); + } else if (!strncasecmp("X-Include", blk_start, name_len)) { + debug(81, 5) ("icapOptParseEntry: found X-Include\n"); + if (strstr(value_start, "X-Client-IP")) { + debug(81, 5) ("icapOptParseEntry: X-Include: found X-Client-IP\n"); + s->flags.need_x_client_ip = 1; + } + if (strstr(value_start, "X-Server-IP")) { + debug(81, 5) ("icapOptParseEntry: X-Include: found X-Server-IP\n"); + s->flags.need_x_server_ip = 1; + } + if (strstr(value_start, "X-Authenticated-User")) { + debug(81, 5) ("icapOptParseEntry: X-Include: found X-Authenticated-User\n"); + s->flags.need_x_authenticated_user = 1; + } + } else { + debug(81, 5) ("icapOptParseEntry: unknown options header\n"); + } +} + +/* parse OPTIONS reply */ +static int +icapOptParseReply(icap_service * s, IcapOptData * i) +{ + int version_major, version_minor; + const char *str_status; + int status; + const char *buf = i->buf; + const char *parse_start; + const char *head_end; + const char *blk_start; + const char *blk_end; + + if ((status = + icapParseStatusLine(i->buf, i->offset, + &version_major, &version_minor, &str_status)) < 0) { + debug(81, 2) ("icapOptParseReply: bad status line <%s>\n", i->buf); + return 0; + } + debug(81, 3) ("icapOptParseReply: got reply: <ICAP/%d.%d %d %s>\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.9 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/icap_reqmod.c Tue Dec 12 14:49:46 2006 @@ -0,0 +1,990 @@ + +/* + * $Id$ + * + * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client + * AUTHOR: Geetha Manjunath, Hewlett Packard Company + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + +#define ICAP_PROXY_KEEP_ALIVE 0 + +/* + * These once-static functions are required to be global for ICAP + */ + +PF clientReadRequest; +PF connStateFree; +StoreEntry *clientCreateStoreEntry(clientHttpRequest *, method_t, request_flags); +int clientReadDefer(int fd, void *data); +int clientCheckContentLength(request_t * r); +void clientProcessRequest(clientHttpRequest *); +int clientCachable(clientHttpRequest *); +int clientHierarchical(clientHttpRequest *); +void clientReadBody(request_t * request, char *buf, size_t size, + CBCB * callback, void *cbdata); +static void icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, + CBCB * callback, void *cbdata); + +static PF icapReqModReadHttpHdrs; +static PF icapReqModReadHttpBody; +static CWCB icapReqModSendBodyChunk; +static CBCB icapReqModBodyHandler; +static BODY_HANDLER icapReqModBodyReader; +static STRCB icapReqModMemBufAppend; + +#define EXPECTED_ICAP_HEADER_LEN 256 +static const char *crlf = "\r\n"; + +/* + * icapExpectedHttpReqHdrSize + * + * calculate the size of the HTTP headers that we expect + * to read from the ICAP server. + */ +static int +icapExpectedHttpReqHdrSize(IcapStateData * icap) +{ + if (icap->enc.req_body > -1 && icap->enc.req_hdr > -1) + return (icap->enc.req_body - icap->enc.req_hdr); + if (icap->enc.null_body > -1) + return icap->enc.null_body; + fatal("icapExpectedHttpReqHdrSize: unexpected case"); + return 0; +} + +/* + * icapReqModCreateClientState + * + * Creates fake client_side data structures so we can use + * that module to read/parse the HTTP request that we read + * from the ICAP server. + */ +static clientHttpRequest * +icapReqModCreateClientState(IcapStateData * icap, request_t * request) +{ + clientHttpRequest *http; + if (!cbdataValid(icap->reqmod.client_cookie)) { + debug(81, 3) ("Whups, client cookie invalid\n"); + icap->reqmod.client_fd = -1; + return NULL; + } + http = cbdataAlloc(clientHttpRequest); + /* + * use our own urlCanonicalClean here, because urlCanonicalClean + * may strip everything after a question-mark. As http->uri + * is used when doing a request to a parent proxy, we need the full + * url here. + */ + http->uri = xstrdup(urlCanonical(icap->request)); + http->log_uri = xstrndup(http->uri, MAX_URL); + http->range_iter.boundary = StringNull; + http->request = requestLink(request ? request : icap->request); + http->flags.did_icap_reqmod = 1; + http->start = icap->reqmod.start; + if (request) + http->http_ver = request->http_ver; +#if ICAP_PROXY_KEEP_ALIVE + /* + * Here it is possible becouse we are using as client_cookie the original http->conn + * if we will keep this code we must declare an icap->conn field........ + * Will work if pipeline_prefetch is not enabled + * We are using a dummy ConnStateData structure, just to free + * old clientHttpRequest :-( + * OK,all this code is a hack and possibly must not exists in cvs ...... + */ + + http->conn = icap->reqmod.client_cookie; + assert(http->conn->chr->next == NULL); + { + ConnStateData *dummyconn; + clientHttpRequest *H; + dummyconn = cbdataAlloc(ConnStateData); + dummyconn->fd = icap->reqmod.client_fd; + dummyconn->pinning.fd = -1; + H=DLINK_HEAD(conn->reqs); + dlinkAddTail(H, &H->node, &dummyconn->reqs); + H->conn = dummyconn; + comm_add_close_handler(dummyconn->fd, connStateFree, dummyconn); + } + http->conn->chr = http; +#else + http->conn = cbdataAlloc(ConnStateData); + http->conn->fd = icap->reqmod.client_fd; + http->conn->pinning.fd = -1; + http->conn->in.size = 0; + http->conn->in.buf = NULL; + http->conn->log_addr = icap->reqmod.log_addr; + dlinkAddTail(http, &http->node, &http->conn->reqs); + comm_add_close_handler(http->conn->fd, connStateFree, http->conn); +#endif + http->icap_reqmod = NULL; + return http; +} + +/* + * icapReqModInterpretHttpRequest + * + * Interpret an HTTP request that we read from the ICAP server. + * Create some "fake" clientHttpRequest and ConnStateData structures + * so we can pass this new request off to the routines in + * client_side.c. + */ +static void +icapReqModInterpretHttpRequest(IcapStateData * icap, request_t * request) +{ + clientHttpRequest *http = icapReqModCreateClientState(icap, request); + if (NULL == http) + return; + /* + * bits from clientReadRequest + */ + request->content_length = httpHeaderGetSize(&request->header, + HDR_CONTENT_LENGTH); + if (!urlCheckRequest(request) || + httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) { + ErrorState *err; + err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED, request); + request->flags.proxy_keepalive = 0; + http->entry = + clientCreateStoreEntry(http, request->method, null_request_flags); + errorAppendEntry(http->entry, err); + return; + } + if (!clientCheckContentLength(request)) { + ErrorState *err; + err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED, request); + http->entry = + clientCreateStoreEntry(http, request->method, null_request_flags); + errorAppendEntry(http->entry, err); + return; + } + /* Do we expect a request-body? */ + if (request->content_length > 0) { + debug(81, 5) ("handing request bodies in ICAP REQMOD\n"); + if (request->body_reader_data) + cbdataUnlock(request->body_reader_data); + request->body_reader = icapReqModBodyReader; + request->body_reader_data = icap; /* XXX cbdataLock? */ + cbdataLock(icap); /*Yes sure ..... */ + memBufDefInit(&icap->reqmod.http_entity.buf); + } + if (clientCachable(http)) + request->flags.cachable = 1; + if (clientHierarchical(http)) + request->flags.hierarchical = 1; + clientProcessRequest(http); +} + +/* + * icapReqModParseHttpError + * + * Handle an error when parsing the new HTTP request we read + * from the ICAP server. + */ +static void +icapReqModParseHttpError(IcapStateData * icap, const char *reason) +{ + debug(81, 1) ("icapReqModParseHttpError: %s\n", reason); +} + +/* + * icapEntryError + * + * A wrapper for errorCon() and errorAppendEntry(). + */ +static void +icapEntryError(IcapStateData * icap, err_type et, http_status hs, int xerrno) +{ + ErrorState *err; + clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); + if (NULL == http) + return; + http->entry = clientCreateStoreEntry(http, + icap->request->method, null_request_flags); + err = errorCon(et, hs, icap->request); + err->xerrno = xerrno; + errorAppendEntry(http->entry, err); +} + +/* + * icapReqModParseHttpRequest + * + * Parse the HTTP request that we read from the ICAP server. + * Creates and fills in the request_t structure. + */ +static void +icapReqModParseHttpRequest(IcapStateData * icap) +{ + char *mstr; + char *uri; + char *inbuf; + char *t; + char *token; + char *headers; + method_t method; + request_t *request; + http_version_t http_ver; + int reqlen = icap->reqmod.hdr_buf.size; + int hdrlen; + + /* + * Lazy, make a copy of the buf so I can chop it up with strtok() + */ + inbuf = xcalloc(reqlen + 1, 1); + memcpy(inbuf, icap->reqmod.hdr_buf.buf, reqlen); + + if ((mstr = strtok(inbuf, "\t ")) == NULL) { + debug(81, 1) ("icapReqModParseHttpRequest: Can't get request method\n"); + icapReqModParseHttpError(icap, "error:invalid-request-method"); + xfree(inbuf); + return; + } + method = urlParseMethod(mstr); + if (method == METHOD_NONE) { + debug(81, 1) ("icapReqModParseHttpRequest: Unsupported method '%s'\n", + mstr); + icapReqModParseHttpError(icap, "error:unsupported-request-method"); + xfree(inbuf); + return; + } + /* look for URL+HTTP/x.x */ + if ((uri = strtok(NULL, "\n")) == NULL) { + debug(81, 1) ("icapReqModParseHttpRequest: Missing URI\n"); + icapReqModParseHttpError(icap, "error:missing-url"); + xfree(inbuf); + return; + } + while (xisspace(*uri)) + uri++; + t = uri + strlen(uri); + assert(*t == '\0'); + token = NULL; + while (t > uri) { + t--; + if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) { + token = t + 1; + break; + } + } + while (t > uri && xisspace(*t)) + *(t--) = '\0'; + debug(81, 5) ("icapReqModParseHttpRequest: URI is '%s'\n", uri); + if (token == NULL) { + debug(81, 3) ("icapReqModParseHttpRequest: Missing HTTP identifier\n"); + icapReqModParseHttpError(icap, "error:missing-http-ident"); + xfree(inbuf); + return; + } + if (sscanf(token + 5, "%d.%d", &http_ver.major, &http_ver.minor) != 2) { + debug(81, 3) ("icapReqModParseHttpRequest: Invalid HTTP identifier.\n"); + icapReqModParseHttpError(icap, "error:invalid-http-ident"); + xfree(inbuf); + return; + } + debug(81, 6) ("icapReqModParseHttpRequest: Client HTTP version %d.%d.\n", + http_ver.major, http_ver.minor); + + headers = strtok(NULL, null_string); + hdrlen = inbuf + reqlen - headers; + + if ((request = urlParse(method, uri)) == NULL) { + debug(81, 3) ("Invalid URL: %s at %s:%d\n", uri, __FILE__, __LINE__); + icapEntryError(icap, ERR_INVALID_URL, HTTP_BAD_REQUEST, 0); + xfree(inbuf); + return; + } + /* compile headers */ + if (!httpHeaderParse(&request->header, headers, headers + hdrlen)) { + debug(81, 3) ("Failed to parse HTTP headers for: %s at %s:%d", + uri, __FILE__, __LINE__); + icapEntryError(icap, ERR_INVALID_REQ, HTTP_BAD_REQUEST, 0); + xfree(inbuf); + return; + } + debug(81, + 3) + ("icapReqModParseHttpRequest: successfully parsed the HTTP request\n"); + request->http_ver = http_ver; + request->client_addr = icap->request->client_addr; + request->client_port = icap->request->client_port; + request->my_addr = icap->request->my_addr; + request->my_port = icap->request->my_port; + request->class = icap->request->class; + if (icap->request->auth_user_request) { + /* Copy authentification info in new request */ + request->auth_user_request = icap->request->auth_user_request; + authenticateAuthUserRequestLock(request->auth_user_request); + } + request->content_length = httpHeaderGetSize(&request->header, + HDR_CONTENT_LENGTH); + if (strBuf(icap->request->extacl_log)) + request->extacl_log = stringDup(&icap->request->extacl_log); + if (icap->request->extacl_user) + request->extacl_user = xstrdup(icap->request->extacl_user); + if (icap->request->extacl_passwd) + request->extacl_passwd = xstrdup(icap->request->extacl_passwd); +#if ICAP_PROXY_KEEP_ALIVE + /* + * Copy the proxy_keepalive flag from the original request + */ + request->flags.proxy_keepalive = icap->request->flags.proxy_keepalive; + /* + * If proxy_keepalive was set for the original request, make + * sure that the adapated request also has the necessary headers + * for keepalive + */ + if (request->flags.proxy_keepalive) { + if (!httpMsgIsPersistent(http_ver, &request->header)) + request->flags.proxy_keepalive = 0; + } +#endif + icapReqModInterpretHttpRequest(icap, request); + xfree(inbuf); +} + +/* + * icapReqModHandoffRespMod + * + * Handles the case where a REQMOD request results in an HTTP REPLY + * (instead of an ICAP REPLY that contains a new HTTP REQUEST). We + * prepare the IcapStateData for passing off to the icap_reqmod + * code, where we have functions for reading HTTP replies in ICAP + * messages. + */ +static void +icapReqModHandoffRespMod(IcapStateData * icap) +{ + extern PF icapReadReply; + clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); + if (NULL == http) + return; + assert(icap->request); + http->entry = clientCreateStoreEntry(http, + icap->request->method, icap->request->flags); + icap->respmod.entry = http->entry; + storeLockObject(icap->respmod.entry); + + /* icap->http_flags = ? */ + memBufDefInit(&icap->respmod.buffer); + memBufDefInit(&icap->chunk_buf); + assert(icap->current_service); + icapReadReply(icap->icap_fd, icap); +} + +/* + * icapReqModKeepAliveOrClose + * + * Called when we are done reading from the ICAP server. + * Either close the connection or keep it open for a future + * transaction. + */ +static void +icapReqModKeepAliveOrClose(IcapStateData * icap) +{ + int fd = icap->icap_fd; + debug(81, 3) ("%s:%d FD %d\n", __FILE__, __LINE__, fd); + if (fd < 0) + return; + if (!icap->flags.keep_alive) { + debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, + __LINE__); + comm_close(fd); + return; + } + if (icap->request->content_length < 0) { + /* no message body */ + debug(81, 3) ("%s:%d no message body\n", __FILE__, __LINE__); + if (1 != icap->reqmod.hdr_state) { + /* didn't get to end of HTTP headers */ + debug(81, 3) ("%s:%d didnt find end of headers, closing\n", + __FILE__, __LINE__); + comm_close(fd); + return; + } + } else if (icap->reqmod.http_entity.bytes_read != + icap->request->content_length) { + debug(81, 3) ("%s:%d bytes_read (%" PRINTF_OFF_T ") != content_length (%" PRINTF_OFF_T ")\n", + __FILE__, __LINE__, icap->reqmod.http_entity.bytes_read, + icap->request->content_length); + /* an error */ + comm_close(fd); + return; + } + debug(81, 3) ("%s:%d looks good, keeping alive\n", __FILE__, __LINE__); + commSetDefer(fd, NULL, NULL); + commSetTimeout(fd, -1, NULL, NULL); + commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); + comm_remove_close_handler(fd, icapStateFree, icap); + pconnPush(fd, icap->current_service->hostname, icap->current_service->port, NULL, NULL, 0); + icap->icap_fd = -1; + icapStateFree(-1, icap); +} + +/* + * icapReqModReadHttpHdrs + * + * Read the HTTP reply from the ICAP server. Uses the values + * from the ICAP Encapsulation header to know how many bytes + * to read. + */ +static void +icapReqModReadHttpHdrs(int fd, void *data) +{ + IcapStateData *icap = data; + LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); + int rl; + debug(81, 3) ("icapReqModReadHttpHdrs:\n"); + assert(fd == icap->icap_fd); + assert(icap->enc.req_hdr == 0); + if (0 == icap->reqmod.hdr_state) { + int expect = icapExpectedHttpReqHdrSize(icap); + int so_far = icap->http_header_bytes_read_so_far; + int needed = expect - so_far; + debug(81, 3) ("expect=%d\n", expect); + debug(81, 3) ("so_far=%d\n", so_far); + debug(81, 3) ("needed=%d\n", needed); + assert(needed >= 0); + if (0 == expect) { + fatalf("unexpected condition in %s:%d", __FILE__, __LINE__); + } + rl = FD_READ_METHOD(fd, tmpbuf, needed); + debug(81, 3) ("icapReqModReadHttpHdrs: read %d bytes\n", rl); + if (rl < 0) { + fatalf("need to handle read error at %s:%d", __FILE__, __LINE__); + } + fd_bytes(fd, rl, FD_READ); + kb_incr(&statCounter.icap.all.kbytes_in, rl); + memBufAppend(&icap->reqmod.hdr_buf, tmpbuf, rl); + icap->http_header_bytes_read_so_far += rl; + if (rl != needed) { + /* still more header data to read */ + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, + 0); + return; + } + icap->reqmod.hdr_state = 1; + } + assert(1 == icap->reqmod.hdr_state); + debug(81, 3) ("icapReqModReadHttpHdrs: read the entire request headers\n"); + icapReqModParseHttpRequest(icap); + if (-1 == icap->reqmod.client_fd) { + /* we detected that the original client_side went away */ + icapReqModKeepAliveOrClose(icap); + } else if (icap->enc.req_body > -1) { + icap->chunk_size = 0; + memBufDefInit(&icap->chunk_buf); + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); + } else { + icapReqModKeepAliveOrClose(icap); + } +} + + +/* + * icapReqModReadIcapPart + * + * Read the ICAP reply header. + */ +static void +icapReqModReadIcapPart(int fd, void *data) +{ + IcapStateData *icap = data; + int version_major, version_minor; + const char *str_status; + int x; + const char *start; + const char *end; + int status; + int isIcap = 0; + int directResponse = 0; + + debug(81, 5) ("icapReqModReadIcapPart: FD %d httpState = %p\n", fd, data); + statCounter.syscalls.sock.reads++; + + x = icapReadHeader(fd, icap, &isIcap); + if (x < 0) { + /* Did not find a proper ICAP response */ + debug(81, 3) ("ICAP : Error path!\n"); + icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, + errno); + comm_close(fd); + return; + } + if (x == 0) { + /* + * Waiting for more headers. Schedule new read hander, but + * don't reset timeout. + */ + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); + return; + } + /* + * Parse the ICAP header + */ + assert(icap->icap_hdr.size); + debug(81, 3) ("Read icap header : <%s>\n", icap->icap_hdr.buf); + if ((status = + icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, + &version_major, &version_minor, &str_status)) < 0) { + debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); + /* is this correct in case of ICAP protocol error? */ + icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, + errno); + comm_close(fd); + return; + }; + if (200 != status && 201 != status) { + debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); + icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, + errno); + comm_close(fd); + return; + } + icapSetKeepAlive(icap, icap->icap_hdr.buf); + if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { + icapParseEncapsulated(icap, start, end); + } else { + debug(81, + 1) + ("WARNING: icapReqModReadIcapPart() did not find 'Encapsulated' header\n"); + } + if (icap->enc.res_hdr > -1) + directResponse = 1; + else if (icap->enc.res_body > -1) + directResponse = 1; + else + directResponse = 0; + debug(81, 3) ("icapReqModReadIcapPart: directResponse=%d\n", + directResponse); + + /* Check whether it is a direct reply - if so over to http part */ + if (directResponse) { + debug(81, + 3) + ("icapReqModReadIcapPart: FD %d, processing HTTP response for REQMOD!\n", + fd); + /* got the reply, no need to come here again */ + icap->flags.wait_for_reply = 0; + icap->flags.got_reply = 1; + icapReqModHandoffRespMod(icap); + return; + } + memBufDefInit(&icap->reqmod.hdr_buf); + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, 0); + return; +} + +/* + * icapSendReqModDone + * + * Called after we've sent the ICAP request. Checks for errors + * and installs the handler functions for the next step. + */ +static void +icapSendReqModDone(int fd, char *bufnotused, size_t size, int errflag, + void *data) +{ + IcapStateData *icap = data; + + debug(81, 5) ("icapSendReqModDone: FD %d: size %d: errflag %d.\n", + fd, size, errflag); + if (size > 0) { + fd_bytes(fd, size, FD_WRITE); + kb_incr(&statCounter.icap.all.kbytes_out, size); + } + if (errflag == COMM_ERR_CLOSING) + return; + if (errflag) { + debug(81, 3) ("icapSendReqModDone: unreachable=1, service=%s\n", + icap->current_service->uri); + icapOptSetUnreachable(icap->current_service); + icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, + errno); + comm_close(fd); + return; + } + /* Schedule read reply. */ + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); + /* + * Set the read timeout here because it hasn't been set yet. + * We only set the read timeout after the request has been + * fully written to the server-side. If we start the timeout + * after connection establishment, then we are likely to hit + * the timeout for POST/PUT requests that have very large + * request bodies. + */ + commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, icap); +} + + +/* + * icapSendReqMod + * + * Send the ICAP request, including HTTP request, to the ICAP server + * after connection has been established. + */ +static void +icapSendReqMod(int fd, int status, void *data) +{ + MemBuf mb; + MemBuf mb_hdr; + Packer p; + IcapStateData *icap = data; + char *client_addr; + int icap_fd = icap->icap_fd; + icap_service *service; + CWCB *theCallback; + + debug(81, 5) ("icapSendReqMod FD %d, status %d\n", fd, status); + icap->flags.connect_pending = 0; + + if (COMM_OK != status) { + debug(81, 1) ("Could not connect to ICAP server %s:%d: %s\n", + icap->current_service->hostname, + icap->current_service->port, xstrerror()); + debug(81, 3) ("icapSendReqMod: unreachable=1, service=%s\n", + icap->current_service->uri); + icapOptSetUnreachable(icap->current_service); + icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_SERVICE_UNAVAILABLE, errno); + comm_close(fd); + return; + } + fd_table[fd].pconn.uses++; + fd_table[fd].pconn.type = 2; + if (icap->request->content_length > 0) + theCallback = icapReqModSendBodyChunk; + else + theCallback = icapSendReqModDone; + + memBufDefInit(&mb); + memBufDefInit(&mb_hdr); + memBufPrintf(&mb_hdr, "%s %s HTTP/%d.%d\r\n", + RequestMethodStr[icap->request->method], + icap->reqmod.uri, + icap->request->http_ver.major, icap->request->http_ver.minor); + packerToMemInit(&p, &mb_hdr); + httpHeaderPackInto(&icap->request->header, &p); + packerClean(&p); + memBufAppend(&mb_hdr, crlf, 2); + service = icap->current_service; + assert(service); + client_addr = inet_ntoa(icap->request->client_addr); + + memBufPrintf(&mb, "REQMOD %s ICAP/1.0\r\n", service->uri); + memBufPrintf(&mb, "Encapsulated: req-hdr=0"); + /* TODO: Change the offset using 'request' if needed */ + if (icap->request->content_length > 0) + memBufPrintf(&mb, ", req-body=%d", mb_hdr.size); + else + memBufPrintf(&mb, ", null-body=%d", mb_hdr.size); + memBufAppend(&mb, crlf, 2); + + if (service->flags.need_x_client_ip && Config.icapcfg.send_client_ip) + memBufPrintf(&mb, "X-Client-IP: %s\r\n", client_addr); + + if (service->flags.need_x_server_ip && Config.icapcfg.send_server_ip) + icapAddOriginIP(&mb, icap->request->host); + + if ((service->flags.need_x_authenticated_user + && Config.icapcfg.send_auth_user) + && (icap->request->auth_user_request != NULL)) + icapAddAuthUserHeader(&mb, icap->request->auth_user_request); + if (service->keep_alive) { + icap->flags.keep_alive = 1; + } else { + icap->flags.keep_alive = 0; + memBufAppend(&mb, "Connection: close\r\n", 19); + } + memBufAppend(&mb, crlf, 2); + memBufAppend(&mb, mb_hdr.buf, mb_hdr.size); + memBufClean(&mb_hdr); + + debug(81, 5) ("icapSendReqMod: FD %d writing {%s}\n", icap->icap_fd, + mb.buf); + comm_write_mbuf(icap_fd, mb, theCallback, icap); +} + +/* + * icapReqModStart + * + * Initiate an ICAP REQMOD transaction. Create and fill in IcapStateData + * structure and request a TCP connection to the server. + */ +IcapStateData * +icapReqModStart(icap_service * service, const char *uri, request_t * request, + int fd, struct timeval start, struct in_addr log_addr, void *cookie) +{ + IcapStateData *icap = NULL; + + debug(81, 3) ("icapReqModStart: type=%d\n", (int) service->type); + + switch (service->type) { + case ICAP_SERVICE_REQMOD_PRECACHE: + break; + default: + fatalf("icapReqModStart: unsupported service type '%s'\n", + icap_service_type_str[service->type]); + break; + } + + if (service->unreachable) { + if (service->bypass) { + debug(81, + 5) ("icapReqModStart: BYPASS because service unreachable: %s\n", + service->uri); + return NULL; + } else { + debug(81, + 5) ("icapReqModStart: ERROR because service unreachable: %s\n", + service->uri); + return (IcapStateData *) - 1; + } + } + icap = icapAllocate(); + if (!icap) { + debug(81, 3) ("icapReqModStart: icapAllocate() failed\n"); + return NULL; + } + icap->current_service = service; + icap->preview_size = service->preview; + icap->reqmod.uri = uri; /* XXX should be xstrdup? */ + icap->reqmod.start = start; + icap->reqmod.log_addr = log_addr; + icap->request = requestLink(request); + icap->reqmod.hdr_state = 0; + icap->reqmod.client_fd = fd; + icap->reqmod.client_cookie = cookie; + cbdataLock(icap->reqmod.client_cookie); + + if (!icapConnect(icap, icapSendReqMod)) + return NULL; + + statCounter.icap.all.requests++; + debug(81, 3) ("icapReqModStart: returning %p\n", icap); + return icap; +} + +/* + * icapReqModSendBodyChunk + * + * A "comm_write" callback. This is called after comm_write() does + * its job to let us know how things went. If there are no errors, + * get another chunk of the body from client_side. + */ +static void +icapReqModSendBodyChunk(int fd, char *bufnotused, size_t size, int errflag, + void *data) +{ + IcapStateData *icap = data; + debug(81, 3) ("icapReqModSendBodyChunk: FD %d wrote %d errflag %d.\n", + fd, (int) size, errflag); + if (errflag == COMM_ERR_CLOSING) + return; + if (errflag) { + icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, + errno); + comm_close(fd); + return; + } + clientReadBody(icap->request, + memAllocate(MEM_8K_BUF), 8192, icapReqModBodyHandler, icap); +} + +/* + * icapReqModBodyHandler + * + * Called after Squid gets a chunk of the request entity from the + * client side. The body is chunkified and passed to comm_write. + * The comm_write callback depends on whether or not this is the + * last chunk. + */ +static void +icapReqModBodyHandler(char *buf, ssize_t size, void *data) +{ + IcapStateData *icap = data; + MemBuf mb; + CWCB *theCallback = icapReqModSendBodyChunk; + if (size < 0) { + debug(81, 1) ("icapReqModBodyHandler: %s\n", xstrerror()); + memFree8K(buf); + return; + } + memBufDefInit(&mb); + debug(81, 3) ("icapReqModBodyHandler: writing chunk size %d\n", size); + memBufPrintf(&mb, "%x\r\n", size); + if (size) + memBufAppend(&mb, buf, size); + else + theCallback = icapSendReqModDone; + memBufAppend(&mb, crlf, 2); + memFree8K(buf); + comm_write_mbuf(icap->icap_fd, mb, theCallback, icap); +} + +/* + * icapReqModReadHttpBody + * + * The read handler for the client's HTTP connection when reading + * message bodies. Called by comm_select(). + */ +static void +icapReqModReadHttpBody(int fd, void *data) +{ + IcapStateData *icap = data; + int len; + debug(81, 3) ("icapReqModReadHttpBody: FD %d called\n", fd); + len = memBufRead(fd, &icap->chunk_buf); + debug(81, 3) ("icapReqModReadHttpBody: read returns %d\n", len); + if (len < 0) { + debug(81, 3) ("icapReqModReadHttpBody: FD %d %s\n", fd, xstrerror()); + if (!ignoreErrno(errno)) + icap->flags.reqmod_http_entity_eof = 1; + } else if (0 == len) { + debug(81, 3) ("icapReqModReadHttpBody: FD %d EOF\n", fd); + icap->flags.reqmod_http_entity_eof = 1; + } else { + fd_bytes(fd, len, FD_READ); + kb_incr(&statCounter.icap.all.kbytes_in, len); + icap->reqmod.http_entity.bytes_read += + icapParseChunkedBody(icap, + icapReqModMemBufAppend, &icap->reqmod.http_entity.buf); + } + if (icap->chunk_size < 0 ) + icap->flags.reqmod_http_entity_eof = 1; + + if (!icap->flags.reqmod_http_entity_eof) + commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); + /* + * Notify the other side if it is waiting for data from us + */ + debug(81, 3) ("%s:%d http_entity.callback=%p\n", __FILE__, __LINE__, + icap->reqmod.http_entity.callback); + debug(81, 3) ("%s:%d http_entity.buf.size=%d\n", __FILE__, __LINE__, + icap->reqmod.http_entity.buf.size); + if (icap->reqmod.http_entity.callback) { + icapReqModPassHttpBody(icap, + icap->reqmod.http_entity.callback_buf, + icap->reqmod.http_entity.callback_bufsize, + icap->reqmod.http_entity.callback, + icap->reqmod.http_entity.callback_data); + icap->reqmod.http_entity.callback = NULL; + cbdataUnlock(icap->reqmod.http_entity.callback_data); + } +} + +/* + * icapReqModPassHttpBody + * + * Called from http.c after request headers have been sent. + * This function feeds the http.c module chunks of the request + * body that were stored in the http_entity.buf MemBuf. + */ +static void +icapReqModPassHttpBody(IcapStateData * icap, char *buf, size_t size, + CBCB * callback, void *cbdata) +{ + debug(81, 3) ("icapReqModPassHttpBody: called\n"); + if (!buf) { + debug(81, 1) ("icapReqModPassHttpBody: FD %d called with %p, %d, %p (request aborted)\n", + icap->icap_fd, buf, (int) size, cbdata); + comm_close(icap->icap_fd); + return; + } + if (!cbdataValid(cbdata)) { + debug(81, + 1) + ("icapReqModPassHttpBody: FD %d callback data invalid, closing\n", + icap->icap_fd); + comm_close(icap->icap_fd); /*It is better to be sure that the connection will be closed..... */ + /*icapReqModKeepAliveOrClose(icap); */ + return; + } + debug(81, 3) ("icapReqModPassHttpBody: entity buf size = %d\n", + icap->reqmod.http_entity.buf.size); + if (icap->reqmod.http_entity.buf.size) { + int copy_sz = icap->reqmod.http_entity.buf.size; + if (copy_sz > size) + copy_sz = size; + xmemcpy(buf, icap->reqmod.http_entity.buf.buf, copy_sz); + /* XXX don't let Alex see this ugliness */ + xmemmove(icap->reqmod.http_entity.buf.buf, + icap->reqmod.http_entity.buf.buf + copy_sz, + icap->reqmod.http_entity.buf.size - copy_sz); + icap->reqmod.http_entity.buf.size -= copy_sz; + debug(81, 3) ("icapReqModPassHttpBody: giving %d bytes to other side\n", + copy_sz); + callback(buf, copy_sz, cbdata); + debug(81, 3) ("icapReqModPassHttpBody: entity buf size now = %d\n", + icap->reqmod.http_entity.buf.size); + return; + } + if (icap->flags.reqmod_http_entity_eof) { + debug(81, 3) ("icapReqModPassHttpBody: signalling EOF\n"); + callback(buf, 0, cbdata); + icapReqModKeepAliveOrClose(icap); + return; + } + /* + * We have no data for the other side at this point. Save all + * these values and use them when we do have data. + */ + assert(NULL == icap->reqmod.http_entity.callback); + icap->reqmod.http_entity.callback = callback; + icap->reqmod.http_entity.callback_data = cbdata; + icap->reqmod.http_entity.callback_buf = buf; + icap->reqmod.http_entity.callback_bufsize = size; + cbdataLock(icap->reqmod.http_entity.callback_data); +} + +/* + * Body reader handler for use with request->body_reader function + * Simple a wrapper for icapReqModPassHttpBody function + */ + +static void +icapReqModBodyReader(request_t * request, char *buf, size_t size, + CBCB * callback, void *cbdata) +{ + IcapStateData *icap = request->body_reader_data; + icapReqModPassHttpBody(icap, buf, size, callback, cbdata); +} + +/* + * icapReqModMemBufAppend + * + * stupid wrapper to eliminate compiler warnings + */ +static void +icapReqModMemBufAppend(void *data, const char *buf, ssize_t size) +{ + memBufAppend(data, buf, size); +} Index: squid/src/icap_respmod.c diff -u /dev/null squid/src/icap_respmod.c:1.1.14.7 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/icap_respmod.c Tue Sep 26 15:47:36 2006 @@ -0,0 +1,1058 @@ + +/* + * $Id$ + * + * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client + * AUTHOR: Geetha Manjunath, Hewlett Packard Company + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + +static CWCB icapSendRespModDone; +static PF icapRespModGobble; +extern PF icapReadReply; +static PF icapRespModReadReply; +static void icapRespModKeepAliveOrClose(IcapStateData * icap); +static int icapReadReply2(IcapStateData * icap); +static void icapReadReply3(IcapStateData * icap); + +#define EXPECTED_ICAP_HEADER_LEN 256 +const char *crlf = "\r\n"; + +static void +getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, + const char *client_addr, IcapStateData * icap, const icap_service * service) +{ + memBufPrintf(mb, "RESPMOD %s ICAP/1.0\r\nEncapsulated:", service->uri); + if (o1 >= 0) + memBufPrintf(mb, " req-hdr=%1d", o1); + if (o2 >= 0) + memBufPrintf(mb, ", res-hdr=%1d", o2); + if (o3 >= 0) + memBufPrintf(mb, ", res-body=%1d", o3); + else + memBufPrintf(mb, ", null-body=%1d", -o3); + memBufPrintf(mb, crlf); + + if (service->flags.need_x_client_ip && Config.icapcfg.send_client_ip) { + memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); + } + if (service->flags.need_x_server_ip && Config.icapcfg.send_server_ip) + icapAddOriginIP(mb, icap->request->host); + + if ((service->flags.need_x_authenticated_user + && Config.icapcfg.send_auth_user) + && (icap->request->auth_user_request != NULL)) { + icapAddAuthUserHeader(mb, icap->request->auth_user_request); + } +#if NOT_YET_FINISHED + if (Config.icapcfg.trailers) { + memBufPrintf(mb, "X-TE: trailers\r\n"); + } +#endif +} + +static int +buildRespModHeader(MemBuf * mb, IcapStateData * icap, char *buf, + ssize_t len, int theEnd) +{ + MemBuf mb_hdr; + char *client_addr; + int o2 = 0; + int o3 = 0; + int hlen; + int consumed; + icap_service *service; + HttpReply *r; + + if (memBufIsNull(&icap->respmod.req_hdr_copy)) + memBufDefInit(&icap->respmod.req_hdr_copy); + + memBufAppend(&icap->respmod.req_hdr_copy, buf, len); + + if (icap->respmod.req_hdr_copy.size > 4 && strncmp(icap->respmod.req_hdr_copy.buf, "HTTP/", 5)) { + debug(81, 3) ("buildRespModHeader: Non-HTTP-compliant header: '%s'\n", buf); + /* + *Possible we can consider that we did not have http responce headers + *(maybe HTTP 0.9 protocol), lets returning -1... + */ + consumed = -1; + o2 = -1; + memBufDefInit(&mb_hdr); + httpBuildRequestPrefix(icap->request, icap->request, + icap->respmod.entry, &mb_hdr, icap->http_flags); + o3 = mb_hdr.size; + } else { + + hlen = headersEnd(icap->respmod.req_hdr_copy.buf, + icap->respmod.req_hdr_copy.size); + debug(81, 3) ("buildRespModHeader: headersEnd = %d(%s)\n", hlen, buf); + if (0 == hlen) + return 0; + + /* + * calc how many bytes from this 'buf' went towards the + * reply header. + */ + consumed = hlen - (icap->respmod.req_hdr_copy.size - len); + debug(81, 3) ("buildRespModHeader: consumed = %d\n", consumed); + + + /* + * now, truncate our req_hdr_copy at the header end. + * this 'if' statement might be unncessary? + */ + if (hlen < icap->respmod.req_hdr_copy.size) + icap->respmod.req_hdr_copy.size = hlen; + + /* Copy request header */ + memBufDefInit(&mb_hdr); + httpBuildRequestPrefix(icap->request, icap->request, + icap->respmod.entry, &mb_hdr, icap->http_flags); + o2 = mb_hdr.size; + + /* Copy response header - Append to request header mbuffer */ + memBufAppend(&mb_hdr, + icap->respmod.req_hdr_copy.buf, icap->respmod.req_hdr_copy.size); + o3 = mb_hdr.size; + } + + service = icap->current_service; + assert(service); + client_addr = inet_ntoa(icap->request->client_addr); + + r = httpReplyCreate(); + httpReplyParse(r, icap->respmod.req_hdr_copy.buf, + icap->respmod.req_hdr_copy.size); + icap->respmod.res_body_sz = httpReplyBodySize(icap->request->method, r); + httpReplyDestroy(r); + if (icap->respmod.res_body_sz) + getICAPRespModString(mb, 0, o2, o3, client_addr, icap, service); + else + getICAPRespModString(mb, 0, o2, -o3, client_addr, icap, service); + if (Config.icapcfg.preview_enable) + if (icap->preview_size >= 0) { + memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size); + icap->flags.preview_done = 0; + } + if (service->keep_alive) { + icap->flags.keep_alive = 1; + memBufAppend(mb, "Connection: keep-alive\r\n", 24); + } else { + icap->flags.keep_alive = 0; + memBufAppend(mb, "Connection: close\r\n", 19); + } + memBufAppend(mb, crlf, 2); + memBufAppend(mb, mb_hdr.buf, mb_hdr.size); + memBufClean(&mb_hdr); + + + return consumed; +} + + +void +icapSendRespMod(IcapStateData * icap, char *buf, int len, int theEnd) +{ + MemBuf mb; +#if ICAP_PREVIEW + int size; + const int preview_size = icap->preview_size; +#endif + debug(81, 5) ("icapSendRespMod: FD %d, len %d, theEnd %d\n", + icap->icap_fd, len, theEnd); + + if (icap->flags.no_content) { + /* + * ICAP server said there are no modifications to make, so + * just append this data to the StoreEntry + */ + if (icap->respmod.resp_copy.size) { + /* + * first copy the data that we already sent to the ICAP server + */ + memBufAppend(&icap->chunk_buf, + icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); + icap->respmod.resp_copy.size = 0; + } + debug(81, 5) ("icapSendRepMod: len=%d theEnd=%d write_pending=%d\n", + len, theEnd, icap->flags.write_pending); + if (len) { + /* + * also copy any new data from the HTTP side + */ + memBufAppend(&icap->chunk_buf, buf, len); + } + (void) icapReadReply2(icap); + return; + } + if (theEnd) { + if (icap->respmod.res_body_sz) + icap->flags.send_zero_chunk = 1; + icap->flags.http_server_eof = 1; + } + /* + * httpReadReply is going to call us with a chunk and then + * right away again with an EOF if httpPconnTransferDone() is true. + * Since the first write is already dispatched, we'll have to + * hack this in somehow. + */ + if (icap->flags.write_pending) { + debug(81, 3) ("icapSendRespMod: oops, write_pending=1\n"); + assert(theEnd); + assert(len == 0); + return; + } + if (!cbdataValid(icap)) { + debug(81, 3) ("icapSendRespMod: failed to establish connection?\n"); + return; + } + memBufDefInit(&mb); + +#if SUPPORT_ICAP_204 || ICAP_PREVIEW + /* + * make a copy of the response in case ICAP server gives us a 204 + */ + /* + * This piece of code is problematic for 204 responces outside preview. + * The icap->respmod.resp_copy continues to filled until we had responce + * If the icap server waits to gets all data before sends its responce + * then we are puting all downloading object to the main system memory. + * My opinion is that 204 responces outside preview must be disabled ..... + * /chtsanti + */ + + if (len && icap->flags.copy_response) { + if (memBufIsNull(&icap->respmod.resp_copy)) + memBufDefInit(&icap->respmod.resp_copy); + memBufAppend(&icap->respmod.resp_copy, buf, len); + } +#endif + + if (icap->sc == 0) { + // http connection has been closed without sending us anything + if (len == 0 && theEnd == 1) { + ErrorState *err; + err = errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY, icap->request); + errorAppendEntry(icap->respmod.entry, err); + comm_close(icap->icap_fd); + return; + } + /* No data sent yet. Start with headers */ + if ((icap->sc = buildRespModHeader(&mb, icap, buf, len, theEnd)) > 0) { + buf += icap->sc; + len -= icap->sc; + } + /* + * Then we do not have http responce headers. All data (previous and those in buf) + * now are exist to icap->respmod.req_hdr_copy. Lets get them back....... + */ + if (icap->sc < 0) { + memBufAppend(&icap->respmod.buffer, + icap->respmod.req_hdr_copy.buf, + icap->respmod.req_hdr_copy.size); + icap->sc = icap->respmod.req_hdr_copy.size; + icap->respmod.req_hdr_copy.size = 0; + buf = NULL; + len = 0; + } + } + if (0 == icap->sc) { + /* check again; bail if we're not ready to send ICAP/HTTP hdrs */ + debug(81, 5) ("icapSendRespMod: dont have full HTTP response hdrs\n"); + memBufClean(&mb); + return; + } +#if ICAP_PREVIEW + if (preview_size < 0 || !Config.icapcfg.preview_enable) /* preview feature off */ + icap->flags.preview_done = 1; + + if (!icap->flags.preview_done) { + /* preview not yet sent */ + if (icap->sc > 0 && icap->respmod.buffer.size <= preview_size + && len > 0) { + /* Try to collect at least preview_size+1 bytes */ + /* By collecting one more byte than needed for preview we know best */ + /* whether we have to send the ieof chunk extension */ + size = icap->respmod.buffer.size + len; + if (size > preview_size + 1) + size = preview_size + 1; + size -= icap->respmod.buffer.size; + debug(81, + 3) + ("icapSendRespMod: FD %d: copy %d more bytes to preview buffer.\n", + icap->icap_fd, size); + memBufAppend(&icap->respmod.buffer, buf, size); + buf = ((char *) buf) + size; + len -= size; + } + if (icap->respmod.buffer.size > preview_size || theEnd) { + /* we got enough bytes for preview or this is the last call */ + /* add preview preview now */ + if (icap->respmod.buffer.size > 0) { + size = icap->respmod.buffer.size; + if (size > preview_size) + size = preview_size; + memBufPrintf(&mb, "%x\r\n", size); + memBufAppend(&mb, icap->respmod.buffer.buf, size); + memBufAppend(&mb, crlf, 2); + icap->sc += size; + } + if (icap->respmod.buffer.size <= preview_size) { + /* content length is less than preview size+1 */ + if (icap->respmod.res_body_sz) + memBufAppend(&mb, "0; ieof\r\n\r\n", 11); + memBufReset(&icap->respmod.buffer); /* will now be used for other data */ + } else { + char ch; + memBufAppend(&mb, "0\r\n\r\n", 5); + /* end of preview, wait for continue or 204 signal */ + /* copy the extra byte and all other data to the icap buffer */ + /* so that it can be handled next time */ + ch = icap->respmod.buffer.buf[preview_size]; + memBufReset(&icap->respmod.buffer); /* will now be used for other data */ + memBufAppend(&icap->respmod.buffer, &ch, 1); + debug(81, + 3) + ("icapSendRespMod: FD %d: sending preview and keeping %d bytes in internal buf.\n", + icap->icap_fd, len + 1); + if (len > 0) + memBufAppend(&icap->respmod.buffer, buf, len); + } + icap->flags.preview_done = 1; + icap->flags.wait_for_preview_reply = 1; + } + } else if (icap->flags.wait_for_preview_reply) { + /* received new data while waiting for preview response */ + /* add data to internal buffer and send later */ + debug(81, + 3) + ("icapSendRespMod: FD %d: add %d more bytes to internal buf while waiting for preview-response.\n", + icap->icap_fd, len); + if (len > 0) + memBufAppend(&icap->respmod.buffer, buf, len); + /* do not send any data now while waiting for preview response */ + /* but prepare for read more data on the HTTP connection */ + memBufClean(&mb); + return; + } else +#endif + { + /* after preview completed and ICAP preview response received */ + /* there may still be some data in the buffer */ + if (icap->respmod.buffer.size > 0) { + memBufPrintf(&mb, "%x\r\n", icap->respmod.buffer.size); + memBufAppend(&mb, icap->respmod.buffer.buf, + icap->respmod.buffer.size); + memBufAppend(&mb, crlf, 2); + icap->sc += icap->respmod.buffer.size; + memBufReset(&icap->respmod.buffer); + } + if (len > 0) { + memBufPrintf(&mb, "%x\r\n", len); + memBufAppend(&mb, buf, len); + memBufAppend(&mb, crlf, 2); + icap->sc += len; + } + if (icap->flags.send_zero_chunk) { + /* send zero end chunk */ + icap->flags.send_zero_chunk = 0; + icap->flags.http_server_eof = 1; + memBufAppend(&mb, "0\r\n\r\n", 5); + } + /* wait for data coming from ICAP server as soon as we sent something */ + /* but of course only until we got the response header */ + if (!icap->flags.got_reply) + icap->flags.wait_for_reply = 1; + } + commSetTimeout(icap->icap_fd, -1, NULL, NULL); + + if (!mb.size) { + memBufClean(&mb); + return; + } + debug(81, 5) ("icapSendRespMod: FD %d writing {%s}\n", icap->icap_fd, + mb.buf); + icap->flags.write_pending = 1; + comm_write_mbuf(icap->icap_fd, mb, icapSendRespModDone, icap); +} + +static void +icapRespModReadReply(int fd, void *data) +{ + IcapStateData *icap = data; + int version_major, version_minor; + const char *str_status; + int x; + int status = 0; + int isIcap = 0; + int directResponse = 0; + ErrorState *err; + const char *start; + const char *end; + + debug(81, 5) ("icapRespModReadReply: FD %d data = %p\n", fd, data); + statCounter.syscalls.sock.reads++; + + x = icapReadHeader(fd, icap, &isIcap); + if (x < 0) { + /* Did not find a proper ICAP response */ + debug(81, 3) ("ICAP : Error path!\n"); + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + } + if (x == 0) { + /* + * Waiting for more headers. Schedule new read hander, but + * don't reset timeout. + */ + commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); + return; + } + /* + * Parse the ICAP header + */ + assert(icap->icap_hdr.size); + debug(81, 3) ("Parse icap header : <%s>\n", icap->icap_hdr.buf); + if ((status = + icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, + &version_major, &version_minor, &str_status)) < 0) { + debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); + /* is this correct in case of ICAP protocol error? */ + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + }; + /* OK here we have responce. Lets stop filling the + * icap->respmod.resp_copy buffer .... + */ + icap->flags.copy_response = 0; + + icapSetKeepAlive(icap, icap->icap_hdr.buf); +#if ICAP_PREVIEW + if (icap->flags.wait_for_preview_reply) { + if (100 == status) { + debug(81, 5) ("icapRespModReadReply: 100 Continue received\n"); + icap->flags.wait_for_preview_reply = 0; + /* if http_server_eof + * call again icapSendRespMod to handle data that + * was received while waiting for this ICAP response + * else let http to call icapSendRespMod when new data arrived + */ + if (icap->flags.http_server_eof) + icapSendRespMod(icap, NULL, 0, 0); + /* + * reset the header to send the rest of the preview + */ + if (!memBufIsNull(&icap->icap_hdr)) + memBufReset(&icap->icap_hdr); + + /*We do n't need it any more ....... */ + if (!memBufIsNull(&icap->respmod.resp_copy)) + memBufClean(&icap->respmod.resp_copy); + + return; + } + if (204 == status) { + debug(81, + 5) ("icapRespModReadReply: 204 No modification received\n"); + icap->flags.wait_for_preview_reply = 0; + } + } +#endif /*ICAP_PREVIEW */ + +#if SUPPORT_ICAP_204 || ICAP_PREVIEW + if (204 == status) { + debug(81, 3) ("got 204 status from ICAP server\n"); + icapRespModKeepAliveOrClose(icap); + + debug(81, 3) ("setting icap->flags.no_content\n"); + icap->flags.no_content = 1; + /* + * copy the response already written to the ICAP server + */ + debug(81, 3) ("copying %d bytes from resp_copy to chunk_buf\n", + icap->respmod.resp_copy.size); + memBufAppend(&icap->chunk_buf, + icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); + icap->respmod.resp_copy.size = 0; + if (icapReadReply2(icap) < 0) + icapStateFree(-1, icap); + + /* + * XXX ideally want to clean icap->respmod.resp_copy here + * XXX ideally want to "close" ICAP server connection here + * OK do it.... + */ + if (!memBufIsNull(&icap->respmod.resp_copy)) + memBufClean(&icap->respmod.resp_copy); + return; + } +#endif + if (200 != status && 201 != status) { + debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); + /* Did not find a proper ICAP response */ + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + } + if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { + icapParseEncapsulated(icap, start, end); + } else { + debug(81, + 1) + ("WARNING: icapRespModReadReply() did not find 'Encapsulated' header\n"); + } + if (icap->enc.res_hdr > -1) + directResponse = 1; + else if (icap->enc.res_body > -1) + directResponse = 1; + else + directResponse = 0; + + /* + * "directResponse" is the normal case here. If we don't have + * a response header or body, it is an error. + */ + if (!directResponse) { + /* Did not find a proper ICAP response */ + debug(81, 3) ("ICAP : Error path!\n"); + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + } + /* got the reply, no need to come here again */ + icap->flags.wait_for_reply = 0; + icap->flags.got_reply = 1; + /* Next, gobble any data before the HTTP response starts */ + if (icap->enc.res_hdr > -1) + icap->bytes_to_gobble = icap->enc.res_hdr; + commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); +} + + +/* + * Gobble up (read) some bytes until we get to the start of the body + */ +static void +icapRespModGobble(int fd, void *data) +{ + IcapStateData *icap = data; + int len; + LOCAL_ARRAY(char, junk, SQUID_TCP_SO_RCVBUF); + debug(81, 3) ("icapRespModGobble: FD %d gobbling %d bytes\n", fd, + icap->bytes_to_gobble); + len = FD_READ_METHOD(fd, junk, icap->bytes_to_gobble); + debug(81, 3) ("icapRespModGobble: gobbled %d bytes\n", len); + if (len < 0) { + /* XXX error */ + abort(); + } + icap->bytes_to_gobble -= len; + if (icap->bytes_to_gobble) + commSetSelect(fd, COMM_SELECT_READ, icapRespModGobble, icap, 0); + else + icapReadReply(fd, icap); +} + + +static void +icapSendRespModDone(int fd, char *bufnotused, size_t size, int errflag, + void *data) +{ + IcapStateData *icap = data; + ErrorState *err; + + icap->flags.write_pending = 0; + debug(81, 5) ("icapSendRespModDone: FD %d: size %d: errflag %d.\n", + fd, size, errflag); + if (size > 0) { + fd_bytes(fd, size, FD_WRITE); + kb_incr(&statCounter.icap.all.kbytes_out, size); + } + if (errflag == COMM_ERR_CLOSING) + return; + if (errflag) { + if (cbdataValid(icap)) + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + else + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, NULL); + err->xerrno = errno; + storeEntryReset(icap->respmod.entry); + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + return; + } + if (EBIT_TEST(icap->respmod.entry->flags, ENTRY_ABORTED)) { + debug(81, 3) ("icapSendRespModDone: Entry Aborded\n"); + comm_close(fd); + return; + } + if (icap->flags.send_zero_chunk) { + debug(81, + 3) ("icapSendRespModDone: I'm supposed to send zero chunk now\n"); + icap->flags.send_zero_chunk = 0; + icapSendRespMod(icap, NULL, 0, 1); + return; + } + if (icap->flags.wait_for_preview_reply || icap->flags.wait_for_reply) { + /* Schedule reading the ICAP response */ + debug(81, + 3) + ("icapSendRespModDone: FD %d: commSetSelect on read icapRespModReadReply.\n", + fd); + commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); +#if 1 + commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); + commSetDefer(fd, fwdCheckDeferRead, icap->respmod.entry); +#else + if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { + /* + * Set the read timeout only after all data has been sent + * or we are waiting for a preview response + * If the ICAP server does not return any data till all data + * has been sent, we are likely to hit the timeout for large + * HTTP bodies + */ + commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); + } +#endif + } +} + +void +icapConnectOver(int fd, int status, void *data) +{ + ErrorState *err; + IcapStateData *icap = data; + debug(81, 3) ("icapConnectOver: FD %d, status=%d\n", fd, status); + icap->flags.connect_pending = 0; + if (status < 0) { + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, icap->request); + err->xerrno = errno; + errorAppendEntry(icap->respmod.entry, err); + comm_close(fd); + debug(81, 3) ("icapConnectOver: status < 0, unreachable=1\n"); + icapOptSetUnreachable(icap->current_service); + return; + } + fd_table[fd].pconn.uses++; + fd_table[fd].pconn.type = 2; + commSetSelect(fd, COMM_SELECT_READ, icapRespModReadReply, icap, 0); +} + + + +IcapStateData * +icapRespModStart(icap_service_t type, request_t * request, StoreEntry * entry, + http_state_flags http_flags) +{ + IcapStateData *icap = NULL; + CNCB *theCallback = NULL; + icap_service *service = NULL; + + debug(81, 3) ("icapRespModStart: type=%d\n", (int) type); + assert(type >= 0 && type < ICAP_SERVICE_MAX); + + service = icapService(type, request); + if (!service) { + debug(81, 3) ("icapRespModStart: no service found\n"); + return NULL; /* no service found */ + } + if (service->unreachable) { + if (service->bypass) { + debug(81, + 5) + ("icapRespModStart: BYPASS because service unreachable: %s\n", + service->uri); + return NULL; + } else { + debug(81, + 5) + ("icapRespModStart: ERROR because service unreachable: %s\n", + service->uri); + return (IcapStateData *) - 1; + } + } + switch (type) { + /* TODO: When we support more than ICAP_SERVICE_RESPMOD_PRECACHE, we needs to change + * this switch, because callbacks isn't keep */ + case ICAP_SERVICE_RESPMOD_PRECACHE: + theCallback = icapConnectOver; + break; + default: + fatalf("icapRespModStart: unsupported service type '%s'\n", + icap_service_type_str[type]); + break; + } + + icap = icapAllocate(); + if (!icap) { + debug(81, 3) ("icapRespModStart: icapAllocate() failed\n"); + return NULL; + } + icap->request = requestLink(request); + icap->respmod.entry = entry; + if (entry) + storeLockObject(entry); + icap->http_flags = http_flags; + memBufDefInit(&icap->respmod.buffer); + memBufDefInit(&icap->chunk_buf); + + icap->current_service = service; + icap->preview_size = service->preview; + + /* + * Don't create socket to the icap server now, but only for the first + * packet receive from the http server. This will resolve all timeout + * between the web server and icap server. + */ + debug(81, 3) ("icapRespModStart: setting connect_requested to 0\n"); + icap->flags.connect_requested = 0; + + /* + * make a copy the HTTP response that we send to the ICAP server in + * case it turns out to be a 204 + */ +#ifdef SUPPORT_ICAP_204 + icap->flags.copy_response = 1; +#elif ICAP_PREVIEW + if (preview_size < 0 || !Config.icapcfg.preview_enable) + icap->flags.copy_response = 0; + else + icap->flags.copy_response = 1; +#else + icap->flags.copy_response = 0; +#endif + + statCounter.icap.all.requests++; + debug(81, 3) ("icapRespModStart: returning %p\n", icap); + return icap; +} + +static int +icapHttpReplyHdrState(IcapStateData * icap) +{ + assert(icap); + if (NULL == icap->httpState) + return 0; + return icap->httpState->reply_hdr_state; +} + +static void +icapProcessHttpReplyHeader(IcapStateData * icap, const char *buf, int size) +{ + if (NULL == icap->httpState) { + icap->httpState = cbdataAlloc(HttpStateData); + icap->httpState->request = requestLink(icap->request); + icap->httpState->orig_request = requestLink(icap->request); + icap->httpState->entry = icap->respmod.entry; + storeLockObject(icap->httpState->entry); /* lock it */ + } + httpProcessReplyHeader(icap->httpState, buf, size); + if (2 == icap->httpState->reply_hdr_state) + EBIT_CLR(icap->httpState->entry->flags, ENTRY_FWD_HDR_WAIT); +} + +/* + * icapRespModKeepAliveOrClose + * + * Called when we are done reading from the ICAP server. + * Either close the connection or keep it open for a future + * transaction. + */ +static void +icapRespModKeepAliveOrClose(IcapStateData * icap) +{ + int fd = icap->icap_fd; + if (fd < 0) + return; + debug(81, 3) ("%s:%d FD %d looks good, keeping alive\n", __FILE__, __LINE__, + fd); + commSetDefer(fd, NULL, NULL); + commSetTimeout(fd, -1, NULL, NULL); + commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); + comm_remove_close_handler(fd, icapStateFree, icap); + icap->icap_fd = -1; + if (!icap->flags.keep_alive) { + debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, + __LINE__); + comm_close(fd); + return; + } else { + pconnPush(fd, icap->current_service->hostname, icap->current_service->port, NULL, NULL, 0); + } +} + + + +/* + * copied from httpPconnTransferDone + * + */ +static int +icapPconnTransferDone(int fd, IcapStateData * icap) +{ + debug(81, 3) ("icapPconnTransferDone: FD %d\n", fd); + /* + * Be careful with 204 responses. Normally we are done when we + * see the zero-end chunk, but that won't happen for 204s, so we + * use an EOF indicator on the HTTP side instead. + */ + if (icap->flags.no_content && icap->flags.http_server_eof) { + debug(81, 5) ("icapPconnTransferDone: no content, ret 1\n"); + return 1; + } + if (icapHttpReplyHdrState(icap) != 2) { + debug(81, + 5) ("icapPconnTransferDone: didn't see end of HTTP hdrs, ret 0\n"); + return 0; + } + if (icap->enc.null_body > -1) { + debug(81, 5) ("icapPconnTransferDone: no message body, ret 1\n"); + return 1; + } + if (icap->chunk_size == -2) { //AI: was != -2 ; and change content with bottom + /* zero end chunk reached */ + debug(81, 5) ("icapPconnTransferDone: got zero end chunk\n"); + return 1; + } + debug(81, 5) ("icapPconnTransferDone: didnt get zero end chunk yet\n"); //AI: change with second top condition + + return 0; +} + +static int +icapExpectedHttpReplyHdrSize(IcapStateData * icap) +{ + if (icap->enc.res_body > -1 && icap->enc.res_hdr > -1) + return (icap->enc.res_body - icap->enc.res_hdr); + if (icap->enc.null_body > -1 && icap->enc.res_hdr > -1) + return icap->enc.null_body - icap->enc.res_hdr; + /*The case we did not get res_hdr ..... */ + if (icap->enc.res_body > -1) + return icap->enc.res_body; + if (icap->enc.null_body > -1) + return icap->enc.null_body; + return -1; +} + +/* + * copied from httpReadReply() + * + * by the time this is called, the ICAP headers have already + * been read. + */ +void +icapReadReply(int fd, void *data) +{ + IcapStateData *icap = data; + StoreEntry *entry = icap->respmod.entry; + const request_t *request = icap->request; + int len; + debug(81, 5) ("icapReadReply: FD %d: icap %p.\n", fd, data); + if (icap->flags.no_content && !icap->flags.http_server_eof) { //AI + + return; + } + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + comm_close(fd); + return; + } + errno = 0; + statCounter.syscalls.sock.reads++; + len = memBufRead(fd, &icap->chunk_buf); + debug(81, 5) ("icapReadReply: FD %d: len %d.\n", fd, len); + if (len > 0) { + fd_bytes(fd, len, FD_READ); + kb_incr(&statCounter.icap.all.kbytes_in, len); + commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); + if (icap->chunk_buf.size < icap->chunk_buf.capacity) { + *(icap->chunk_buf.buf + icap->chunk_buf.size) = '\0'; + debug(81, 9) ("{%s}\n", icap->chunk_buf.buf); + } + } + if (len <= 0) { + debug(81, 2) ("icapReadReply: FD %d: read failure: %s.\n", + fd, xstrerror()); + if (ignoreErrno(errno)) { + debug(81, 2) ("icapReadReply: FD %d: ignored errno\n", fd); + commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); + } else if (entry->mem_obj->inmem_hi == 0) { + ErrorState *err; + debug(81, 2) ("icapReadReply: FD %d: generating error page\n", fd); + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, (request_t *)request); + err->xerrno = errno; + errorAppendEntry(entry, err); + comm_close(fd); + } else { + debug(81, 2) ("icapReadReply: FD %d: just calling comm_close()\n", + fd); + comm_close(fd); + } + return; + } + if (icapReadReply2(icap) < 0) + comm_close(fd); +} + +static int +icapReadReply2(IcapStateData * icap) +{ + StoreEntry *entry = icap->respmod.entry; + const request_t *request = icap->request; + debug(81, 3) ("icapReadReply2\n"); + if (icap->chunk_buf.size == 0 && entry->mem_obj->inmem_hi == 0) { + ErrorState *err; + err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE, (request_t *)request); + err->xerrno = errno; + errorAppendEntry(entry, err); + icap->flags.http_server_eof = 1; + return -1; + } + if (icap->chunk_buf.size == 0) { + /* Retrieval done. */ + if (icapHttpReplyHdrState(icap) < 2) + icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, + icap->chunk_buf.size); + icap->flags.http_server_eof = 1; + icapReadReply3(icap); + return 0; + } + if (icapHttpReplyHdrState(icap) == 0) { + int expect = icapExpectedHttpReplyHdrSize(icap); + int so_far = icap->http_header_bytes_read_so_far; + int needed = expect - so_far; + debug(81, 3) ("expect=%d\n", expect); + debug(81, 3) ("so_far=%d\n", so_far); + debug(81, 3) ("needed=%d\n", needed); + assert(needed < 0 || needed >= 0); + if (0 > expect) { + icapProcessHttpReplyHeader(icap, + icap->chunk_buf.buf, icap->chunk_buf.size); + } else if (0 == expect) { + /* + * this icap reply doesn't give us new HTTP headers + * so we must copy them from our copy + */ + debug(81, 1) ("WARNING: untested code at %s:%d\n", __FILE__, + __LINE__); + if (icap->respmod.req_hdr_copy.size) { /*For HTTP 0.9 we do not have headers */ + storeAppend(entry, + icap->respmod.req_hdr_copy.buf, + icap->respmod.req_hdr_copy.size); + } + icapProcessHttpReplyHeader(icap, icap->chunk_buf.buf, + icap->chunk_buf.size); + assert(icapHttpReplyHdrState(icap) == 2); + icap->chunk_size = 0; /*we are ready to read chunks of data now.... */ + } else if (needed) { + icapProcessHttpReplyHeader(icap, + icap->chunk_buf.buf, icap->chunk_buf.size); + if (icap->chunk_buf.size >= needed) { + storeAppend(entry, icap->chunk_buf.buf, needed); + so_far += needed; + xmemmove(icap->chunk_buf.buf, + icap->chunk_buf.buf + needed, + icap->chunk_buf.size - needed); + icap->chunk_buf.size -= needed; + assert(icapHttpReplyHdrState(icap) == 2); + icap->chunk_size = 0; + } else { + /* + * We don't have the full HTTP reply headers yet, so keep + * the partial reply buffered in 'chunk_buf' and wait + * for more. + */ + debug(81, 3) ("We don't have full Http headers.Schedule a new read\n"); + commSetSelect(icap->icap_fd, COMM_SELECT_READ, icapReadReply, icap, 0); + } + } + icap->http_header_bytes_read_so_far = so_far; + } + debug(81, 3) ("%s:%d: icap->chunk_buf.size=%d\n", __FILE__, __LINE__, + (int) icap->chunk_buf.size); + debug(81, 3) ("%s:%d: flags.no_content=%d\n", __FILE__, __LINE__, + icap->flags.no_content); + if (icap->flags.no_content) { + /* data from http.c is not chunked */ + if (!EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + debug(81, 3) ("copying %d bytes from chunk_buf to entry\n", + icap->chunk_buf.size); + storeAppend(entry, icap->chunk_buf.buf, icap->chunk_buf.size); + icap->chunk_buf.size = 0; + } + } else if (2 == icapHttpReplyHdrState(icap)) { + if (icap->chunk_buf.size) + icapParseChunkedBody(icap, (STRCB *) storeAppend, entry); + } + icapReadReply3(icap); + return 0; +} + +static void +icapReadReply3(IcapStateData * icap) +{ + StoreEntry *entry = icap->respmod.entry; + int fd = icap->icap_fd; + debug(81, 3) ("icapReadReply3\n"); + if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { + debug(81, 3) ("icapReadReply3: Entry Aborded\n"); + if (icap->flags.no_content) + icapStateFree(-1, icap); + else + comm_close(fd); + } else if (icapPconnTransferDone(fd, icap)) { + storeComplete(entry); + if (icap->flags.no_content) + icapStateFree(-1, icap); + else { + icapRespModKeepAliveOrClose(icap); + icapStateFree(-1, icap); + } + } else if (!icap->flags.no_content) { + /* Wait for EOF condition */ + commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); + debug(81, + 3) + ("icapReadReply3: Going to read mode data throught icapReadReply\n"); + } else { + debug(81, 3) ("icapReadReply3: Nothing\n"); + } +} Index: squid/src/main.c diff -u squid/src/main.c:1.72 squid/src/main.c:1.45.4.10 --- squid/src/main.c:1.72 Mon Oct 23 04:52:55 2006 +++ squid/src/main.c Fri Nov 3 10:47:14 2006 @@ -391,6 +391,9 @@ #else idnsShutdown(); #endif +#ifdef HS_FEAT_ICAP + icapClose(); +#endif redirectShutdown(); locationRewriteShutdown(); authenticateShutdown(); @@ -422,6 +425,9 @@ #endif redirectInit(); locationRewriteInit(); +#ifdef HS_FEAT_ICAP + icapInit(); +#endif authenticateInit(&Config.authConfig); externalAclInit(); #if USE_WCCP @@ -573,6 +579,9 @@ redirectInit(); locationRewriteInit(); errorMapInit(); +#ifdef HS_FEAT_ICAP + icapInit(); +#endif authenticateInit(&Config.authConfig); externalAclInit(); useragentOpenLog(); Index: squid/src/mem.c diff -u squid/src/mem.c:1.27 squid/src/mem.c:1.24.4.3 --- squid/src/mem.c:1.27 Sat May 20 15:50:55 2006 +++ squid/src/mem.c Fri May 26 11:21:32 2006 @@ -353,6 +353,13 @@ memDataInit(MEM_TLV, "storeSwapTLV", sizeof(tlv), 0); memDataInit(MEM_SWAP_LOG_DATA, "storeSwapLogData", sizeof(storeSwapLogData), 0); +#ifdef HS_FEAT_ICAP + memDataInit(MEM_ICAP_OPT_DATA, "IcapOptData", sizeof(IcapOptData), 0); + memDataInit(MEM_ICAP_SERVICE_LIST, "icap_service_list", sizeof(icap_service_list), 0); + memDataInit(MEM_ICAP_CLASS, "icap_class", sizeof(icap_class), 0); + memDataInit(MEM_ICAP_ACCESS, "icap_access", sizeof(icap_access), 0); +#endif + /* init string pools */ for (i = 0; i < mem_str_pool_count; i++) { StrPools[i].pool = memPoolCreate(StrPoolsAttrs[i].name, StrPoolsAttrs[i].obj_size); Index: squid/src/mk-string-arrays.pl diff -u squid/src/mk-string-arrays.pl:1.2 squid/src/mk-string-arrays.pl:1.2.180.1 --- squid/src/mk-string-arrays.pl:1.2 Mon Oct 23 08:04:21 2000 +++ squid/src/mk-string-arrays.pl Wed May 17 10:58:01 2006 @@ -16,6 +16,7 @@ $pat{'icp_opcode'} = "icp_opcode_str"; $pat{'swap_log_op'} = "swap_log_op_str"; $pat{'lookup_t'} = "lookup_t_str"; +$pat{'icap_service_t'} = "icap_service_type_str"; $state = 0; # start state while (<>) { Index: squid/src/pconn.c diff -u squid/src/pconn.c:1.10 squid/src/pconn.c:1.9.4.2 --- squid/src/pconn.c:1.10 Mon May 22 15:06:12 2006 +++ squid/src/pconn.c Fri May 26 11:21:32 2006 @@ -46,6 +46,9 @@ #define PCONN_HIST_SZ (1<<16) int client_pconn_hist[PCONN_HIST_SZ]; int server_pconn_hist[PCONN_HIST_SZ]; +#ifdef HS_FEAT_ICAP +int icap_server_pconn_hist[PCONN_HIST_SZ]; +#endif static PF pconnRead; static PF pconnTimeout; @@ -169,6 +172,20 @@ continue; storeAppendPrintf(e, "\t%4d %9d\n", i, server_pconn_hist[i]); } +#ifdef HS_FEAT_ICAP + storeAppendPrintf(e, + "\n" + "ICAP-server persistent connection counts:\n" + "\n" + "\treq/\n" + "\tconn count\n" + "\t---- ---------\n"); + for (i = 0; i < PCONN_HIST_SZ; i++) { + if (icap_server_pconn_hist[i] == 0) + continue; + storeAppendPrintf(e, "\t%4d %9d\n", i, icap_server_pconn_hist[i]); + } +#endif } /* ========== PUBLIC FUNCTIONS ============================================ */ @@ -183,6 +200,9 @@ for (i = 0; i < PCONN_HIST_SZ; i++) { client_pconn_hist[i] = 0; server_pconn_hist[i] = 0; +#ifdef HS_FEAT_ICAP + icap_server_pconn_hist[i] = 0; +#endif } pconn_data_pool = memPoolCreate("pconn_data", sizeof(struct _pconn)); pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int)); @@ -265,11 +285,15 @@ { if (i >= PCONN_HIST_SZ) i = PCONN_HIST_SZ - 1; - /* what == 0 for client, 1 for server */ + /* what == 0 for client, 1 for server, 2 for ICAP server */ if (what == 0) client_pconn_hist[i]++; else if (what == 1) server_pconn_hist[i]++; +#ifdef HS_FEAT_ICAP + else if (what == 2) + icap_server_pconn_hist[i]++; +#endif else assert(0); } Index: squid/src/protos.h diff -u squid/src/protos.h:1.129 squid/src/protos.h:1.74.4.11 --- squid/src/protos.h:1.129 Mon Oct 23 04:52:55 2006 +++ squid/src/protos.h Fri Nov 3 10:47:14 2006 @@ -302,6 +302,8 @@ /* http.c */ extern int httpCachable(method_t); extern void httpStart(FwdState *); +extern void httpParseReplyHeaders(const char *, http_reply *); +extern void httpProcessReplyHeader(HttpStateData *, const char *, int); extern int httpBuildRequestPrefix(request_t * request, request_t * orig_request, StoreEntry * entry, @@ -624,6 +626,7 @@ extern FREE *memBufFreeFunc(MemBuf * mb); /* puts report on MemBuf _module_ usage into mb */ extern void memBufReport(MemBuf * mb); +extern int memBufRead(int fd, MemBuf * mb); extern char *mime_get_header(const char *mime, const char *header); extern char *mime_get_header_field(const char *mime, const char *name, const char *prefix); @@ -1417,4 +1420,53 @@ void storeLocateVary(StoreEntry * e, int offset, const char *vary_data, String accept_encoding, STLVCB * callback, void *cbdata); void storeAddVary(const char *url, const char *log_url, const method_t method, const cache_key * key, const char *etag, const char *vary, const char *vary_headers, const char *accept_encoding); +#ifdef HS_FEAT_ICAP +/* + * icap_common.c + */ +void icapInit(void); +void icapClose(void); +void icapParseEncapsulated(IcapStateData *, const char *, const char *); +icap_service *icapService(icap_service_t, request_t *); +int icapConnect(IcapStateData *, CNCB *); +IcapStateData *icapAllocate(void); +PF icapStateFree; +PF icapConnectTimeout; +PF icapReadTimeout; +icap_service_t icapServiceToType(const char *); +const char *icapServiceToStr(const icap_service_t); +int icapCheckAcl(clientHttpRequest *); +size_t icapLineLength(const char *, int); +int icapReadHeader(int, IcapStateData *, int *); +int icapFindHeader(const char *, const char *, const char **, const char **); +int icapParseKeepAlive(const IcapStateData *, const char *, const char *); +void icapSetKeepAlive(IcapStateData * icap, const char *hdrs); +size_t icapParseChunkedBody(IcapStateData *, STRCB *, void *); +void icapAddAuthUserHeader(MemBuf *, auth_user_request_t *); +int icapParseStatusLine(const char *, int, int *, int *, const char **); + +/* + * icap_respmod.c + */ +IcapStateData *icapRespModStart(icap_service_t, request_t *, StoreEntry *, http_state_flags); +void icapSendRespMod(IcapStateData *, char *, int, int); +CNCB icapConnectOver; + +/* + * icap_reqmod.c + */ +IcapStateData *icapReqModStart(icap_service*, const char *, request_t *, int, struct timeval, struct in_addr, void *); + +/* icap_opt.c */ +void icapOptInit(void); +void icapOptShutdown(void); +void icapOptSetUnreachable(icap_service * s); + +/* X-Server-IP support */ +void icapAddOriginIP(MemBuf *, const char *); + +/* for debugging purposes only */ +void dump_icap_config(IcapConfig * cfg); +#endif + #endif /* SQUID_PROTOS_H */ Index: squid/src/squid.h diff -u squid/src/squid.h:1.36 squid/src/squid.h:1.24.8.7 --- squid/src/squid.h:1.36 Fri Sep 8 12:50:59 2006 +++ squid/src/squid.h Tue Sep 26 15:47:38 2006 @@ -38,6 +38,14 @@ #include "config.h" /* + * experimental defines for ICAP + */ +#ifdef HS_FEAT_ICAP +#define ICAP_PREVIEW 1 +#define SUPPORT_ICAP_204 0 +#endif + +/* * On some systems, FD_SETSIZE is set to something lower than the * actual number of files which can be opened. IRIX is one case, * NetBSD is another. So here we increase FD_SETSIZE to our Index: squid/src/stat.c diff -u squid/src/stat.c:1.38 squid/src/stat.c:1.26.8.10 --- squid/src/stat.c:1.38 Wed Nov 1 13:51:29 2006 +++ squid/src/stat.c Fri Nov 3 10:47:14 2006 @@ -804,6 +804,17 @@ storeAppendPrintf(sentry, "server.other.kbytes_out = %f/sec\n", XAVG(server.other.kbytes_out.kb)); +#ifdef HS_FEAT_ICAP + storeAppendPrintf(sentry, "icap.all.requests = %f/sec\n", + XAVG(icap.all.requests)); + storeAppendPrintf(sentry, "icap.all.errors = %f/sec\n", + XAVG(icap.all.errors)); + storeAppendPrintf(sentry, "icap.all.kbytes_in = %f/sec\n", + XAVG(icap.all.kbytes_in.kb)); + storeAppendPrintf(sentry, "icap.all.kbytes_out = %f/sec\n", + XAVG(icap.all.kbytes_out.kb)); +#endif + storeAppendPrintf(sentry, "icp.pkts_sent = %f/sec\n", XAVG(icp.pkts_sent)); storeAppendPrintf(sentry, "icp.pkts_recv = %f/sec\n", @@ -1188,6 +1199,17 @@ storeAppendPrintf(sentry, "server.other.kbytes_out = %d\n", (int) f->server.other.kbytes_out.kb); +#if HS_FEAT_ICAP + storeAppendPrintf(sentry, "icap.all.requests = %d\n", + (int) f->icap.all.requests); + storeAppendPrintf(sentry, "icap.all.errors = %d\n", + (int) f->icap.all.errors); + storeAppendPrintf(sentry, "icap.all.kbytes_in = %d\n", + (int) f->icap.all.kbytes_in.kb); + storeAppendPrintf(sentry, "icap.all.kbytes_out = %d\n", + (int) f->icap.all.kbytes_out.kb); +#endif + storeAppendPrintf(sentry, "icp.pkts_sent = %d\n", f->icp.pkts_sent); storeAppendPrintf(sentry, "icp.pkts_recv = %d\n", @@ -1488,8 +1510,6 @@ storeAppendPrintf(s, "\tme: %s:%d\n", inet_ntoa(conn->me.sin_addr), ntohs(conn->me.sin_port)); - storeAppendPrintf(s, "\tnrequests: %d\n", - conn->nrequests); storeAppendPrintf(s, "\tdefer: n %d, until %ld\n", conn->defer.n, (long int) conn->defer.until); } Index: squid/src/store.c diff -u squid/src/store.c:1.39 squid/src/store.c:1.21.10.10 --- squid/src/store.c:1.39 Sat Dec 9 22:51:20 2006 +++ squid/src/store.c Tue Dec 12 14:49:46 2006 @@ -1105,8 +1105,17 @@ MemObject *mem = e->mem_obj; assert(mem != NULL); assert(len >= 0); - assert(e->store_status == STORE_PENDING); mem->refresh_timestamp = squid_curtime; + debug(20, 3) ("storeAppend: '%s'\n", storeKeyText(e->hash.key)); + if (e->store_status != STORE_PENDING) { + /* + * if we're not STORE_PENDING, then probably we got aborted + * and there should be NO clients on this entry + */ + assert(EBIT_TEST(e->flags, ENTRY_ABORTED)); + assert(e->mem_obj->nclients == 0); + return; + } if (len) { debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", len, Index: squid/src/structs.h diff -u squid/src/structs.h:1.136 squid/src/structs.h:1.81.4.12 --- squid/src/structs.h:1.136 Wed Nov 29 08:52:51 2006 +++ squid/src/structs.h Tue Dec 12 14:49:47 2006 @@ -423,6 +423,23 @@ wordlist *args; }; +#if HS_FEAT_ICAP +struct _IcapConfig { + int onoff; + int preview_enable; + icap_service *service_head; + icap_class *class_head; + icap_access *access_head; + int preview_size; + int check_interval; + int send_client_ip; + int send_server_ip; + int send_auth_user; + char *auth_scheme; +}; + +#endif /* HS_FEAT_ICAP */ + struct _SquidConfig { struct { squid_off_t maxSize; @@ -810,6 +827,9 @@ #endif time_t refresh_stale_window; int umask; +#ifdef HS_FEAT_ICAP + IcapConfig icapcfg; +#endif }; struct _SquidConfig2 { @@ -891,6 +911,10 @@ comm_pending write_pending; squid_off_t bytes_read; squid_off_t bytes_written; + struct { + int uses; + int type; + } pconn; int uses; /* ie # req's over persistent conn */ struct _fde_disk { DWCB *wrt_handle; @@ -1095,6 +1119,131 @@ unsigned int originpeer:1; }; +#ifdef HS_FEAT_ICAP +struct _IcapStateData { + request_t *request; + http_state_flags http_flags; + HttpStateData *httpState; /* needed to parse HTTP headers only */ + int icap_fd; + int sc; + icap_service *current_service; + MemBuf icap_hdr; + struct { + int res_hdr; + int res_body; + int req_hdr; + int req_body; + int opt_body; + int null_body; + } enc; + int bytes_to_gobble; + int chunk_size; + MemBuf chunk_buf; + int preview_size; + squid_off_t fake_content_length; + int http_header_bytes_read_so_far; + struct { + const char *uri; /* URI for REQMODs */ + int client_fd; + struct timeval start; /* for logging */ + struct in_addr log_addr; /* for logging */ + int hdr_state; + MemBuf hdr_buf; + void *client_cookie; + struct { + MemBuf buf; + CBCB *callback; + void *callback_data; + char *callback_buf; + size_t callback_bufsize; + squid_off_t bytes_read; + } http_entity; + } reqmod; + struct { + StoreEntry *entry; + MemBuf buffer; + MemBuf req_hdr_copy; /* XXX barf */ + MemBuf resp_copy; /* XXX barf^max */ + squid_off_t res_body_sz; + } respmod; + struct { + unsigned int connect_requested:1; + unsigned int connect_pending:1; + unsigned int write_pending:1; + unsigned int keep_alive:1; + unsigned int http_server_eof:1; + unsigned int send_zero_chunk:1; + unsigned int got_reply:1; + unsigned int wait_for_reply:1; + unsigned int wait_for_preview_reply:1; + unsigned int preview_done:1; + unsigned int copy_response:1; + unsigned int no_content:1; + unsigned int reqmod_http_entity_eof:1; + } flags; +}; + +struct _icap_service { + icap_service *next; + char *name; /* name to be used when referencing ths service */ + char *uri; /* uri of server/service to use */ + char *type_name; /* {req|resp}mod_{pre|post}cache */ + + char *hostname; + unsigned short int port; + char *resource; + icap_service_t type; /* parsed type */ + icap_method_t method; + ushort bypass; /* flag: bypass allowed */ + ushort unreachable; /* flag: set to 1 if options request fails */ + IcapOptData *opt; /* temp data needed during opt request */ + struct { + unsigned int allow_204:1; + unsigned int need_x_client_ip:1; + unsigned int need_x_server_ip:1; + unsigned int need_x_authenticated_user:1; + } flags; + int preview; + String istag; + String transfer_preview; + String transfer_ignore; + String transfer_complete; + int max_connections; + int options_ttl; + int keep_alive; +}; + +struct _icap_service_list { + icap_service_list *next; + icap_service *services[16]; + int nservices; /* Number of services already used */ + int last_service_used; /* Last services used, use to do a round robin */ +}; + +struct _icap_class { + icap_class *next; + char *name; + wordlist *services; + icap_service_list *isl; + ushort hidden; /* for unnamed classes */ +}; + +struct _icap_access { + icap_access *next; + char *service_name; + icap_class *class; + acl_access *access; +}; + +struct _IcapOptData { + char *buf; + off_t offset; + size_t size; + off_t headlen; +}; + +#endif + struct _HttpStateData { StoreEntry *entry; request_t *request; @@ -1106,10 +1255,14 @@ int fd; http_state_flags flags; FwdState *fwd; +#ifdef HS_FEAT_ICAP + struct _IcapStateData *icap_writer; +#endif char *body_buf; int body_buf_sz; }; + struct _icpUdpData { struct sockaddr_in address; void *msg; @@ -1218,6 +1371,7 @@ unsigned int internal:1; unsigned int done_copying:1; unsigned int purging:1; + unsigned int did_icap_reqmod:1; unsigned int hit:1; } flags; struct { @@ -1232,6 +1386,9 @@ * zero.. [ahc] */ char readbuf[CLIENT_SOCK_SZ]; +#if HS_FEAT_ICAP + IcapStateData *icap_reqmod; +#endif }; struct _ConnStateData { @@ -1900,6 +2057,9 @@ unsigned int done_etag:1; /* We have done clientProcessETag on this, don't attempt it again */ char *urlgroup; /* urlgroup, returned by redirectors */ char *peer_domain; /* Configured peer forceddomain */ +#if HS_FEAT_ICAP + icap_class *class; +#endif BODY_HANDLER *body_reader; void *body_reader_data; String extacl_log; /* String to be used for access.log purposes */ @@ -2007,7 +2167,11 @@ kb_t kbytes_in; kb_t kbytes_out; } all , http, ftp, other; - } server; + } +#if HS_FEAT_ICAP + icap, +#endif + server; struct { int pkts_sent; int queries_sent; Index: squid/src/typedefs.h diff -u squid/src/typedefs.h:1.41 squid/src/typedefs.h:1.32.4.8 --- squid/src/typedefs.h:1.41 Sat Sep 2 07:17:45 2006 +++ squid/src/typedefs.h Tue Sep 26 15:47:39 2006 @@ -136,6 +136,15 @@ typedef struct _HttpBody HttpBody; typedef struct _HttpReply HttpReply; typedef struct _HttpStateData HttpStateData; +#ifdef HS_FEAT_ICAP +typedef struct _IcapStateData IcapStateData; +typedef struct _IcapConfig IcapConfig; +typedef struct _icap_service icap_service; +typedef struct _icap_service_list icap_service_list; +typedef struct _icap_class icap_class; +typedef struct _icap_access icap_access; +typedef struct _IcapOptData IcapOptData; +#endif typedef struct _icpUdpData icpUdpData; typedef struct _clientHttpRequest clientHttpRequest; typedef struct _ConnStateData ConnStateData; Index: squid/src/url.c diff -u squid/src/url.c:1.17 squid/src/url.c:1.14.10.4 --- squid/src/url.c:1.17 Sat Jun 17 16:51:19 2006 +++ squid/src/url.c Wed Jun 28 14:12:01 2006 @@ -103,6 +103,9 @@ "whois", "internal", "https", +#ifdef HS_FEAT_ICAP + "icap", +#endif "TOTAL" }; @@ -217,6 +220,10 @@ return PROTO_WHOIS; if (strcasecmp(s, "internal") == 0) return PROTO_INTERNAL; +#ifdef HS_FEAT_ICAP + if (strcasecmp(s, "icap") == 0) + return PROTO_ICAP; +#endif return PROTO_NONE; } @@ -240,6 +247,10 @@ return CACHE_HTTP_PORT; case PROTO_WHOIS: return 43; +#ifdef HS_FEAT_ICAP + case PROTO_ICAP: + return 1344; +#endif default: return 0; }