4a5a7a317c
- Implement a new option WITH_SQUID_SASL_AUTH, off by default - Update the ICAP core patchset to the latest CVS - Extensive portlintification and cleanups PR: ports/94642 Submitted by: Thomas-Martin Seck <tmseck@netcologne.de> (maintainer)
7140 lines
216 KiB
Diff
7140 lines
216 KiB
Diff
Patch 1 of 2 to integrate the icap-2_5 branch into the FreeBSD squid port.
|
|
|
|
Created by Thomas-Martin Seck <tmseck@netcologne.de>.
|
|
|
|
This patch only contains the parts of the original patchset that
|
|
actually implement the ICAP client functionality. The updates to
|
|
the build infrastructure are omitted to avoid the need to run an
|
|
autotools bootstrap. Instead, we simulate said bootstrapping with
|
|
a second patch, icap-2.5-bootstrap.patch.
|
|
|
|
The patchset was pulled from the project's CVS repository
|
|
at cvs.devel.squid-cache.org using
|
|
|
|
cvs diff -u -b -N -kk -rs2_5 -ricap-2_5
|
|
|
|
See also
|
|
<http://devel.squid-cache.org/cgi-bin/diff2/icap-2_5.patch?s2_5>
|
|
for the "official" auto-generated patchset.
|
|
|
|
See http://devel.squid-cache.org/icap/ for further information
|
|
about the ICAP client project.
|
|
|
|
Patch last updated: 2006-03-17
|
|
|
|
Index: errors/Azerbaijani/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Azerbaijani/ERR_ICAP_FAILURE
|
|
diff -N errors/Azerbaijani/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Azerbaijani/ERR_ICAP_FAILURE 8 Dec 2003 12:30:56 -0000 1.1.2.1
|
|
@@ -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: errors/Bulgarian/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Bulgarian/ERR_ICAP_FAILURE
|
|
diff -N errors/Bulgarian/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Bulgarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:56 -0000 1.1.2.1
|
|
@@ -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: errors/Catalan/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Catalan/ERR_ICAP_FAILURE
|
|
diff -N errors/Catalan/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Catalan/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1
|
|
@@ -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: errors/Czech/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Czech/ERR_ICAP_FAILURE
|
|
diff -N errors/Czech/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Czech/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1
|
|
@@ -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: errors/Danish/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Danish/ERR_ICAP_FAILURE
|
|
diff -N errors/Danish/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Danish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1
|
|
@@ -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: errors/Dutch/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Dutch/ERR_ICAP_FAILURE
|
|
diff -N errors/Dutch/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Dutch/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.1
|
|
@@ -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: errors/English/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/English/ERR_ICAP_FAILURE
|
|
diff -N errors/English/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/English/ERR_ICAP_FAILURE 8 Dec 2003 12:30:57 -0000 1.1.2.2
|
|
@@ -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: errors/Estonian/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Estonian/ERR_ICAP_FAILURE
|
|
diff -N errors/Estonian/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Estonian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1
|
|
@@ -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: errors/Finnish/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Finnish/ERR_ICAP_FAILURE
|
|
diff -N errors/Finnish/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Finnish/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1
|
|
@@ -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: errors/French/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/French/ERR_ICAP_FAILURE
|
|
diff -N errors/French/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/French/ERR_ICAP_FAILURE 8 Dec 2003 12:30:58 -0000 1.1.2.1
|
|
@@ -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: errors/German/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/German/ERR_ICAP_FAILURE
|
|
diff -N errors/German/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/German/ERR_ICAP_FAILURE 23 Mar 2004 08:20:05 -0000 1.1.2.2
|
|
@@ -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: errors/Greek/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Greek/ERR_ICAP_FAILURE
|
|
diff -N errors/Greek/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Greek/ERR_ICAP_FAILURE 24 Sep 2005 10:31:00 -0000 1.1.2.1
|
|
@@ -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: errors/Hebrew/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Hebrew/ERR_ICAP_FAILURE
|
|
diff -N errors/Hebrew/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Hebrew/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1
|
|
@@ -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: errors/Hungarian/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Hungarian/ERR_ICAP_FAILURE
|
|
diff -N errors/Hungarian/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Hungarian/ERR_ICAP_FAILURE 8 Dec 2003 12:30:59 -0000 1.1.2.1
|
|
@@ -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: errors/Italian/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Italian/ERR_ICAP_FAILURE
|
|
diff -N errors/Italian/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Italian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1
|
|
@@ -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: errors/Japanese/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Japanese/ERR_ICAP_FAILURE
|
|
diff -N errors/Japanese/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Japanese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1
|
|
@@ -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: errors/Korean/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Korean/ERR_ICAP_FAILURE
|
|
diff -N errors/Korean/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Korean/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1
|
|
@@ -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: errors/Lithuanian/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Lithuanian/ERR_ICAP_FAILURE
|
|
diff -N errors/Lithuanian/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Lithuanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1
|
|
@@ -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: errors/Polish/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Polish/ERR_ICAP_FAILURE
|
|
diff -N errors/Polish/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Polish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:00 -0000 1.1.2.1
|
|
@@ -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: errors/Portuguese/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Portuguese/ERR_ICAP_FAILURE
|
|
diff -N errors/Portuguese/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Portuguese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1
|
|
@@ -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: errors/Romanian/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Romanian/ERR_ICAP_FAILURE
|
|
diff -N errors/Romanian/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Romanian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:01 -0000 1.1.2.1
|
|
@@ -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: errors/Russian-1251/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Russian-1251/ERR_ICAP_FAILURE
|
|
diff -N errors/Russian-1251/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Russian-1251/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1
|
|
@@ -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: errors/Russian-koi8-r/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Russian-koi8-r/ERR_ICAP_FAILURE
|
|
diff -N errors/Russian-koi8-r/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Russian-koi8-r/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1
|
|
@@ -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: errors/Serbian/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Serbian/ERR_ICAP_FAILURE
|
|
diff -N errors/Serbian/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Serbian/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1
|
|
@@ -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: errors/Simplify_Chinese/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Simplify_Chinese/ERR_ICAP_FAILURE
|
|
diff -N errors/Simplify_Chinese/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Simplify_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:02 -0000 1.1.2.1
|
|
@@ -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: errors/Slovak/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Slovak/ERR_ICAP_FAILURE
|
|
diff -N errors/Slovak/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Slovak/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1
|
|
@@ -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: errors/Spanish/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Spanish/ERR_ICAP_FAILURE
|
|
diff -N errors/Spanish/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Spanish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1
|
|
@@ -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: errors/Swedish/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Swedish/ERR_ICAP_FAILURE
|
|
diff -N errors/Swedish/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Swedish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1
|
|
@@ -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: errors/Traditional_Chinese/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Traditional_Chinese/ERR_ICAP_FAILURE
|
|
diff -N errors/Traditional_Chinese/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Traditional_Chinese/ERR_ICAP_FAILURE 8 Dec 2003 12:31:03 -0000 1.1.2.1
|
|
@@ -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: errors/Turkish/ERR_ICAP_FAILURE
|
|
===================================================================
|
|
RCS file: errors/Turkish/ERR_ICAP_FAILURE
|
|
diff -N errors/Turkish/ERR_ICAP_FAILURE
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ errors/Turkish/ERR_ICAP_FAILURE 8 Dec 2003 12:31:04 -0000 1.1.2.1
|
|
@@ -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: include/util.h
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/include/util.h,v
|
|
retrieving revision 1.10
|
|
retrieving revision 1.10.30.2
|
|
diff -p -u -b -r1.10 -r1.10.30.2
|
|
--- include/util.h 17 Oct 2001 12:30:51 -0000 1.10
|
|
+++ include/util.h 6 Apr 2004 13:04:37 -0000 1.10.30.2
|
|
@@ -132,4 +132,12 @@ double drand48(void);
|
|
*/
|
|
int statMemoryAccounted(void);
|
|
|
|
+#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: lib/strcasestr.c
|
|
===================================================================
|
|
RCS file: lib/strcasestr.c
|
|
diff -N lib/strcasestr.c
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ lib/strcasestr.c 6 Apr 2004 13:04:38 -0000 1.1.2.1
|
|
@@ -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: lib/strnstr.c
|
|
===================================================================
|
|
RCS file: lib/strnstr.c
|
|
diff -N lib/strnstr.c
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ lib/strnstr.c 16 May 2005 20:52:40 -0000 1.1.2.2
|
|
@@ -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: src/MemBuf.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/MemBuf.c,v
|
|
retrieving revision 1.5.30.3
|
|
retrieving revision 1.5.44.8
|
|
diff -p -u -b -r1.5.30.3 -r1.5.44.8
|
|
--- src/MemBuf.c 26 Mar 2005 03:15:54 -0000 1.5.30.3
|
|
+++ src/MemBuf.c 28 Mar 2005 18:02:04 -0000 1.5.44.8
|
|
@@ -386,3 +386,15 @@ memBufReport(MemBuf * mb)
|
|
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)
|
|
+ mb->size += len;
|
|
+ return len;
|
|
+}
|
|
Index: src/cache_cf.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/cache_cf.c,v
|
|
retrieving revision 1.38.6.29
|
|
retrieving revision 1.38.6.11.2.22
|
|
diff -p -u -b -r1.38.6.29 -r1.38.6.11.2.22
|
|
--- src/cache_cf.c 27 Oct 2005 02:13:24 -0000 1.38.6.29
|
|
+++ src/cache_cf.c 23 Nov 2005 20:38:56 -0000 1.38.6.11.2.22
|
|
@@ -2198,6 +2198,587 @@ check_null_body_size_t(dlink_list bodyli
|
|
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, 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: src/cbdata.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/cbdata.c,v
|
|
retrieving revision 1.14.6.1
|
|
retrieving revision 1.14.32.2
|
|
diff -p -u -b -r1.14.6.1 -r1.14.32.2
|
|
--- src/cbdata.c 17 Jul 2003 02:13:28 -0000 1.14.6.1
|
|
+++ src/cbdata.c 14 Sep 2003 01:36:26 -0000 1.14.32.2
|
|
@@ -144,6 +144,10 @@ cbdataInit(void)
|
|
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: src/cf.data.pre
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/cf.data.pre,v
|
|
retrieving revision 1.49.2.86
|
|
retrieving revision 1.49.2.33.2.33
|
|
diff -p -u -b -r1.49.2.86 -r1.49.2.33.2.33
|
|
--- src/cf.data.pre 26 Feb 2006 03:13:57 -0000 1.49.2.86
|
|
+++ src/cf.data.pre 16 Feb 2006 17:21:45 -0000 1.49.2.33.2.33
|
|
@@ -2397,7 +2397,6 @@ DOC_START
|
|
ensure correct results it is best to set server_persisten_connections
|
|
to off when using this directive in such configurations.
|
|
DOC_END
|
|
-
|
|
NAME: reply_header_max_size
|
|
COMMENT: (KB)
|
|
TYPE: b_size_t
|
|
@@ -2716,6 +2715,187 @@ DOC_START
|
|
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: src/cf_gen_defines
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/cf_gen_defines,v
|
|
retrieving revision 1.5
|
|
retrieving revision 1.5.48.3
|
|
diff -p -u -b -r1.5 -r1.5.48.3
|
|
--- src/cf_gen_defines 3 Dec 2001 08:03:21 -0000 1.5
|
|
+++ src/cf_gen_defines 13 Mar 2005 17:58:44 -0000 1.5.48.3
|
|
@@ -18,12 +18,13 @@ BEGIN {
|
|
define["USE_UNLINKD"]="--enable-unlinkd"
|
|
define["USE_USERAGENT_LOG"]="--enable-useragent-log"
|
|
define["USE_WCCP"]="--enable-wccp"
|
|
+ define["HS_FEAT_ICAP"]="--enable-icap-support"
|
|
}
|
|
/^IFDEF:/ {
|
|
if (define[$2] != "")
|
|
- DEFINE=define[$2]
|
|
+ DEFINE = define[$2]
|
|
else
|
|
- DEFINE="-D" $2
|
|
+ DEFINE = "-D" $2
|
|
print "{\"" $2 "\", \"" DEFINE "\", "
|
|
print "#if " $2
|
|
print "1"
|
|
Index: src/client_side.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/client_side.c,v
|
|
retrieving revision 1.47.2.76
|
|
retrieving revision 1.47.2.28.2.40
|
|
diff -p -u -b -r1.47.2.76 -r1.47.2.28.2.40
|
|
--- src/client_side.c 11 Mar 2006 03:16:31 -0000 1.47.2.76
|
|
+++ src/client_side.c 6 Dec 2005 21:53:44 -0000 1.47.2.28.2.40
|
|
@@ -109,7 +109,7 @@ static const char *const crlf = "\r\n";
|
|
static CWCB clientWriteComplete;
|
|
static CWCB clientWriteBodyComplete;
|
|
static PF clientReadRequest;
|
|
-static PF connStateFree;
|
|
+PF connStateFree;
|
|
static PF requestTimeout;
|
|
static PF clientLifetimeTimeout;
|
|
static int clientCheckTransferDone(clientHttpRequest *);
|
|
@@ -136,20 +136,23 @@ static void clientSetKeepaliveFlag(clien
|
|
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);
|
|
static int clientRequestBodyTooLarge(squid_off_t clen);
|
|
static void clientProcessBody(ConnStateData * conn);
|
|
static void clientEatRequestBody(clientHttpRequest *);
|
|
-static BODY_HANDLER clientReadBody;
|
|
+BODY_HANDLER clientReadBody;
|
|
static void clientAbortBody(request_t * req);
|
|
+#if HS_FEAT_ICAP
|
|
+static int clientIcapReqMod(clientHttpRequest * http);
|
|
+#endif
|
|
|
|
static int
|
|
checkAccelOnly(clientHttpRequest * http)
|
|
@@ -392,6 +395,10 @@ clientRedirectDone(void *data, char *res
|
|
http->request = requestLink(new_request);
|
|
}
|
|
clientInterpretRequestHeaders(http);
|
|
+#if HS_FEAT_ICAP
|
|
+ if (Config.icapcfg.onoff)
|
|
+ icapCheckAcl(http);
|
|
+#endif
|
|
#if HEADERS_LOG
|
|
headersLog(0, 1, request->method, request);
|
|
#endif
|
|
@@ -931,11 +938,22 @@ httpRequestFree(void *data)
|
|
*H = http->next;
|
|
http->next = NULL;
|
|
dlinkDelete(&http->active, &ClientActiveRequests);
|
|
+#if HS_FEAT_ICAP
|
|
+ /*In the case that the upload of data breaks, we need this code here .... */
|
|
+ if (NULL != http->icap_reqmod) {
|
|
+ if (cbdataValid(http->icap_reqmod))
|
|
+ if (http->icap_reqmod->icap_fd > -1) {
|
|
+ comm_close(http->icap_reqmod->icap_fd);
|
|
+ }
|
|
+ cbdataUnlock(http->icap_reqmod);
|
|
+ http->icap_reqmod = NULL;
|
|
+ }
|
|
+#endif
|
|
cbdataFree(http);
|
|
}
|
|
|
|
/* This is a handler normally called by comm_close() */
|
|
-static void
|
|
+void
|
|
connStateFree(int fd, void *data)
|
|
{
|
|
ConnStateData *connState = data;
|
|
@@ -963,7 +976,6 @@ connStateFree(int fd, void *data)
|
|
} else
|
|
safe_free(connState->in.buf);
|
|
/* XXX account connState->in.buf */
|
|
- pconnHistCount(0, connState->nrequests);
|
|
cbdataFree(connState);
|
|
#ifdef _SQUID_LINUX_
|
|
/* prevent those nasty RST packets */
|
|
@@ -1108,7 +1120,7 @@ clientSetKeepaliveFlag(clientHttpRequest
|
|
}
|
|
}
|
|
|
|
-static int
|
|
+int
|
|
clientCheckContentLength(request_t * r)
|
|
{
|
|
switch (r->method) {
|
|
@@ -1127,7 +1139,7 @@ clientCheckContentLength(request_t * r)
|
|
/* NOT REACHED */
|
|
}
|
|
|
|
-static int
|
|
+int
|
|
clientCachable(clientHttpRequest * http)
|
|
{
|
|
request_t *req = http->request;
|
|
@@ -1153,7 +1165,7 @@ clientCachable(clientHttpRequest * http)
|
|
}
|
|
|
|
/* Return true if we can query our neighbors for this object */
|
|
-static int
|
|
+int
|
|
clientHierarchical(clientHttpRequest * http)
|
|
{
|
|
const char *url = http->uri;
|
|
@@ -2439,7 +2456,7 @@ clientProcessRequest2(clientHttpRequest
|
|
return LOG_TCP_HIT;
|
|
}
|
|
|
|
-static void
|
|
+void
|
|
clientProcessRequest(clientHttpRequest * http)
|
|
{
|
|
char *url = http->uri;
|
|
@@ -2457,6 +2466,11 @@ clientProcessRequest(clientHttpRequest *
|
|
debug(33, 4) ("clientProcessRequest: %s '%s'\n",
|
|
RequestMethodStr[r->method],
|
|
url);
|
|
+#if HS_FEAT_ICAP
|
|
+ if (clientIcapReqMod(http)) {
|
|
+ return;
|
|
+ }
|
|
+#endif
|
|
if (r->method == METHOD_CONNECT && !http->redirect.status) {
|
|
http->log_type = LOG_TCP_MISS;
|
|
sslStart(http, &http->out.size, &http->al.http.code);
|
|
@@ -3001,6 +3015,20 @@ clientReadRequest(int fd, void *data)
|
|
(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) {
|
|
@@ -3104,7 +3132,8 @@ clientReadRequest(int fd, void *data)
|
|
/* add to the client request queue */
|
|
for (H = &conn->chr; *H; H = &(*H)->next);
|
|
*H = http;
|
|
- conn->nrequests++;
|
|
+ F->pconn.uses++;
|
|
+ F->pconn.type = 0;
|
|
/*
|
|
* I wanted to lock 'http' here since its callback data for
|
|
* clientLifetimeTimeout(), but there's no logical place to
|
|
@@ -3274,7 +3303,7 @@ clientReadRequest(int fd, void *data)
|
|
}
|
|
|
|
/* 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;
|
|
@@ -3398,7 +3427,7 @@ clientProcessBody(ConnStateData * conn)
|
|
}
|
|
|
|
/* Abort a body request */
|
|
-static void
|
|
+void
|
|
clientAbortBody(request_t * request)
|
|
{
|
|
ConnStateData *conn = request->body_reader_data;
|
|
@@ -3432,7 +3469,7 @@ requestTimeout(int fd, void *data)
|
|
* 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
|
|
*/
|
|
@@ -3956,3 +3985,49 @@ varyEvaluateMatch(StoreEntry * entry, re
|
|
}
|
|
}
|
|
}
|
|
+
|
|
+#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);
|
|
+ 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
|
|
Index: src/comm.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/comm.c,v
|
|
retrieving revision 1.18.6.6
|
|
retrieving revision 1.18.6.2.12.9
|
|
diff -p -u -b -r1.18.6.6 -r1.18.6.2.12.9
|
|
--- src/comm.c 11 Sep 2005 02:13:22 -0000 1.18.6.6
|
|
+++ src/comm.c 23 Nov 2005 20:33:06 -0000 1.18.6.2.12.9
|
|
@@ -653,8 +653,8 @@ comm_close(int fd)
|
|
#endif
|
|
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) {
|
|
SSL_free(F->ssl);
|
|
Index: src/enums.h
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/enums.h,v
|
|
retrieving revision 1.29.2.18
|
|
retrieving revision 1.29.2.8.2.17
|
|
diff -p -u -b -r1.29.2.18 -r1.29.2.8.2.17
|
|
--- src/enums.h 12 Nov 2005 03:13:48 -0000 1.29.2.18
|
|
+++ src/enums.h 23 Nov 2005 20:38:56 -0000 1.29.2.8.2.17
|
|
@@ -93,6 +93,7 @@ typedef enum {
|
|
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;
|
|
@@ -438,6 +439,9 @@ typedef enum {
|
|
PROTO_WHOIS,
|
|
PROTO_INTERNAL,
|
|
PROTO_HTTPS,
|
|
+#if HS_FEAT_ICAP
|
|
+ PROTO_ICAP,
|
|
+#endif
|
|
PROTO_MAX
|
|
} protocol_t;
|
|
|
|
@@ -610,6 +614,12 @@ typedef enum {
|
|
MEM_TLV,
|
|
MEM_SWAP_LOG_DATA,
|
|
MEM_CLIENT_REQ_BUF,
|
|
+#if HS_FEAT_ICAP
|
|
+ MEM_ICAP_OPT_DATA,
|
|
+ MEM_ICAP_SERVICE_LIST,
|
|
+ MEM_ICAP_CLASS,
|
|
+ MEM_ICAP_ACCESS,
|
|
+#endif
|
|
MEM_MAX
|
|
} mem_type;
|
|
|
|
@@ -709,9 +719,14 @@ typedef enum {
|
|
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)
|
|
*/
|
|
@@ -742,4 +757,68 @@ enum {
|
|
|
|
#endif
|
|
|
|
+#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: src/forward.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/forward.c,v
|
|
retrieving revision 1.13.6.16
|
|
retrieving revision 1.13.6.3.2.15
|
|
diff -p -u -b -r1.13.6.16 -r1.13.6.3.2.15
|
|
--- src/forward.c 11 Mar 2006 03:16:31 -0000 1.13.6.16
|
|
+++ src/forward.c 30 Nov 2005 21:52:15 -0000 1.13.6.3.2.15
|
|
@@ -262,7 +262,8 @@ fwdConnectDone(int server_fd, int status
|
|
else
|
|
hierarchyNote(&fwdState->request->hier, fs->code, request->host);
|
|
fd_note(server_fd, storeUrl(fwdState->entry));
|
|
- fd_table[server_fd].uses++;
|
|
+ fd_table[server_fd].pconn.uses++;
|
|
+ fd_table[server_fd].pconn.type = 1;
|
|
if (fs->peer)
|
|
peerConnectSucceded(fs->peer);
|
|
fwdDispatch(fwdState);
|
|
@@ -704,6 +705,8 @@ fwdCheckDeferRead(int fd, void *data)
|
|
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),
|
|
@@ -742,6 +745,8 @@ fwdPeerClosed(int fd, void *data)
|
|
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);
|
|
@@ -758,7 +763,10 @@ fwdUnregister(int fd, FwdState * fwdStat
|
|
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: src/globals.h
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/globals.h,v
|
|
retrieving revision 1.14.6.7
|
|
retrieving revision 1.14.6.3.2.5
|
|
diff -p -u -b -r1.14.6.7 -r1.14.6.3.2.5
|
|
--- src/globals.h 14 Jun 2005 02:15:00 -0000 1.14.6.7
|
|
+++ src/globals.h 12 Sep 2005 18:34:41 -0000 1.14.6.3.2.5
|
|
@@ -165,6 +165,9 @@ extern char *WIN32_OS_string; /* NULL */
|
|
#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 */
|
|
|
|
Index: src/http.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/http.c,v
|
|
retrieving revision 1.17.6.32
|
|
retrieving revision 1.17.6.3.6.40
|
|
diff -p -u -b -r1.17.6.32 -r1.17.6.3.6.40
|
|
--- src/http.c 19 Oct 2005 02:13:21 -0000 1.17.6.32
|
|
+++ src/http.c 17 Feb 2006 12:45:21 -0000 1.17.6.3.6.40
|
|
@@ -47,7 +47,7 @@ static CWCB httpSendRequestEntry;
|
|
|
|
static PF httpReadReply;
|
|
static void httpSendRequest(HttpStateData *);
|
|
-static PF httpStateFree;
|
|
+PF httpStateFree;
|
|
static PF httpTimeout;
|
|
static void httpCacheNegatively(StoreEntry *);
|
|
static void httpMakePrivate(StoreEntry *);
|
|
@@ -55,11 +55,12 @@ static void httpMakePublic(StoreEntry *)
|
|
static int httpCachableReply(HttpStateData *);
|
|
static void httpMaybeRemovePublic(StoreEntry *, http_status);
|
|
|
|
-static void
|
|
+void
|
|
httpStateFree(int fd, void *data)
|
|
{
|
|
HttpStateData *httpState = data;
|
|
#if DELAY_POOLS
|
|
+ if (fd >= 0)
|
|
delayClearNoDelay(fd);
|
|
#endif
|
|
if (httpState == NULL)
|
|
@@ -79,6 +80,9 @@ httpStateFree(int fd, void *data)
|
|
requestUnlink(httpState->orig_request);
|
|
httpState->request = NULL;
|
|
httpState->orig_request = NULL;
|
|
+#if HS_FEAT_ICAP
|
|
+ cbdataUnlock(httpState->icap_writer);
|
|
+#endif
|
|
cbdataFree(httpState);
|
|
}
|
|
|
|
@@ -392,7 +396,7 @@ httpMakeVaryMark(request_t * request, Ht
|
|
}
|
|
|
|
/* rewrite this later using new interfaces @?@ */
|
|
-static void
|
|
+void
|
|
httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
|
|
{
|
|
StoreEntry *entry = httpState->entry;
|
|
@@ -527,24 +531,35 @@ httpPconnTransferDone(HttpStateData * ht
|
|
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;
|
|
@@ -568,6 +583,17 @@ httpReadReply(int fd, void *data)
|
|
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;
|
|
@@ -579,6 +605,37 @@ httpReadReply(int fd, void *data)
|
|
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);
|
|
@@ -595,7 +652,13 @@ httpReadReply(int fd, void *data)
|
|
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--);
|
|
@@ -625,6 +688,12 @@ httpReadReply(int fd, void *data)
|
|
} 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
|
|
@@ -677,7 +746,28 @@ httpReadReply(int fd, void *data)
|
|
EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
|
|
}
|
|
}
|
|
+#ifdef HS_FEAT_ICAP
|
|
+ if (httpState->icap_writer) {
|
|
+ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__);
|
|
+ if (cbdataValid(httpState->icap_writer)) {
|
|
+ icapSendRespMod(httpState->icap_writer, buf, len, 0);
|
|
+ httpState->icap_writer->fake_content_length += len;
|
|
+ }
|
|
+ } else
|
|
+#endif
|
|
storeAppend(entry, buf, len);
|
|
+
|
|
+
|
|
+ debug(11, 5) ("httpReadReply: after storeAppend FD %d read %d\n", fd, len);
|
|
+#if HS_FEAT_ICAP
|
|
+ if (httpState->icap_writer) {
|
|
+ if (!httpState->icap_writer->respmod.entry) {
|
|
+ debug(11, 3) ("httpReadReply: FD: %d: icap respmod aborded!\n", fd);
|
|
+ comm_close(fd);
|
|
+ return;
|
|
+ }
|
|
+ } else
|
|
+#endif
|
|
if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
|
|
/*
|
|
* the above storeAppend() call could ABORT this entry,
|
|
@@ -724,10 +814,21 @@ httpReadReply(int fd, void *data)
|
|
("httpReadReply: Excess data from \"%s %s\"\n",
|
|
RequestMethodStr[httpState->orig_request->method],
|
|
storeUrl(entry));
|
|
+#ifdef HS_FEAT_ICAP
|
|
+ if (httpState->icap_writer) {
|
|
+ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__);
|
|
+ icapSendRespMod(httpState->icap_writer, buf, len, 0);
|
|
+ httpState->icap_writer->fake_content_length += len;
|
|
+ } else
|
|
+#endif
|
|
storeAppend(entry, buf, len);
|
|
keep_alive = 0;
|
|
}
|
|
}
|
|
+#ifdef HS_FEAT_ICAP
|
|
+ if (httpState->icap_writer)
|
|
+ icapSendRespMod(httpState->icap_writer, NULL, 0, 1);
|
|
+#endif
|
|
if (keep_alive) {
|
|
/* yes we have to clear all these! */
|
|
commSetDefer(fd, NULL, NULL);
|
|
@@ -766,6 +867,10 @@ httpReadReply(int fd, void *data)
|
|
("httpReadReply: Excess data from \"%s %s\"\n",
|
|
RequestMethodStr[httpState->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;
|
|
@@ -776,6 +881,34 @@ httpReadReply(int fd, void *data)
|
|
}
|
|
}
|
|
|
|
+#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
|
|
@@ -803,6 +936,63 @@ httpSendComplete(int fd, char *bufnotuse
|
|
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);
|
|
+ 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
|
|
+ */
|
|
+ storeUnlockObject(httpState->entry);
|
|
+ storeUnregisterAbort(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
|
|
@@ -811,8 +1001,18 @@ httpSendComplete(int fd, char *bufnotuse
|
|
* 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;
|
|
}
|
|
@@ -1010,8 +1210,11 @@ httpBuildRequestHeader(request_t * reque
|
|
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))
|
|
@@ -1119,6 +1322,7 @@ httpStart(FwdState * fwd)
|
|
int fd = fwd->server_fd;
|
|
HttpStateData *httpState;
|
|
request_t *proxy_req;
|
|
+ /* ErrorState *err; */
|
|
request_t *orig_req = fwd->request;
|
|
debug(11, 3) ("httpStart: \"%s %s\"\n",
|
|
RequestMethodStr[orig_req->method],
|
|
@@ -1156,12 +1360,22 @@ httpStart(FwdState * fwd)
|
|
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: src/icap_common.c
|
|
===================================================================
|
|
RCS file: src/icap_common.c
|
|
diff -N src/icap_common.c
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ src/icap_common.c 16 Feb 2006 17:21:45 -0000 1.1.2.40
|
|
@@ -0,0 +1,817 @@
|
|
+/*
|
|
+ * $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);
|
|
+ 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: src/icap_opt.c
|
|
===================================================================
|
|
RCS file: src/icap_opt.c
|
|
diff -N src/icap_opt.c
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ src/icap_opt.c 16 Feb 2006 17:21:45 -0000 1.1.2.18
|
|
@@ -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: src/icap_reqmod.c
|
|
===================================================================
|
|
RCS file: src/icap_reqmod.c
|
|
diff -N src/icap_reqmod.c
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ src/icap_reqmod.c 17 Feb 2006 12:50:52 -0000 1.1.2.60
|
|
@@ -0,0 +1,981 @@
|
|
+
|
|
+/*
|
|
+ * $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;
|
|
+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 ICAP_PROXY_KEEP_ALIVE
|
|
+ /*
|
|
+ * Here it is possible becouse we are using as client_cookie the original http->conn
|
|
+ * if we will keep this code we must declare an icap->conn field........
|
|
+ * Will work if pipeline_prefetch is not enabled
|
|
+ * We are using a dummy ConnStateData structure, just to free
|
|
+ * old clientHttpRequest :-(
|
|
+ * OK,all this code is a hack and possibly must not exists in cvs ......
|
|
+ */
|
|
+
|
|
+ http->conn = icap->reqmod.client_cookie;
|
|
+ assert(http->conn->chr->next == NULL);
|
|
+ {
|
|
+ ConnStateData *dummyconn;
|
|
+ dummyconn = cbdataAlloc(ConnStateData);
|
|
+ dummyconn->fd = icap->reqmod.client_fd;
|
|
+ dummyconn->chr = http->conn->chr;
|
|
+ dummyconn->chr->conn = dummyconn;
|
|
+ comm_add_close_handler(dummyconn->fd, connStateFree, dummyconn);
|
|
+ }
|
|
+
|
|
+ http->conn->chr = http;
|
|
+
|
|
+#else
|
|
+ http->conn = cbdataAlloc(ConnStateData);
|
|
+ http->conn->fd = icap->reqmod.client_fd;
|
|
+ http->conn->in.size = 0;
|
|
+ http->conn->in.buf = NULL;
|
|
+ http->conn->log_addr = icap->reqmod.log_addr;
|
|
+ http->conn->chr = http;
|
|
+ comm_add_close_handler(http->conn->fd, connStateFree, http->conn);
|
|
+#endif
|
|
+ http->icap_reqmod = NULL;
|
|
+ return http;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * icapReqModInterpretHttpRequest
|
|
+ *
|
|
+ * Interpret an HTTP request that we read from the ICAP server.
|
|
+ * Create some "fake" clientHttpRequest and ConnStateData structures
|
|
+ * so we can pass this new request off to the routines in
|
|
+ * client_side.c.
|
|
+ */
|
|
+static void
|
|
+icapReqModInterpretHttpRequest(IcapStateData * icap, request_t * request)
|
|
+{
|
|
+ clientHttpRequest *http = icapReqModCreateClientState(icap, request);
|
|
+ if (NULL == http)
|
|
+ return;
|
|
+ /*
|
|
+ * bits from clientReadRequest
|
|
+ */
|
|
+ request->content_length = httpHeaderGetSize(&request->header,
|
|
+ HDR_CONTENT_LENGTH);
|
|
+ if (!urlCheckRequest(request) ||
|
|
+ httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) {
|
|
+ ErrorState *err;
|
|
+ err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED);
|
|
+ err->request = requestLink(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);
|
|
+ err->request = requestLink(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);
|
|
+ err->xerrno = xerrno;
|
|
+ err->request = requestLink(icap->request);
|
|
+ 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->my_addr = icap->request->my_addr;
|
|
+ request->my_port = icap->request->my_port;
|
|
+ request->class = icap->request->class;
|
|
+ if (icap->request->auth_user_request != NULL) {
|
|
+ /* Copy authentification info in new request */
|
|
+ request->auth_user_request = icap->request->auth_user_request;
|
|
+ authenticateAuthUserRequestLock(request->auth_user_request);
|
|
+ }
|
|
+#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);
|
|
+ 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->reqmod.http_entity.bytes_read >= icap->request->content_length)
|
|
+ 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 && icap->reqmod.http_entity.buf.size) {
|
|
+ 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: src/icap_respmod.c
|
|
===================================================================
|
|
RCS file: src/icap_respmod.c
|
|
diff -N src/icap_respmod.c
|
|
--- /dev/null 1 Jan 1970 00:00:00 -0000
|
|
+++ src/icap_respmod.c 17 Feb 2006 12:45:23 -0000 1.1.2.64
|
|
@@ -0,0 +1,1057 @@
|
|
+
|
|
+/*
|
|
+ * $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 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
|
|
+ if (service->flags.allow_204)
|
|
+ memBufPrintf(mb, "Allow: 204\r\n");
|
|
+}
|
|
+
|
|
+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);
|
|
+ err->request = requestLink(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);
|
|
+ err->request = requestLink(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);
|
|
+ err->request = requestLink(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");
|
|
+ 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)
|
|
+ comm_close(fd);
|
|
+ /*
|
|
+ * 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);
|
|
+ err->request = requestLink(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);
|
|
+ err->request = requestLink(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) {
|
|
+ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
|
|
+ err->xerrno = errno;
|
|
+ if (cbdataValid(icap))
|
|
+ err->request = requestLink(icap->request);
|
|
+ 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);
|
|
+ err->xerrno = errno;
|
|
+ err->request = requestLink(icap->request);
|
|
+ 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;
|
|
+ if (!icap->flags.keep_alive) {
|
|
+ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__,
|
|
+ __LINE__);
|
|
+ comm_close(fd);
|
|
+ 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);
|
|
+ pconnPush(fd, icap->current_service->hostname, icap->current_service->port);
|
|
+ icap->icap_fd = -1;
|
|
+ icapStateFree(-1, icap);
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+ * 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);
|
|
+ err->request = requestLink((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);
|
|
+ err->xerrno = errno;
|
|
+ err->request = requestLink((request_t *) request);
|
|
+ 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");
|
|
+ comm_close(fd);
|
|
+ } else if (icapPconnTransferDone(fd, icap)) {
|
|
+ storeComplete(entry);
|
|
+ icapRespModKeepAliveOrClose(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: src/main.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/main.c,v
|
|
retrieving revision 1.28.6.25
|
|
retrieving revision 1.28.6.8.2.11
|
|
diff -p -u -b -r1.28.6.25 -r1.28.6.8.2.11
|
|
--- src/main.c 28 Jun 2005 02:16:51 -0000 1.28.6.25
|
|
+++ src/main.c 12 Sep 2005 18:34:41 -0000 1.28.6.8.2.11
|
|
@@ -350,6 +350,9 @@ mainReconfigure(void)
|
|
#else
|
|
idnsShutdown();
|
|
#endif
|
|
+#ifdef HS_FEAT_ICAP
|
|
+ icapClose();
|
|
+#endif
|
|
redirectShutdown();
|
|
authenticateShutdown();
|
|
externalAclShutdown();
|
|
@@ -378,6 +381,9 @@ mainReconfigure(void)
|
|
idnsInit();
|
|
#endif
|
|
redirectInit();
|
|
+#ifdef HS_FEAT_ICAP
|
|
+ icapInit();
|
|
+#endif
|
|
authenticateInit(&Config.authConfig);
|
|
externalAclInit();
|
|
#if USE_WCCP
|
|
@@ -507,6 +513,9 @@ mainInitialize(void)
|
|
idnsInit();
|
|
#endif
|
|
redirectInit();
|
|
+#ifdef HS_FEAT_ICAP
|
|
+ icapInit();
|
|
+#endif
|
|
authenticateInit(&Config.authConfig);
|
|
externalAclInit();
|
|
useragentOpenLog();
|
|
Index: src/mem.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/mem.c,v
|
|
retrieving revision 1.13
|
|
retrieving revision 1.13.28.2
|
|
diff -p -u -b -r1.13 -r1.13.28.2
|
|
--- src/mem.c 7 Sep 2001 23:55:49 -0000 1.13
|
|
+++ src/mem.c 27 Jun 2003 01:15:18 -0000 1.13.28.2
|
|
@@ -243,6 +243,13 @@ memInit(void)
|
|
memDataInit(MEM_CLIENT_REQ_BUF, "clientRequestBuffer", CLIENT_REQ_BUF_SZ, 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: src/mk-string-arrays.pl
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/mk-string-arrays.pl,v
|
|
retrieving revision 1.2
|
|
retrieving revision 1.2.140.1
|
|
diff -p -u -b -r1.2 -r1.2.140.1
|
|
--- src/mk-string-arrays.pl 23 Oct 2000 15:04:21 -0000 1.2
|
|
+++ src/mk-string-arrays.pl 4 Apr 2003 16:55:44 -0000 1.2.140.1
|
|
@@ -16,6 +16,7 @@ $pat{'err_type'} = "err_type_str";
|
|
$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: src/pconn.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/pconn.c,v
|
|
retrieving revision 1.6.38.2
|
|
retrieving revision 1.6.60.2
|
|
diff -p -u -b -r1.6.38.2 -r1.6.60.2
|
|
--- src/pconn.c 16 Dec 2003 03:13:59 -0000 1.6.38.2
|
|
+++ src/pconn.c 23 Nov 2005 20:33:07 -0000 1.6.60.2
|
|
@@ -46,6 +46,9 @@ struct _pconn {
|
|
#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;
|
|
@@ -159,6 +162,20 @@ pconnHistDump(StoreEntry * e)
|
|
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 ============================================ */
|
|
@@ -173,6 +190,9 @@ pconnInit(void)
|
|
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));
|
|
@@ -248,11 +268,15 @@ pconnHistCount(int what, int i)
|
|
{
|
|
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: src/protos.h
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/protos.h,v
|
|
retrieving revision 1.41.6.34
|
|
retrieving revision 1.41.6.13.2.38
|
|
diff -p -u -b -r1.41.6.34 -r1.41.6.13.2.38
|
|
--- src/protos.h 26 Feb 2006 03:13:57 -0000 1.41.6.34
|
|
+++ src/protos.h 16 Feb 2006 17:21:45 -0000 1.41.6.13.2.38
|
|
@@ -292,6 +292,8 @@ extern void whoisStart(FwdState *);
|
|
/* 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,
|
|
@@ -614,6 +616,7 @@ extern void memBufVPrintf(MemBuf * mb, c
|
|
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);
|
|
@@ -1341,4 +1344,53 @@ extern void externalAclShutdown(void);
|
|
extern int externalAclRequiresAuth(void *acl_data);
|
|
extern char *strtokFile(void);
|
|
|
|
+#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: src/squid.h
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/squid.h,v
|
|
retrieving revision 1.13.6.9
|
|
retrieving revision 1.13.6.6.2.11
|
|
diff -p -u -b -r1.13.6.9 -r1.13.6.6.2.11
|
|
--- src/squid.h 11 Mar 2006 03:16:31 -0000 1.13.6.9
|
|
+++ src/squid.h 15 May 2005 20:10:33 -0000 1.13.6.6.2.11
|
|
@@ -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: src/stat.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/stat.c,v
|
|
retrieving revision 1.13.6.14
|
|
retrieving revision 1.13.6.7.2.7
|
|
diff -p -u -b -r1.13.6.14 -r1.13.6.7.2.7
|
|
--- src/stat.c 30 Mar 2005 02:17:46 -0000 1.13.6.14
|
|
+++ src/stat.c 23 Nov 2005 20:33:07 -0000 1.13.6.7.2.7
|
|
@@ -775,6 +775,17 @@ statAvgDump(StoreEntry * sentry, int min
|
|
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",
|
|
@@ -1160,6 +1171,17 @@ statCountersDump(StoreEntry * sentry)
|
|
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",
|
|
@@ -1459,8 +1481,6 @@ statClientRequests(StoreEntry * s)
|
|
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: src/store.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/store.c,v
|
|
retrieving revision 1.16.6.9
|
|
retrieving revision 1.16.6.2.2.8
|
|
diff -p -u -b -r1.16.6.9 -r1.16.6.2.2.8
|
|
--- src/store.c 2 Sep 2005 02:13:43 -0000 1.16.6.9
|
|
+++ src/store.c 12 Sep 2005 18:34:41 -0000 1.16.6.2.2.8
|
|
@@ -520,7 +520,16 @@ storeAppend(StoreEntry * e, const char *
|
|
MemObject *mem = e->mem_obj;
|
|
assert(mem != NULL);
|
|
assert(len >= 0);
|
|
- assert(e->store_status == STORE_PENDING);
|
|
+ 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: src/structs.h
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/structs.h,v
|
|
retrieving revision 1.48.2.46
|
|
retrieving revision 1.48.2.9.2.49
|
|
diff -p -u -b -r1.48.2.46 -r1.48.2.9.2.49
|
|
--- src/structs.h 11 Mar 2006 03:16:31 -0000 1.48.2.46
|
|
+++ src/structs.h 16 Feb 2006 17:21:45 -0000 1.48.2.9.2.49
|
|
@@ -384,6 +384,23 @@ struct _RemovalPolicySettings {
|
|
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;
|
|
@@ -714,6 +730,9 @@ struct _SquidConfig {
|
|
char *store_dir_select_algorithm;
|
|
int sleep_after_fork; /* microseconds */
|
|
external_acl *externalAclHelperList;
|
|
+#ifdef HS_FEAT_ICAP
|
|
+ IcapConfig icapcfg;
|
|
+#endif
|
|
};
|
|
|
|
struct _SquidConfig2 {
|
|
@@ -788,7 +807,10 @@ struct _fde {
|
|
} flags;
|
|
squid_off_t bytes_read;
|
|
squid_off_t bytes_written;
|
|
- int uses; /* ie # req's over persistent conn */
|
|
+ struct {
|
|
+ int uses;
|
|
+ int type;
|
|
+ } pconn;
|
|
struct _fde_disk {
|
|
DWCB *wrt_handle;
|
|
void *wrt_handle_data;
|
|
@@ -983,6 +1005,131 @@ struct _http_state_flags {
|
|
unsigned int request_sent: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;
|
|
@@ -994,10 +1141,14 @@ struct _HttpStateData {
|
|
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;
|
|
@@ -1092,6 +1242,7 @@ struct _clientHttpRequest {
|
|
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 {
|
|
@@ -1107,6 +1253,9 @@ struct _clientHttpRequest {
|
|
} redirect;
|
|
dlink_node active;
|
|
squid_off_t maxBodySize;
|
|
+#if HS_FEAT_ICAP
|
|
+ IcapStateData *icap_reqmod;
|
|
+#endif
|
|
};
|
|
|
|
struct _ConnStateData {
|
|
@@ -1134,7 +1283,6 @@ struct _ConnStateData {
|
|
struct sockaddr_in me;
|
|
struct in_addr log_addr;
|
|
char rfc931[USER_IDENT_SZ];
|
|
- int nrequests;
|
|
struct {
|
|
int n;
|
|
time_t until;
|
|
@@ -1685,6 +1833,9 @@ struct _request_t {
|
|
char *peer_login; /* Configured peer login:password */
|
|
time_t lastmod; /* Used on refreshes */
|
|
const char *vary_headers; /* Used when varying entities are detected. Changes how the store key is calculated */
|
|
+#if HS_FEAT_ICAP
|
|
+ icap_class *class;
|
|
+#endif
|
|
BODY_HANDLER *body_reader;
|
|
void *body_reader_data;
|
|
};
|
|
@@ -1784,7 +1940,11 @@ struct _StatCounters {
|
|
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: src/typedefs.h
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/typedefs.h,v
|
|
retrieving revision 1.25.6.8
|
|
retrieving revision 1.25.6.1.6.13
|
|
diff -p -u -b -r1.25.6.8 -r1.25.6.1.6.13
|
|
--- src/typedefs.h 27 Mar 2005 02:16:17 -0000 1.25.6.8
|
|
+++ src/typedefs.h 28 Mar 2005 18:05:08 -0000 1.25.6.1.6.13
|
|
@@ -131,6 +131,15 @@ typedef struct _HttpHeaderStat HttpHeade
|
|
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: src/url.c
|
|
===================================================================
|
|
RCS file: /cvsroot/squid/squid/src/url.c,v
|
|
retrieving revision 1.7.6.7
|
|
retrieving revision 1.7.6.5.2.2
|
|
diff -p -u -b -r1.7.6.7 -r1.7.6.5.2.2
|
|
--- src/url.c 11 Mar 2006 03:16:31 -0000 1.7.6.7
|
|
+++ src/url.c 23 Nov 2005 20:38:56 -0000 1.7.6.5.2.2
|
|
@@ -103,6 +103,9 @@ const char *ProtocolStr[] =
|
|
"whois",
|
|
"internal",
|
|
"https",
|
|
+#ifdef HS_FEAT_ICAP
|
|
+ "icap",
|
|
+#endif
|
|
"TOTAL"
|
|
};
|
|
|
|
@@ -221,6 +224,10 @@ urlParseProtocol(const char *s)
|
|
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;
|
|
}
|
|
|
|
@@ -244,6 +251,10 @@ urlDefaultPort(protocol_t p)
|
|
return CACHE_HTTP_PORT;
|
|
case PROTO_WHOIS:
|
|
return 43;
|
|
+#ifdef HS_FEAT_ICAP
|
|
+ case PROTO_ICAP:
|
|
+ return 1344;
|
|
+#endif
|
|
default:
|
|
return 0;
|
|
}
|