From owner-freebsd-ports-bugs@FreeBSD.ORG Sat Aug 5 19:00:31 2006 Return-Path: X-Original-To: freebsd-ports-bugs@hub.freebsd.org Delivered-To: freebsd-ports-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id A9AF316A4E6 for ; Sat, 5 Aug 2006 19:00:31 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 90A5A43D58 for ; Sat, 5 Aug 2006 19:00:30 +0000 (GMT) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.13.4/8.13.4) with ESMTP id k75J0Upg078062 for ; Sat, 5 Aug 2006 19:00:30 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.13.4/8.13.4/Submit) id k75J0Uhe078060; Sat, 5 Aug 2006 19:00:30 GMT (envelope-from gnats) Resent-Date: Sat, 5 Aug 2006 19:00:30 GMT Resent-Message-Id: <200608051900.k75J0Uhe078060@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-ports-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Thomas-Martin Seck Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 8075616A4DF for ; Sat, 5 Aug 2006 18:56:45 +0000 (UTC) (envelope-from tmseck@netcologne.de) Received: from smtp2.netcologne.de (smtp2.netcologne.de [194.8.194.112]) by mx1.FreeBSD.org (Postfix) with ESMTP id 419D443D5C for ; Sat, 5 Aug 2006 18:56:39 +0000 (GMT) (envelope-from tmseck@netcologne.de) Received: from laurel.tmseck.homedns.org (xdsl-213-196-254-205.netcologne.de [213.196.254.205]) by smtp2.netcologne.de (Postfix) with SMTP id 91FEF42FA for ; Sat, 5 Aug 2006 20:56:32 +0200 (MEST) Received: (qmail 15703 invoked from network); 5 Aug 2006 18:56:32 -0000 Received: from unknown (HELO hardy.tmseck.homedns.org) (192.168.1.2) by 0 with SMTP; 5 Aug 2006 18:56:32 -0000 Received: from hardy.tmseck.homedns.org (localhost [127.0.0.1]) by hardy.tmseck.homedns.org (8.13.6/8.13.6) with ESMTP id k75IuU4N058453; Sat, 5 Aug 2006 20:56:30 +0200 (CEST) (envelope-from tmseck@netcologne.de) Received: (from thomas@localhost) by hardy.tmseck.homedns.org (8.13.6/8.13.6/Submit) id k75IuO8t058452; Sat, 5 Aug 2006 20:56:24 +0200 (CEST) (envelope-from tmseck@netcologne.de) Message-Id: <200608051856.k75IuO8t058452@hardy.tmseck.homedns.org> Date: Sat, 5 Aug 2006 20:56:24 +0200 (CEST) From: Thomas-Martin Seck To: FreeBSD-gnats-submit@FreeBSD.org X-Send-Pr-Version: 3.113 Cc: Subject: ports/101422: [Maintainer] www/squid26: update to 2.6.STABLE2, add ICAP support X-BeenThere: freebsd-ports-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: Thomas-Martin Seck List-Id: Ports bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 05 Aug 2006 19:00:31 -0000 >Number: 101422 >Category: ports >Synopsis: [Maintainer] www/squid26: update to 2.6.STABLE2, add ICAP support >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-ports-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: maintainer-update >Submitter-Id: current-users >Arrival-Date: Sat Aug 05 19:00:29 GMT 2006 >Closed-Date: >Last-Modified: >Originator: Thomas-Martin Seck >Release: FreeBSD 6.1-STABLE i386 >Organization: a private site in Germany >Environment: FreeBSD ports collection as of August 5, 2006. >Description: - Update to 2.6.STABLE2. - Include most of the post-STABLE2 changes/bugfixes published at . - Remove the local fix for the problem that ipfw(4) support was not working because the problem was fixed upstream. - Remove the SQUID_IPFW option again, ipfw(4) should now work out of the box as in earlier Squid versions. - Add ICAP support. Added files: files/icap-2.6-bootstrap.patch files/icap-2.6-core.patch Removed files: files/patch-changeset.10801 files/patch-src-client_side.c >How-To-Repeat: >Fix: Apply this patch: Index: distinfo =================================================================== --- distinfo (.../www/squid26) (revision 984) +++ distinfo (.../local/squid26) (revision 984) @@ -1,6 +1,33 @@ -MD5 (squid2.6/squid-2.6.STABLE1.tar.bz2) = d6bb23a67b0475cca11d4af8d574d054 -SHA256 (squid2.6/squid-2.6.STABLE1.tar.bz2) = a9efea26f1c3966dff69f80c271a73562afed5c65929d69dab713308dd402ba9 -SIZE (squid2.6/squid-2.6.STABLE1.tar.bz2) = 1212556 -MD5 (squid2.6/10799.patch) = 3a3d941b0d256a1da9ad8f2430da4e2b -SHA256 (squid2.6/10799.patch) = 0f010962faae226a21761996df7c487ed6ca2d51458e6941a9f2a5b69aa3e6c3 -SIZE (squid2.6/10799.patch) = 1288 +MD5 (squid2.6/squid-2.6.STABLE2.tar.bz2) = 838492f95de0964e31e633bfa07a0682 +SHA256 (squid2.6/squid-2.6.STABLE2.tar.bz2) = 8ceb890161648f26b3c16f07e2e1beecad2b0de0f3f1793142a32b5aa25ef8e6 +SIZE (squid2.6/squid-2.6.STABLE2.tar.bz2) = 1222355 +MD5 (squid2.6/10894.patch) = 8b66b5f16a1563e15690f0b8ecddd5b9 +SHA256 (squid2.6/10894.patch) = cab38b13352135433fb85e04a76182ca96b03e4c6df487b6de25e73b1bf71ffd +SIZE (squid2.6/10894.patch) = 1303 +MD5 (squid2.6/10895.patch) = 234efdec5f31662d07188829bcd28721 +SHA256 (squid2.6/10895.patch) = 8d12a761c64ca9b8c7f18e4393f47d22e4bd772a788bcbea61c81f48647a8835 +SIZE (squid2.6/10895.patch) = 5885 +MD5 (squid2.6/10896.patch) = 187944a1b8b3e796f5ae5d18b2d66284 +SHA256 (squid2.6/10896.patch) = 0a8d6dd65a3d9d76b9aeb85135f560abe9a53ce17891e866d0e38f14aa4f436b +SIZE (squid2.6/10896.patch) = 1100 +MD5 (squid2.6/10898.patch) = 830d6788b613b93221d7661bf19946a1 +SHA256 (squid2.6/10898.patch) = f6b95725ba7d78fdbd41a8589229acf9b2128deb109adbe13d5f7e0d81d192f7 +SIZE (squid2.6/10898.patch) = 3611 +MD5 (squid2.6/10899.patch) = dee2d52860f2ee66bb4ecbfbdbf1f66f +SHA256 (squid2.6/10899.patch) = c3c8b334fe5d6f3e5037e6db4cf6e5049076e07e44df2f5cd8f0f4a9d0fde5fd +SIZE (squid2.6/10899.patch) = 1641 +MD5 (squid2.6/10900.patch) = 5bb888d3a4041572bc38f9cf6e177e5e +SHA256 (squid2.6/10900.patch) = 48bc152e1986efb4ef58b64409d062b1a01a09cba0c4dcb31b719bb595c11751 +SIZE (squid2.6/10900.patch) = 917 +MD5 (squid2.6/10901.patch) = 16d5d2bf91a868da672daa8c24f0ca74 +SHA256 (squid2.6/10901.patch) = f0fe03966617b895e1fae361e8aa8557a51433c1c4eec92599424f0d2ece545e +SIZE (squid2.6/10901.patch) = 1709 +MD5 (squid2.6/10902.patch) = f9b7dd81b26a17e52175d06161b00ee6 +SHA256 (squid2.6/10902.patch) = d63c4dca13096ce3185ad6b15de2f897f24018581a69e611e9a00dbf5842ab59 +SIZE (squid2.6/10902.patch) = 1439 +MD5 (squid2.6/10903.patch) = 008c0c4e8b08def33f48f2706d13433e +SHA256 (squid2.6/10903.patch) = 727dbe9951d915f7b846ed905148ce4177703dbbfa493d45b5efbb1da61851e0 +SIZE (squid2.6/10903.patch) = 968 +MD5 (squid2.6/10904.patch) = 20d803cd8ede1132d8f8009cc93ca816 +SHA256 (squid2.6/10904.patch) = 36bf8c00084988a850bcbe816cf52a7002e8516806fb7a17438eaeeb389457fa +SIZE (squid2.6/10904.patch) = 37791 Index: files/patch-src-client_side.c =================================================================== --- files/patch-src-client_side.c (.../www/squid26) (revision 984) +++ files/patch-src-client_side.c (.../local/squid26) (revision 984) @@ -1,16 +0,0 @@ ---- ./src/client_side.c.orig Thu Jul 6 11:44:18 2006 -+++ ./src/client_side.c Thu Jul 6 11:52:07 2006 -@@ -4490,6 +4490,13 @@ - return -1; - } - } -+#elif IPFW_TRANSPARENT -+static int inline -+clientNatLookup(ConnStateData * conn) -+{ -+ static time_t last_reported = 0; -+ return 0; -+} - #else - static int inline - clientNatLookup(ConnStateData * conn) Index: files/patch-changeset.10801 =================================================================== --- files/patch-changeset.10801 (.../www/squid26) (revision 984) +++ files/patch-changeset.10801 (.../local/squid26) (revision 984) @@ -1,284 +0,0 @@ ---------------------- -PatchSet 10801 -Date: 2006/07/04 21:51:15 -Author: hno -Branch: HEAD -Tag: (none) -Log: -Bug #1650: transparent interception "Unable to forward this request at this time" - -this patch clears up some confusion between accelerated and transparently -intercepted requests, clearly separating the two cases. - -With this patch the --enable-auth-on-acceleration is removed again as it -no longer serves any purpose as there no longer is any conflict between -the processing accelerated and transparently intercepted requests. - -Members: - configure:1.388->1.389 - configure.in:1.384->1.385 - include/autoconf.h.in:1.153->1.154 - src/acl.c:1.303->1.304 - src/client_side.c:1.657->1.658 - src/structs.h:1.489->1.490 - -Index: squid/configure -=================================================================== -RCS file: /cvsroot/squid/squid/configure,v -retrieving revision 1.388 -retrieving revision 1.389 -diff -u -r1.388 -r1.389 ---- configure 1 Jul 2006 18:42:04 -0000 1.388 -+++ configure 4 Jul 2006 21:51:15 -0000 1.389 -@@ -907,8 +907,6 @@ - --enable-forw-via-db Enable Forw/Via database - --enable-cache-digests Use Cache Digests - see http://www.squid-cache.org/FAQ/FAQ-16.html -- --enable-auth-on-acceleration -- Enable authentication in accelerators - --enable-default-err-language=lang - Select default language for Error pages (see - errors directory) -@@ -4220,20 +4218,6 @@ - - fi; - --# Check whether --enable-auth-on-acceleration or --disable-auth-on-acceleration was given. --if test "${enable_auth_on_acceleration+set}" = set; then -- enableval="$enable_auth_on_acceleration" -- if test "$enableval" = "yes" ; then -- echo "AUTH_ON_ACCELERATION enabled" -- --cat >>confdefs.h <<\_ACEOF --#define AUTH_ON_ACCELERATION 1 --_ACEOF -- -- fi -- --fi; -- - # Check whether --enable-default-err-language or --disable-default-err-language was given. - if test "${enable_default_err_language+set}" = set; then - enableval="$enable_default_err_language" -Index: squid/configure.in -=================================================================== -RCS file: /cvsroot/squid/squid/configure.in,v -retrieving revision 1.384 -retrieving revision 1.385 -diff -u -r1.384 -r1.385 ---- configure.in 1 Jul 2006 18:41:21 -0000 1.384 -+++ configure.in 4 Jul 2006 21:51:15 -0000 1.385 -@@ -736,15 +736,6 @@ - fi - ]) - --AC_ARG_ENABLE(auth-on-acceleration, --[ --enable-auth-on-acceleration -- Enable authentication in accelerators], --[ if test "$enableval" = "yes" ; then -- echo "AUTH_ON_ACCELERATION enabled" -- AC_DEFINE(AUTH_ON_ACCELERATION, 1, [Enable authentication support in accelerators]) -- fi --]) -- - dnl Select Default Error language - AC_ARG_ENABLE(default-err-language, - [ --enable-default-err-language=lang -Index: squid/include/autoconf.h.in -=================================================================== -RCS file: /cvsroot/squid/squid/include/autoconf.h.in,v -retrieving revision 1.153 -retrieving revision 1.154 -diff -u -r1.153 -r1.154 ---- include/autoconf.h.in 21 Jun 2006 20:33:46 -0000 1.153 -+++ include/autoconf.h.in 4 Jul 2006 21:51:16 -0000 1.154 -@@ -3,9 +3,6 @@ - /* Defines how many threads aufs uses for I/O */ - #undef AUFS_IO_THREADS - --/* Enable authentication support in accelerators */ --#undef AUTH_ON_ACCELERATION -- - /* If you are upset that the cachemgr.cgi form comes up with the hostname - field blank, then define this to getfullhostname() */ - #undef CACHEMGR_HOSTNAME -Index: squid/src/acl.c -=================================================================== -RCS file: /cvsroot/squid/squid/src/acl.c,v -retrieving revision 1.303 -retrieving revision 1.304 -diff -u -r1.303 -r1.304 ---- src/acl.c 17 Jun 2006 23:31:03 -0000 1.303 -+++ src/acl.c 4 Jul 2006 21:51:16 -0000 1.304 -@@ -400,6 +400,10 @@ - type->accelerated = 1; - continue; - } -+ if (strcmp(t, "transparent") == 0) { -+ type->transparent = 1; -+ continue; -+ } - if (strcmp(t, "internal") == 0) { - type->internal = 1; - continue; -@@ -1679,6 +1683,8 @@ - { - if (type->accelerated && request->flags.accelerated) - return 1; -+ if (type->transparent && request->flags.transparent) -+ return 1; - if (type->internal && request->flags.internal) - return 1; - return 0; -@@ -1691,20 +1697,15 @@ - http_hdr_type headertype; - if (NULL == r) { - return -1; -- } else if (!r->flags.accelerated) { -- /* Proxy authorization on proxy requests */ -- headertype = HDR_PROXY_AUTHORIZATION; -- } else if (r->flags.internal) { -- /* WWW authorization on accelerated internal requests */ -- headertype = HDR_AUTHORIZATION; -- } else { --#if AUTH_ON_ACCELERATION -+ } else if (r->flags.accelerated) { - /* WWW authorization on accelerated requests */ - headertype = HDR_AUTHORIZATION; --#else -- debug(28, 1) ("aclAuthenticated: authentication not applicable on accelerated requests.\n"); -+ } else if (r->flags.transparent) { -+ debug(28, 1) ("aclAuthenticated: authentication not applicable on transparently intercepted requests.\n"); - return -1; --#endif -+ } else { -+ /* Proxy authorization on proxy requests */ -+ headertype = HDR_PROXY_AUTHORIZATION; - } - /* get authed here */ - /* Note: this fills in checklist->auth_user_request when applicable (auth incomplete) */ -@@ -2911,6 +2912,8 @@ - wordlistAdd(&W, "accelerated"); - if (type->internal) - wordlistAdd(&W, "internal"); -+ if (type->transparent) -+ wordlistAdd(&W, "transparent"); - return W; - } - -Index: squid/src/client_side.c -=================================================================== -RCS file: /cvsroot/squid/squid/src/client_side.c,v -retrieving revision 1.657 -retrieving revision 1.658 -diff -u -r1.657 -r1.658 ---- src/client_side.c 4 Jul 2006 00:06:11 -0000 1.657 -+++ src/client_side.c 4 Jul 2006 21:51:17 -0000 1.658 -@@ -3482,7 +3482,7 @@ - /* - * Deny loops when running in accelerator/transproxy mode. - */ -- if (http->flags.accel && r->flags.loopdetect) { -+ if (r->flags.loopdetect && (http->flags.accel || http->flags.transparent)) { - http->al.http.code = HTTP_FORBIDDEN; - err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN); - err->request = requestLink(http->orig_request); -@@ -3684,15 +3684,19 @@ - if (method == METHOD_CONNECT) { - if (http_ver.major < 1) - goto invalid_request; -+ if (conn->port->accel) -+ goto invalid_request; - } else if (*url == '/') - accel:{ - int vhost = conn->port->vhost || conn->port->transparent; -- int vport = conn->port->vport || conn->transparent; -+ int vport = conn->port->vport; -+ int accel = conn->port->accel; -+ if (!vport && conn->transparent) -+ vport = ntohs(conn->me.sin_port); - if (Config.onoff.global_internal_static && conn->port->accel && internalCheck(url)) { - /* prepend our name & port */ - http->uri = xstrdup(internalStoreUri("", url)); - http->flags.internal = 1; -- http->flags.accel = 1; - debug(33, 5) ("INTERNAL REWRITE: '%s'\n", http->uri); - } else if (vhost && (t = mime_get_header(req_hdr, "Host"))) { - url_sz = strlen(url) + 32 + Config.appendDomainLen + -@@ -3724,14 +3728,14 @@ - /* prepend our name & port */ - http->uri = xstrdup(internalStoreUri("", url)); - http->flags.internal = 1; -- http->flags.accel = 1; - debug(33, 5) ("INTERNAL REWRITE: '%s'\n", http->uri); - } else { - goto invalid_request; - } -- http->flags.accel = 1; -- } else if (conn->transparent) { -- http->flags.accel = 1; -+ if (accel) -+ http->flags.accel = 1; -+ else if (conn->port->transparent) -+ http->flags.transparent = 1; - } else if (conn->port->accel) { - http->flags.accel = 1; - if (!conn->port->vhost) { -@@ -3743,9 +3747,6 @@ - url = (char *) "/"; - goto accel; - } -- } else { -- /* Proxy request */ -- http->flags.accel = 0; - } - if (!http->uri) { - /* No special rewrites have been applied above, use the -@@ -3753,7 +3754,6 @@ - url_sz = strlen(url) + Config.appendDomainLen + 5; - http->uri = xcalloc(url_sz, 1); - strcpy(http->uri, url); -- http->flags.accel = 0; - } - if (!stringHasCntl(http->uri)) - http->log_uri = xstrndup(http->uri, MAX_URL); -@@ -3989,6 +3989,7 @@ - request->flags.tproxy = conn->port->tproxy; - #endif - request->flags.accelerated = http->flags.accel; -+ request->flags.transparent = http->flags.transparent; - /* - * cache the Content-length value in request_t. - */ -Index: squid/src/structs.h -=================================================================== -RCS file: /cvsroot/squid/squid/src/structs.h,v -retrieving revision 1.489 -retrieving revision 1.490 -diff -u -r1.489 -r1.490 ---- src/structs.h 30 Jun 2006 21:23:05 -0000 1.489 -+++ src/structs.h 4 Jul 2006 21:51:18 -0000 1.490 -@@ -346,6 +346,7 @@ - - struct _acl_request_type { - unsigned int accelerated:1; -+ unsigned int transparent:1; - unsigned int internal:1; - }; - -@@ -1190,6 +1191,7 @@ - AccessLogEntry al; - struct { - unsigned int accel:1; -+ unsigned int transparent:1; - unsigned int internal:1; - unsigned int done_copying:1; - unsigned int purging:1; -@@ -1785,6 +1787,7 @@ - unsigned int nocache_hack:1; /* for changing/ignoring no-cache requests */ - #endif - unsigned int accelerated:1; -+ unsigned int transparent:1; - unsigned int internal:1; - unsigned int body_sent:1; - unsigned int reset_tcp:1; Index: files/icap-2.6-core.patch =================================================================== --- files/icap-2.6-core.patch (.../www/squid26) (revision 0) +++ files/icap-2.6-core.patch (.../local/squid26) (revision 984) @@ -0,0 +1,7159 @@ +Patch 1 of 2 to integrate the icap-2_6 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck . + +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.6-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 -rZ-icap-s2_6_merge_HEAD -ricap-2_6 + +See http://devel.squid-cache.org/icap/ for further information +about the ICAP client project. + +Patch last updated: 2006-08-05 + +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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,33 @@ ++ ++ ++FEHLER: Der angeforderte URL konnte nicht geholt werden ++ ++ ++

FEHLER

++

Der angeforderte URL konnte nicht geholt werden

++
++

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

    ++
  • ++ ++ICAP-Protokollfehler ++ ++
++ ++

++

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

    ++
  • Nicht erreichbarer ICAP-Server ++
  • Ungültige Antwort vom ICAP-Server ++ ++
++

++ ++

Ihr Cache Administrator ist %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.12.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:57:59 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: 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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,31 @@ ++ ++ ++ERROR: The requested URL could not be retrieved ++ ++ ++

ERROR

++

The requested URL could not be retrieved

++
++

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

++the following error was encountered: ++

    ++
  • ++ ++ICAP protocol error. ++ ++
++ ++

++

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

    ++
  • ICAP server is not reachable. ++
  • Illegal response from ICAP server. ++
++

++ ++

Your cache administrator is %w. ++ +Index: include/util.h +=================================================================== +RCS file: /cvsroot/squid/squid/include/util.h,v +retrieving revision 1.13 +retrieving revision 1.13.8.1 +diff -p -u -b -r1.13 -r1.13.8.1 +--- include/util.h 12 May 2006 22:51:56 -0000 1.13 ++++ include/util.h 17 May 2006 17:58:00 -0000 1.13.8.1 +@@ -123,4 +123,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 17 May 2006 17:58:00 -0000 1.1.14.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 ++ ++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 17 May 2006 17:58:00 -0000 1.1.14.1 +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (C) 2003 Nikos Mavroyanopoulos ++ * ++ * This file is part of GNUTLS. ++ * ++ * The GNUTLS library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++ /* ++ * DW 2003/10/17: ++ * Changed 'ssize_t' types to 'size_t' ++ */ ++ ++#include "config.h" ++#ifndef HAVE_STRNSTR ++#include ++#include ++ ++char *strnstr(const char *haystack, const char *needle, size_t haystacklen) ++{ ++ char *p; ++ size_t plen; ++ size_t len = strlen(needle); ++ ++ if (*needle == '\0') /* everything matches empty string */ ++ return (char*) haystack; ++ ++ plen = haystacklen; ++ for (p = (char*) haystack; p != NULL; p = memchr(p + 1, *needle, plen-1)) { ++ plen = haystacklen - (p - haystack); ++ ++ if (plen < len) return NULL; ++ ++ if (strncmp(p, needle, len) == 0) ++ return (p); ++ } ++ return NULL; ++} ++#endif +Index: src/MemBuf.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/MemBuf.c,v +retrieving revision 1.10 +retrieving revision 1.9.10.2 +diff -p -u -b -r1.10 -r1.9.10.2 +--- src/MemBuf.c 20 May 2006 22:50:55 -0000 1.10 ++++ src/MemBuf.c 26 May 2006 18:21:31 -0000 1.9.10.2 +@@ -341,3 +341,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.80 +retrieving revision 1.61.4.8 +diff -p -u -b -r1.80 -r1.61.4.8 +--- src/cache_cf.c 30 Jul 2006 23:52:54 -0000 1.80 ++++ src/cache_cf.c 3 Aug 2006 17:16:01 -0000 1.61.4.8 +@@ -2367,6 +2367,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, const wordlist * list); ++static void icap_class_add(icap_class * c); ++ ++/*************************************************** ++ * icap_service ++ */ ++ ++/* ++ * example: ++ * icap_service reqmode_precache 0 icap://192.168.0.1:1344/respmod ++ */ ++ ++static void ++parse_icap_service_type(IcapConfig * cfg) ++{ ++ char *token; ++ icap_service *A = NULL; ++ icap_service *B = NULL; ++ icap_service **T = NULL; ++ ++ A = cbdataAlloc(icap_service); ++ icap_service_init(A); ++ parse_string(&A->name); ++ parse_string(&A->type_name); ++ parse_ushort(&A->bypass); ++ parse_string(&A->uri); ++ while ((token = strtok(NULL, w_space))) { ++ if (strcasecmp(token, "no-keep-alive") == 0) { ++ A->keep_alive = 0; ++ } else { ++ debug(3, 0) ("parse_peer: token='%s'\n", token); ++ self_destruct(); ++ } ++ } ++ debug(3, 5) ("parse_icap_service_type (line %d): %s %s %d %s\n", config_lineno, A->name, A->type_name, A->bypass, A->name); ++ if (icap_service_process(A)) { ++ /* put into linked list */ ++ for (B = cfg->service_head, T = &cfg->service_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_service_type (line %d): skipping %s\n", config_lineno, A->name); ++ icap_service_destroy(A); ++ cbdataFree(A); ++ } ++ ++} ++ ++static void ++dump_icap_service_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_service *current_node = NULL; ++ ++ if (!cfg.service_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.service_head; ++ ++ while (current_node) { ++ storeAppendPrintf(e, "%s %s %s %d %s", name, current_node->name, current_node->type_name, current_node->bypass, current_node->uri); ++ if (current_node->keep_alive == 0) { ++ storeAppendPrintf(e, " no-keep-alive"); ++ } ++ storeAppendPrintf(e, "\n"); ++ current_node = current_node->next; ++ } ++ ++} ++ ++static void ++free_icap_service_type(IcapConfig * cfg) ++{ ++ while (cfg->service_head) { ++ icap_service *current_node = cfg->service_head; ++ cfg->service_head = current_node->next; ++ icap_service_destroy(current_node); ++ cbdataFree(current_node); ++ } ++} ++ ++/* ++ * parse the raw string and cache some parts that are needed later ++ * returns 1 if everything was ok ++ */ ++static int ++icap_service_process(icap_service * s) ++{ ++ char *start, *end, *tempEnd; ++ char *tailp; ++ unsigned int len; ++ int port_in_uri, resource_in_uri = 0; ++ s->type = icapServiceToType(s->type_name); ++ if (s->type >= ICAP_SERVICE_MAX) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service type %s\n", config_lineno, s->type_name); ++ return 0; ++ } ++ if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_PRECACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_REQMOD_POSTCACHE) ++ s->method = ICAP_METHOD_REQMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_PRECACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ else if (s->type == ICAP_SERVICE_RESPMOD_POSTCACHE) ++ s->method = ICAP_METHOD_RESPMOD; ++ debug(3, 5) ("icap_service_process (line %d): type=%s\n", config_lineno, icapServiceToStr(s->type)); ++ if (strncmp(s->uri, "icap://", 7) != 0) { ++ debug(3, 0) ("icap_service_process (line %d): wrong uri: %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ start = s->uri + 7; ++ if ((end = strchr(start, ':')) != NULL) { ++ /* ok */ ++ port_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): port given\n", config_lineno); ++ } else { ++ /* ok */ ++ port_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no port given\n", config_lineno); ++ } ++ ++ if ((tempEnd = strchr(start, '/')) != NULL) { ++ /* ok */ ++ resource_in_uri = 1; ++ debug(3, 5) ("icap_service_process (line %d): resource given\n", config_lineno); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ } else { ++ /* ok */ ++ resource_in_uri = 0; ++ debug(3, 5) ("icap_service_process (line %d): no resource given\n", config_lineno); ++ } ++ ++ tempEnd = strchr(start, '\0'); ++ if (end == '\0') { ++ end = tempEnd; ++ } ++ len = end - start; ++ s->hostname = xstrndup(start, len + 1); ++ s->hostname[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): hostname=%s\n", config_lineno, s->hostname); ++ start = end; ++ ++ if (port_in_uri) { ++ start++; /* skip ':' */ ++ if (resource_in_uri) ++ end = strchr(start, '/'); ++ else ++ end = strchr(start, '\0'); ++ s->port = strtoul(start, &tailp, 0) % 65536; ++ if (tailp != end) { ++ debug(3, 0) ("icap_service_process (line %d): wrong service uri (port could not be parsed): %s\n", config_lineno, s->uri); ++ return 0; ++ } ++ debug(3, 5) ("icap_service_process (line %d): port=%d\n", config_lineno, s->port); ++ start = end; ++ } else { ++ /* no explicit ICAP port; first ask by getservbyname or default to ++ * hardwired port 1344 per ICAP specification section 4.2 */ ++ struct servent *serv = getservbyname("icap", "tcp"); ++ if (serv) { ++ s->port = htons(serv->s_port); ++ debug(3, 5) ("icap_service_process (line %d): default port=%d getservbyname(icap,tcp)\n", config_lineno, s->port); ++ } else { ++ s->port = 1344; ++ debug(3, 5) ("icap_service_process (line %d): default hardwired port=%d\n", config_lineno, s->port); ++ } ++ } ++ ++ if (resource_in_uri) { ++ start++; /* skip '/' */ ++ /* the rest is resource name */ ++ end = strchr(start, '\0'); ++ len = end - start; ++ if (len > 1024) { ++ debug(3, 0) ("icap_service_process (line %d): long resource name (>1024), probably wrong\n", config_lineno); ++ } ++ s->resource = xstrndup(start, len + 1); ++ s->resource[len] = 0; ++ debug(3, 5) ("icap_service_process (line %d): service=%s\n", config_lineno, s->resource); ++ } ++ /* check bypass */ ++ if ((s->bypass != 0) && (s->bypass != 1)) { ++ debug(3, 0) ("icap_service_process (line %d): invalid bypass value\n", config_lineno); ++ return 0; ++ } ++ return 1; ++} ++ ++/* ++ * constructor ++ */ ++static void ++icap_service_init(icap_service * s) ++{ ++ s->type = ICAP_SERVICE_MAX; /* means undefined */ ++ s->preview = Config.icapcfg.preview_size; ++ s->opt = 0; ++ s->keep_alive = 1; ++ s->istag = StringNull; ++ s->transfer_preview = StringNull; ++ s->transfer_ignore = StringNull; ++ s->transfer_complete = StringNull; ++} ++ ++/* ++ * destructor ++ * frees only strings, but don't touch the linked list ++ */ ++static void ++icap_service_destroy(icap_service * s) ++{ ++ xfree(s->name); ++ xfree(s->uri); ++ xfree(s->type_name); ++ xfree(s->hostname); ++ xfree(s->resource); ++ assert(s->opt == 0); /* there should be no opt request running now */ ++ stringClean(&s->istag); ++ stringClean(&s->transfer_preview); ++ stringClean(&s->transfer_ignore); ++ stringClean(&s->transfer_complete); ++} ++ ++icap_service * ++icap_service_lookup(char *name) ++{ ++ icap_service *iter; ++ for (iter = Config.icapcfg.service_head; iter; iter = iter->next) { ++ if (!strcmp(name, iter->name)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/*************************************************** ++ * icap_service_list ++ */ ++ ++static void ++icap_service_list_add(icap_service_list ** isl, char *service_name) ++{ ++ icap_service_list **iter; ++ icap_service_list *new; ++ icap_service *gbl_service; ++ int i; ++ int max_services; ++ ++ new = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* Found all services with that name, and add to the array */ ++ max_services = sizeof(new->services) / sizeof(icap_service *); ++ gbl_service = Config.icapcfg.service_head; ++ i = 0; ++ while (gbl_service && i < max_services) { ++ if (!strcmp(service_name, gbl_service->name)) ++ new->services[i++] = gbl_service; ++ gbl_service = gbl_service->next; ++ } ++ new->nservices = i; ++ ++ if (*isl) { ++ iter = isl; ++ while ((*iter)->next) ++ iter = &((*iter)->next); ++ (*iter)->next = new; ++ } else { ++ *isl = new; ++ } ++} ++ ++/* ++ * free the linked list without touching references icap_service ++ */ ++static void ++icap_service_list_destroy(icap_service_list * isl) ++{ ++ icap_service_list *current; ++ icap_service_list *next; ++ ++ current = isl; ++ while (current) { ++ next = current->next; ++ memFree(current, MEM_ICAP_SERVICE_LIST); ++ current = next; ++ } ++} ++ ++/*************************************************** ++ * icap_class ++ */ ++static void ++parse_icap_class_type(IcapConfig * cfg) ++{ ++ icap_class *s = NULL; ++ ++ s = memAllocate(MEM_ICAP_CLASS); ++ parse_string(&s->name); ++ parse_wordlist(&s->services); ++ ++ if (icap_class_process(s)) { ++ /* if ok, put into linked list */ ++ icap_class_add(s); ++ } else { ++ /* clean up structure */ ++ debug(3, 0) ("parse_icap_class_type (line %d): skipping %s\n", config_lineno, s->name); ++ icap_class_destroy(s); ++ memFree(s, MEM_ICAP_CLASS); ++ } ++} ++ ++static void ++dump_icap_class_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_class *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.class_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.class_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->name); ++ dump_wordlist(e, nom, current_node->services); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_class_type(IcapConfig * cfg) ++{ ++ while (cfg->class_head) { ++ icap_class *current_node = cfg->class_head; ++ cfg->class_head = current_node->next; ++ icap_class_destroy(current_node); ++ memFree(current_node, MEM_ICAP_CLASS); ++ } ++} ++ ++/* ++ * process services list, return 1, if at least one service was found ++ */ ++static int ++icap_class_process(icap_class * c) ++{ ++ icap_service_list *isl = NULL; ++ wordlist *iter; ++ icap_service *service; ++ /* take services list and build icap_service_list from it */ ++ for (iter = c->services; iter; iter = iter->next) { ++ service = icap_service_lookup(iter->key); ++ if (service) { ++ icap_service_list_add(&isl, iter->key); ++ } else { ++ debug(3, 0) ("icap_class_process (line %d): skipping service %s in class %s\n", config_lineno, iter->key, c->name); ++ } ++ } ++ ++ if (isl) { ++ c->isl = isl; ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * search for an icap_class in the global IcapConfig ++ * classes with hidden-flag are skipped ++ */ ++static icap_class * ++icap_class_lookup(char *name) ++{ ++ icap_class *iter; ++ for (iter = Config.icapcfg.class_head; iter; iter = iter->next) { ++ if ((!strcmp(name, iter->name)) && (!iter->hidden)) { ++ return iter; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * adds an icap_class to the global IcapConfig ++ */ ++static void ++icap_class_add(icap_class * c) ++{ ++ icap_class *cp = NULL; ++ icap_class **t = NULL; ++ IcapConfig *cfg = &Config.icapcfg; ++ if (c) { ++ for (cp = cfg->class_head, t = &cfg->class_head; cp; t = &cp->next, cp = cp->next); ++ *t = c; ++ } ++} ++ ++/* ++ * free allocated memory inside icap_class ++ */ ++static void ++icap_class_destroy(icap_class * c) ++{ ++ xfree(c->name); ++ wordlistDestroy(&c->services); ++ icap_service_list_destroy(c->isl); ++} ++ ++/*************************************************** ++ * icap_access ++ */ ++ ++/* format: icap_access {allow|deny} acl, ... */ ++static void ++parse_icap_access_type(IcapConfig * cfg) ++{ ++ icap_access *A = NULL; ++ icap_access *B = NULL; ++ icap_access **T = NULL; ++ icap_service *s = NULL; ++ icap_class *c = NULL; ++ ushort no_class = 0; ++ ++ A = memAllocate(MEM_ICAP_ACCESS); ++ parse_string(&A->service_name); ++ ++ /* ++ * try to find a class with the given name first. if not found, search ++ * the services. if a service is found, create a new hidden class with ++ * only this service. this is for backward compatibility. ++ * ++ * the special classname All is allowed only in deny rules, because ++ * the class is not used there. ++ */ ++ if (!strcmp(A->service_name, "None")) { ++ no_class = 1; ++ } else { ++ A->class = icap_class_lookup(A->service_name); ++ if (!A->class) { ++ s = icap_service_lookup(A->service_name); ++ if (s) { ++ c = memAllocate(MEM_ICAP_CLASS); ++ c->name = xstrdup("(hidden)"); ++ c->hidden = 1; ++ wordlistAdd(&c->services, A->service_name); ++ c->isl = memAllocate(MEM_ICAP_SERVICE_LIST); ++ /* FIXME:luc: check what access do */ ++ c->isl->services[0] = s; ++ c->isl->nservices = 1; ++ icap_class_add(c); ++ A->class = c; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): servicename %s not found. skipping.\n", config_lineno, A->service_name); ++ memFree(A, MEM_ICAP_ACCESS); ++ return; ++ } ++ } ++ } ++ ++ aclParseAccessLine(&(A->access)); ++ debug(3, 5) ("parse_icap_access_type (line %d): %s\n", config_lineno, A->service_name); ++ ++ /* check that All class is only used in deny rule */ ++ if (no_class && A->access->allow) { ++ memFree(A, MEM_ICAP_ACCESS); ++ debug(3, 0) ("parse_icap_access (line %d): special class 'None' only allowed in deny rule. skipping.\n", config_lineno); ++ return; ++ } ++ if (A->access) { ++ for (B = cfg->access_head, T = &cfg->access_head; B; T = &B->next, B = B->next); ++ *T = A; ++ } else { ++ debug(3, 0) ("parse_icap_access_type (line %d): invalid line skipped\n", config_lineno); ++ memFree(A, MEM_ICAP_ACCESS); ++ } ++} ++ ++static void ++dump_icap_access_type(StoreEntry * e, const char *name, IcapConfig cfg) ++{ ++ icap_access *current_node = NULL; ++ LOCAL_ARRAY(char, nom, 64); ++ ++ if (!cfg.access_head) { ++ storeAppendPrintf(e, "%s 0\n", name); ++ return; ++ } ++ current_node = cfg.access_head; ++ ++ while (current_node) { ++ snprintf(nom, 64, "%s %s", name, current_node->service_name); ++ dump_acl_access(e, nom, current_node->access); ++ current_node = current_node->next; ++ } ++} ++ ++static void ++free_icap_access_type(IcapConfig * cfg) ++{ ++ while (cfg->access_head) { ++ icap_access *current_node = cfg->access_head; ++ cfg->access_head = current_node->next; ++ icap_access_destroy(current_node); ++ memFree(current_node, MEM_ICAP_ACCESS); ++ } ++} ++ ++/* ++ * destructor ++ * frees everything but the linked list ++ */ ++static void ++icap_access_destroy(icap_access * a) ++{ ++ xfree(a->service_name); ++ aclDestroyAccessList(&a->access); ++} ++ ++/*************************************************** ++ * for debugging purposes only ++ */ ++void ++dump_icap_config(IcapConfig * cfg) ++{ ++ icap_service *s_iter; ++ icap_class *c_iter; ++ icap_access *a_iter; ++ icap_service_list *isl_iter; ++ acl_list *l; ++ debug(3, 0) ("IcapConfig: onoff = %d\n", cfg->onoff); ++ debug(3, 0) ("IcapConfig: service_head = %d\n", (int) cfg->service_head); ++ debug(3, 0) ("IcapConfig: class_head = %d\n", (int) cfg->class_head); ++ debug(3, 0) ("IcapConfig: access_head = %d\n", (int) cfg->access_head); ++ ++ debug(3, 0) ("IcapConfig: services =\n"); ++ for (s_iter = cfg->service_head; s_iter; s_iter = s_iter->next) { ++ printf(" %s: \n", s_iter->name); ++ printf(" bypass = %d\n", s_iter->bypass); ++ printf(" hostname = %s\n", s_iter->hostname); ++ printf(" port = %d\n", s_iter->port); ++ printf(" resource = %s\n", s_iter->resource); ++ } ++ debug(3, 0) ("IcapConfig: classes =\n"); ++ for (c_iter = cfg->class_head; c_iter; c_iter = c_iter->next) { ++ printf(" %s: \n", c_iter->name); ++ printf(" services = \n"); ++ for (isl_iter = c_iter->isl; isl_iter; isl_iter = isl_iter->next) { ++ int i; ++ for (i = 0; i < isl_iter->nservices; i++) ++ printf(" %s\n", isl_iter->services[i]->name); ++ } ++ } ++ debug(3, 0) ("IcapConfig: access =\n"); ++ for (a_iter = cfg->access_head; a_iter; a_iter = a_iter->next) { ++ printf(" service_name = %s\n", a_iter->service_name); ++ printf(" access = %s", a_iter->access->allow ? "allow" : "deny"); ++ for (l = a_iter->access->acl_list; l != NULL; l = l->next) { ++ printf(" %s%s", ++ l->op ? null_string : "!", ++ l->acl->name); ++ } ++ printf("\n"); ++ } ++} ++#endif /* HS_FEAT_ICAP */ + + static void + parse_kb_size_t(squid_off_t * var) +Index: src/cbdata.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/cbdata.c,v +retrieving revision 1.18 +retrieving revision 1.18.8.1 +diff -p -u -b -r1.18 -r1.18.8.1 +--- src/cbdata.c 12 May 2006 22:51:56 -0000 1.18 ++++ src/cbdata.c 17 May 2006 17:58:00 -0000 1.18.8.1 +@@ -179,6 +179,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.147 +retrieving revision 1.100.4.8 +diff -p -u -b -r1.147 -r1.100.4.8 +--- src/cf.data.pre 3 Aug 2006 02:53:44 -0000 1.147 ++++ src/cf.data.pre 3 Aug 2006 17:16:02 -0000 1.100.4.8 +@@ -3178,7 +3178,6 @@ DOC_START + ensure correct results it is best to set server_persistent_connections + to off when using this directive in such configurations. + DOC_END +- + NAME: reply_header_max_size + COMMENT: (KB) + TYPE: b_size_t +@@ -3447,6 +3446,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.7 +retrieving revision 1.6.8.2 +diff -p -u -b -r1.7 -r1.6.8.2 +--- src/cf_gen_defines 31 May 2006 19:51:14 -0000 1.7 ++++ src/cf_gen_defines 4 Jun 2006 14:15:43 -0000 1.6.8.2 +@@ -22,6 +22,7 @@ BEGIN { + define["USE_WCCP"]="--enable-wccp" + define["USE_WCCPv2"]="--enable-wccpv2" + define["WIP_FWD_LOG"]="--enable-forward-log" ++ define["HS_FEAT_ICAP"]="--enable-icap-support" + } + /^IFDEF:/ { + if (define[$2] != "") +Index: src/client_side.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/client_side.c,v +retrieving revision 1.138 +retrieving revision 1.89.4.10 +diff -p -u -b -r1.138 -r1.89.4.10 +--- src/client_side.c 2 Aug 2006 21:51:40 -0000 1.138 ++++ src/client_side.c 3 Aug 2006 17:16:02 -0000 1.89.4.10 +@@ -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 *); +@@ -141,12 +141,12 @@ 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); +@@ -157,14 +157,17 @@ static void clientAccessCheck(void *data + static void clientAccessCheckDone(int answer, void *data); + static void clientAccessCheck2(void *data); + static void clientAccessCheckDone2(int answer, void *data); +-static BODY_HANDLER clientReadBody; ++BODY_HANDLER clientReadBody; + static void clientAbortBody(request_t * req); + #if USE_SSL + static void httpsAcceptSSL(ConnStateData * connState, SSL_CTX * sslContext); + #endif + static int varyEvaluateMatch(StoreEntry * entry, request_t * request); + static int modifiedSince(StoreEntry *, request_t *); +-static StoreEntry *clientCreateStoreEntry(clientHttpRequest *, method_t, request_flags); ++StoreEntry *clientCreateStoreEntry(clientHttpRequest *, method_t, request_flags); ++#if HS_FEAT_ICAP ++static int clientIcapReqMod(clientHttpRequest * http); ++#endif + + #if USE_IDENT + static void +@@ -382,7 +385,7 @@ clientOnlyIfCached(clientHttpRequest * h + EBIT_TEST(r->cache_control->mask, CC_ONLY_IF_CACHED); + } + +-static StoreEntry * ++StoreEntry * + clientCreateStoreEntry(clientHttpRequest * h, method_t m, request_flags flags) + { + StoreEntry *e; +@@ -642,6 +645,10 @@ clientRedirectDone(void *data, char *res + if (urlgroup && *urlgroup) + http->request->urlgroup = xstrdup(urlgroup); + clientInterpretRequestHeaders(http); ++#if HS_FEAT_ICAP ++ if (Config.icapcfg.onoff) ++ icapCheckAcl(http); ++#endif + #if HEADERS_LOG + headersLog(0, 1, request->method, request); + #endif +@@ -1380,11 +1387,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; +@@ -1401,8 +1419,9 @@ connStateFree(int fd, void *data) + authenticateAuthUserRequestUnlock(connState->auth_user_request); + connState->auth_user_request = NULL; + authenticateOnCloseConnection(connState); ++ if (connState->in.buf) + memFreeBuf(connState->in.size, connState->in.buf); +- pconnHistCount(0, connState->nrequests); ++/* pconnHistCount(0, connState->nrequests);*/ + if (connState->pinning.fd >= 0) + comm_close(connState->pinning.fd); + cbdataFree(connState); +@@ -1600,7 +1619,7 @@ clientSetKeepaliveFlag(clientHttpRequest + } + } + +-static int ++int + clientCheckContentLength(request_t * r) + { + switch (r->method) { +@@ -1619,7 +1638,7 @@ clientCheckContentLength(request_t * r) + /* NOT REACHED */ + } + +-static int ++int + clientCachable(clientHttpRequest * http) + { + request_t *req = http->request; +@@ -1645,7 +1664,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; +@@ -3350,7 +3369,7 @@ clientProcessRequest2(clientHttpRequest + return LOG_TCP_HIT; + } + +-static void ++void + clientProcessRequest(clientHttpRequest * http) + { + char *url = http->uri; +@@ -3360,6 +3379,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; + #if USE_SSL && SSL_CONNECT_INTERCEPT +@@ -3814,6 +3838,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) { +@@ -3917,7 +3955,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 +@@ -4100,7 +4139,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; +@@ -4229,7 +4268,7 @@ clientProcessBody(ConnStateData * conn) + } + + /* Abort a body request */ +-static void ++void + clientAbortBody(request_t * request) + { + ConnStateData *conn = request->body_reader_data; +@@ -4271,7 +4310,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 + */ +@@ -5007,6 +5046,52 @@ 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 ++ + /* This is a handler normally called by comm_close() */ + static void + clientPinnedConnectionClosed(int fd, void *data) +Index: src/comm.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/comm.c,v +retrieving revision 1.44 +retrieving revision 1.29.10.6 +diff -p -u -b -r1.44 -r1.29.10.6 +--- src/comm.c 27 Jun 2006 11:52:53 -0000 1.44 ++++ src/comm.c 28 Jun 2006 21:11:59 -0000 1.29.10.6 +@@ -736,8 +736,8 @@ comm_close(int fd) + F->flags.closing = 1; + CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING); + commCallCloseHandlers(fd); +- if (F->uses) /* assume persistent connect count */ +- pconnHistCount(1, F->uses); ++ if (F->pconn.uses) ++ pconnHistCount(F->pconn.type, F->pconn.uses); + #if USE_SSL + if (F->ssl) { + if (!F->flags.close_request) { +Index: src/enums.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/enums.h,v +retrieving revision 1.56 +retrieving revision 1.45.4.5 +diff -p -u -b -r1.56 -r1.45.4.5 +--- src/enums.h 19 Jul 2006 17:52:44 -0000 1.56 ++++ src/enums.h 22 Jul 2006 14:12:01 -0000 1.45.4.5 +@@ -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; +@@ -455,6 +456,9 @@ typedef enum { + PROTO_WHOIS, + PROTO_INTERNAL, + PROTO_HTTPS, ++#if HS_FEAT_ICAP ++ PROTO_ICAP, ++#endif + PROTO_MAX + } protocol_t; + +@@ -632,6 +636,12 @@ typedef enum { + #if USE_SSL + MEM_ACL_CERT_DATA, + #endif ++#if HS_FEAT_ICAP ++ MEM_ICAP_OPT_DATA, ++ MEM_ICAP_SERVICE_LIST, ++ MEM_ICAP_CLASS, ++ MEM_ICAP_ACCESS, ++#endif + MEM_MAX + } mem_type; + +@@ -732,9 +742,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) + */ +@@ -782,4 +797,68 @@ typedef enum { + ST_OP_CREATE + } store_op_t; + ++#if HS_FEAT_ICAP ++typedef enum { ++ ICAP_STATUS_NONE = 0, ++ ICAP_STATUS_CONTINUE = 100, ++ ICAP_STATUS_SWITCHING_PROTOCOLS = 101, ++ ICAP_STATUS_STATUS_OK = 200, ++ ICAP_CREATED = 201, ++ ICAP_STATUS_ACCEPTED = 202, ++ ICAP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, ++ ICAP_STATUS_NO_MODIFICATION_NEEDED = 204, ++ ICAP_STATUS_RESET_CONTENT = 205, ++ ICAP_STATUS_PARTIAL_CONTENT = 206, ++ ICAP_STATUS_MULTIPLE_CHOICES = 300, ++ ICAP_STATUS_MOVED_PERMANENTLY = 301, ++ ICAP_STATUS_MOVED_TEMPORARILY = 302, ++ ICAP_STATUS_SEE_OTHER = 303, ++ ICAP_STATUS_NOT_MODIFIED = 304, ++ ICAP_STATUS_USE_PROXY = 305, ++ ICAP_STATUS_BAD_REQUEST = 400, ++ ICAP_STATUS_UNAUTHORIZED = 401, ++ ICAP_STATUS_PAYMENT_REQUIRED = 402, ++ ICAP_STATUS_FORBIDDEN = 403, ++ ICAP_STATUS_SERVICE_NOT_FOUND = 404, ++ ICAP_STATUS_METHOD_NOT_ALLOWED = 405, ++ ICAP_STATUS_NOT_ACCEPTABLE = 406, ++ ICAP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, ++ ICAP_STATUS_REQUEST_TIMEOUT = 408, ++ ICAP_STATUS_CONFLICT = 409, ++ ICAP_STATUS_GONE = 410, ++ ICAP_STATUS_LENGTH_REQUIRED = 411, ++ ICAP_STATUS_PRECONDITION_FAILED = 412, ++ ICAP_STATUS_REQUEST_ENTITY_TOO_LARGE = 413, ++ ICAP_STATUS_REQUEST_URI_TOO_LARGE = 414, ++ ICAP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, ++ ICAP_STATUS_INTERNAL_SERVER_ERROR = 500, ++ ICAP_STATUS_NOT_IMPLEMENTED = 501, ++ ICAP_STATUS_BAD_GATEWAY = 502, ++ ICAP_STATUS_SERVICE_OVERLOADED = 503, ++ ICAP_STATUS_GATEWAY_TIMEOUT = 504, ++ ICAP_STATUS_ICAP_VERSION_NOT_SUPPORTED = 505, ++ ICAP_STATUS_INVALID_HEADER = 600 ++} icap_status; ++ ++/* ++ * these values are used as index in an array, so it seems to be better to ++ * assign some numbers ++ */ ++typedef enum { ++ ICAP_SERVICE_REQMOD_PRECACHE = 0, ++ ICAP_SERVICE_REQMOD_POSTCACHE = 1, ++ ICAP_SERVICE_RESPMOD_PRECACHE = 2, ++ ICAP_SERVICE_RESPMOD_POSTCACHE = 3, ++ ICAP_SERVICE_MAX = 4 ++} icap_service_t; ++ ++typedef enum { ++ ICAP_METHOD_NONE, ++ ICAP_METHOD_OPTION, ++ ICAP_METHOD_REQMOD, ++ ICAP_METHOD_RESPMOD ++} icap_method_t; ++ ++#endif /* HS_FEAT_ICAP */ ++ + #endif /* SQUID_ENUMS_H */ +Index: src/forward.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/forward.c,v +retrieving revision 1.40 +retrieving revision 1.20.4.7 +diff -p -u -b -r1.40 -r1.20.4.7 +--- src/forward.c 30 Jun 2006 21:52:14 -0000 1.40 ++++ src/forward.c 22 Jul 2006 14:12:01 -0000 1.20.4.7 +@@ -362,8 +362,9 @@ fwdConnectDone(int server_fd, int status + } else { + debug(17, 3) ("fwdConnectDone: FD %d: '%s'\n", server_fd, storeUrl(fwdState->entry)); + fd_note(server_fd, storeUrl(fwdState->entry)); +- fd_table[server_fd].uses++; +- if (fd_table[server_fd].uses == 1 && fs->peer) ++ fd_table[server_fd].pconn.uses++; ++ fd_table[server_fd].pconn.type = 1; ++ if (fd_table[server_fd].pconn.uses ==1 && fs->peer) + peerConnectSucceded(fs->peer); + #if USE_SSL + if ((fs->peer && fs->peer->use_ssl) || +@@ -941,6 +942,8 @@ 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), +@@ -979,6 +982,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); +@@ -998,7 +1003,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.26 +retrieving revision 1.22.4.4 +diff -p -u -b -r1.26 -r1.22.4.4 +--- src/globals.h 30 Jul 2006 23:52:54 -0000 1.26 ++++ src/globals.h 3 Aug 2006 17:16:04 -0000 1.22.4.4 +@@ -171,6 +171,9 @@ extern const char *external_acl_message; + #if HAVE_SBRK + extern void *sbrk_start; /* 0 */ + #endif ++#if HS_FEAT_ICAP ++extern char *icap_service_type_str[]; ++#endif + extern int opt_send_signal; /* -1 */ + extern int opt_no_daemon; /* 0 */ + #if LINUX_TPROXY +Index: src/http.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/http.c,v +retrieving revision 1.47 +retrieving revision 1.28.4.9 +diff -p -u -b -r1.47 -r1.28.4.9 +--- src/http.c 26 Jul 2006 20:51:25 -0000 1.47 ++++ src/http.c 3 Aug 2006 17:16:04 -0000 1.28.4.9 +@@ -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 *); +@@ -56,11 +56,12 @@ static int httpCachableReply(HttpStateDa + static void httpMaybeRemovePublic(StoreEntry *, http_status); + static int peer_supports_connection_pinning(HttpStateData * httpState); + +-static void ++void + httpStateFree(int fd, void *data) + { + HttpStateData *httpState = data; + #if DELAY_POOLS ++ if (fd >= 0) + delayClearNoDelay(fd); + #endif + if (httpState == NULL) +@@ -80,6 +81,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); + } + +@@ -409,7 +413,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; +@@ -551,24 +555,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; +@@ -635,6 +650,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; +@@ -646,7 +672,35 @@ 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); +@@ -663,7 +717,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--); +@@ -693,6 +753,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 +@@ -745,7 +811,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, +@@ -792,10 +879,21 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) { ++ debug(81, 5) ("calling icapSendRespMod from %s:%d\n", __FILE__, __LINE__); ++ icapSendRespMod(httpState->icap_writer, buf, len, 0); ++ httpState->icap_writer->fake_content_length += len; ++ } else ++#endif + storeAppend(entry, buf, len); + keep_alive = 0; + } + } ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + if (keep_alive) { + int pinned = 0; + #if LINUX_TPROXY +@@ -851,6 +949,10 @@ httpReadReply(int fd, void *data) + ("httpReadReply: Excess data from \"%s %s\"\n", + RequestMethodStr[orig_request->method], + storeUrl(entry)); ++#ifdef HS_FEAT_ICAP ++ if (httpState->icap_writer) ++ icapSendRespMod(httpState->icap_writer, NULL, 0, 1); ++#endif + fwdComplete(httpState->fwd); + comm_close(fd); + return; +@@ -861,6 +963,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 +@@ -888,6 +1018,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 ++ */ ++ storeClientUnregisterAbort(httpState->entry); ++ storeUnlockObject(httpState->entry); ++ /* ++ * create a bogus entry because the code assumes one is ++ * always there. ++ */ ++ fake_flags.cachable = 0; ++ fake_flags.hierarchical = 0; /* force private key */ ++ httpState->entry = storeCreateEntry("fake", "fake", fake_flags, fake_method); ++ storeAppend(httpState->entry, fake_msg, strlen(fake_msg)); ++ /* ++ * pull a switcheroo on fwdState->entry. ++ */ ++ storeUnlockObject(httpState->fwd->entry); ++ httpState->fwd->entry = httpState->entry; ++ storeLockObject(httpState->fwd->entry); ++ /* ++ * Note that we leave fwdState connected to httpState, ++ * but we changed the entry. So when fwdComplete ++ * or whatever is called it does no harm -- its ++ * just the fake entry. ++ */ ++ } else { ++ /* ++ * failed to open connection to ICAP server. ++ * But bypass request, so just continue here. ++ */ ++ } ++ } ++#endif + /* + * Set the read timeout here because it hasn't been set yet. + * We only set the read timeout after the request has been +@@ -896,8 +1083,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; + } +@@ -1191,8 +1388,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)) +@@ -1318,6 +1518,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], +@@ -1360,12 +1561,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 26 May 2006 19:24:02 -0000 1.1.14.3 +@@ -0,0 +1,815 @@ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++/* _GNU_SOURCE is required for strcasestr */ ++#define _GNU_SOURCE 1 ++ ++#include "squid.h" ++#include "util.h" ++ ++extern PF httpStateFree; ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++#define ICAP_OPTIONS_REQUEST ++ ++ ++void ++icapInit() ++{ ++#ifdef ICAP_OPTIONS_REQUEST ++ if (Config.icapcfg.onoff) { ++ icapOptInit(); ++ } ++#endif ++} ++ ++void ++icapClose() ++{ ++ icapOptShutdown(); ++} ++ ++/* ++ * search for a HTTP-like header in the buffer. ++ * Note, buf must be 0-terminated ++ * ++ * This function is not very good. It should probably look for ++ * header tokens only at the start of a line, not just anywhere in ++ * the buffer. ++ */ ++int ++icapFindHeader(const char *buf, const char *hdr, const char **Start, ++ const char **End) ++{ ++ const char *start = NULL; ++ const char *end = NULL; ++ start = strcasestr(buf, hdr); ++ if (NULL == start) ++ return 0; ++ end = start + strcspn(start, "\r\n"); ++ if (start == end) ++ return 0; ++ *Start = start; ++ *End = end; ++ return 1; ++} ++ ++/* ++ * parse the contents of the encapsulated header (buffer between enc_start ++ * and enc_end) and put the result into IcapStateData ++ */ ++void ++icapParseEncapsulated(IcapStateData * icap, const char *enc_start, ++ const char *enc_end) ++{ ++ char *current, *end; ++ ++ assert(icap); ++ assert(enc_start); ++ assert(enc_end); ++ ++ current = strchr(enc_start, ':'); ++ current++; ++ while (current < enc_end) { ++ while (isspace(*current)) ++ current++; ++ if (!strncmp(current, "res-hdr=", 8)) { ++ current += 8; ++ icap->enc.res_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-hdr=", 8)) { ++ current += 8; ++ icap->enc.req_hdr = strtol(current, &end, 10); ++ } else if (!strncmp(current, "null-body=", 10)) { ++ current += 10; ++ icap->enc.null_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "res-body=", 9)) { ++ current += 9; ++ icap->enc.res_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "req-body=", 9)) { ++ current += 9; ++ icap->enc.req_body = strtol(current, &end, 10); ++ } else if (!strncmp(current, "opt-body=", 9)) { ++ current += 9; ++ icap->enc.opt_body = strtol(current, &end, 10); ++ } else { ++ /* invalid header */ ++ debug(81, 5) ("icapParseEncapsulated: error in: %s\n", current); ++ return; ++ } ++ current = end; ++ current = strchr(current, ','); ++ if (current == NULL) ++ break; ++ else ++ current++; /* skip ',' */ ++ } ++ debug(81, ++ 3) ("icapParseEncapsulated: res-hdr=%d, req-hdr=%d, null-body=%d, " ++ "res-body=%d, req-body=%d, opt-body=%d\n", icap->enc.res_hdr, ++ icap->enc.req_hdr, icap->enc.null_body, icap->enc.res_body, ++ icap->enc.req_body, icap->enc.opt_body); ++ ++} ++ ++icap_service * ++icapService(icap_service_t type, request_t * r) ++{ ++ icap_service_list *isl_iter; ++ int is_iter; ++ int nb_unreachable = 0; ++ icap_service *unreachable_one = NULL; ++ ++ debug(81, 8) ("icapService: type=%s\n", icapServiceToStr(type)); ++ if (NULL == r) { ++ debug(81, 8) ("icapService: no request_t\n"); ++ return NULL; ++ } ++ if (NULL == r->class) { ++ debug(81, 8) ("icapService: no class\n"); ++ return NULL; ++ } ++ for (isl_iter = r->class->isl; isl_iter; isl_iter = isl_iter->next) { ++ /* TODO:luc: Do a round-robin, choose a random value ? ++ * For now, we use a simple round robin with checking is the ++ * icap server is available */ ++ is_iter = isl_iter->last_service_used; ++ do { ++ is_iter = (is_iter + 1) % isl_iter->nservices; ++ debug(81, 8) ("icapService: checking service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ if (type == isl_iter->services[is_iter]->type) { ++ if (!isl_iter->services[is_iter]->unreachable) { ++ debug(81, 8) ("icapService: found service %s/id=%d\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ isl_iter->last_service_used = is_iter; ++ return isl_iter->services[is_iter]; ++ } ++ debug(81, ++ 8) ++ ("icapService: found service %s/id=%d, but it's unreachable. I don't want to use it\n", ++ isl_iter->services[is_iter]->name, is_iter); ++ unreachable_one = isl_iter->services[is_iter]; ++ nb_unreachable++; ++ /* FIXME:luc: in response mod, if we return an NULL pointer, user can bypass ++ * the filter, is it normal ? */ ++ } ++ } while (is_iter != isl_iter->last_service_used); ++ } ++ debug(81, 8) ("icapService: no service found\n"); ++ isl_iter = r->class->isl; ++ ++ if (nb_unreachable > 0) { ++ debug(81, ++ 8) ++ ("All the services are unreachable, returning an unreachable one\n"); ++ return unreachable_one; ++ } else { ++ return NULL; ++ } ++} ++ ++int ++icapConnect(IcapStateData * icap, CNCB * theCallback) ++{ ++ int rc; ++ icap->icap_fd = pconnPop(icap->current_service->hostname, ++ icap->current_service->port, NULL, NULL, 0); ++ if (icap->icap_fd >= 0) { ++ debug(81, 3) ("icapConnect: reused pconn FD %d\n", icap->icap_fd); ++ fd_note(icap->icap_fd, icap->current_service->uri); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ theCallback(icap->icap_fd, 0, icap); ++ return 1; ++ } ++ icap->icap_fd = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), 0, ++ COMM_NONBLOCKING, icap->current_service->uri); ++ debug(81, 5) ("icapConnect: new socket, FD %d, local address %s\n", ++ icap->icap_fd, inet_ntoa(getOutgoingAddr(NULL))); ++ if (icap->icap_fd < 0) { ++ icapStateFree(-1, icap); /* XXX test */ ++ return 0; ++ } ++ icap->flags.connect_pending = 1; ++ /* ++ * Configure timeout and close handler before calling ++ * connect because commConnectStart() might get an error ++ * immediately and close the descriptor before it returns. ++ */ ++ commSetTimeout(icap->icap_fd, Config.Timeout.connect, ++ icapConnectTimeout, icap); ++ comm_add_close_handler(icap->icap_fd, icapStateFree, icap); ++ /* ++ * This sucks. commConnectStart() may fail before returning, ++ * so lets lock the data and check its validity afterwards. ++ */ ++ cbdataLock(icap); ++ commConnectStart(icap->icap_fd, ++ icap->current_service->hostname, ++ icap->current_service->port, theCallback, icap); ++ rc = cbdataValid(icap); ++ cbdataUnlock(icap); ++ debug(81, 3) ("icapConnect: returning %d\n", rc); ++ return rc; ++} ++ ++IcapStateData * ++icapAllocate(void) ++{ ++ IcapStateData *icap; ++ ++ if (!Config.icapcfg.onoff) ++ return 0; ++ ++ icap = cbdataAlloc(IcapStateData); ++ icap->icap_fd = -1; ++ icap->enc.res_hdr = -1; ++ icap->enc.res_body = -1; ++ icap->enc.req_hdr = -1; ++ icap->enc.req_body = -1; ++ icap->enc.opt_body = -1; ++ icap->enc.null_body = -1; ++ icap->chunk_size = -1; ++ memBufDefInit(&icap->icap_hdr); ++ ++ debug(81, 3) ("New ICAP state\n"); ++ return icap; ++} ++ ++void ++icapStateFree(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapStateFree: FD %d, icap %p\n", fd, icap); ++ assert(icap); ++ assert(-1 == fd || fd == icap->icap_fd); ++ if (icap->respmod.entry) { ++ /* ++ * If we got some error on this side (like ECONNRESET) ++ * we must signal the other side(s) with a storeAbort() ++ * call. ++ */ ++ if (icap->respmod.entry->store_status != STORE_OK) ++ storeAbort(icap->respmod.entry); ++ storeUnlockObject(icap->respmod.entry); ++ icap->respmod.entry = NULL; ++ } ++ requestUnlink(icap->request); ++ icap->request = NULL; ++ if (!memBufIsNull(&icap->icap_hdr)) ++ memBufClean(&icap->icap_hdr); ++ if (!memBufIsNull(&icap->respmod.buffer)) ++ memBufClean(&icap->respmod.buffer); ++ if (!memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufClean(&icap->respmod.req_hdr_copy); ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ if (!memBufIsNull(&icap->reqmod.hdr_buf)) ++ memBufClean(&icap->reqmod.hdr_buf); ++ if (!memBufIsNull(&icap->reqmod.http_entity.buf)) ++ memBufClean(&icap->reqmod.http_entity.buf); ++ if (!memBufIsNull(&icap->chunk_buf)) ++ memBufClean(&icap->chunk_buf); ++ if (icap->httpState) ++ httpStateFree(-1, icap->httpState); ++ cbdataUnlock(icap->reqmod.client_cookie); ++ cbdataFree(icap); ++} ++ ++void ++icapConnectTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapConnectTimeout: FD %d, unreachable=1\n", fd); ++ assert(fd == icap->icap_fd); ++ icapOptSetUnreachable(icap->current_service); ++ comm_close(fd); ++} ++ ++void ++icapReadTimeout(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ assert(fd == icap->icap_fd); ++ if (icap->flags.wait_for_preview_reply || icap->flags.http_server_eof) { ++ debug(81, 3) ("icapReadTimeout: FD %d, unreachable=1\n", fd); ++ icapOptSetUnreachable(icap->current_service); ++ } else ++ debug(81, 3) ("icapReadTimeout: FD %d, still reachable\n", fd); ++ comm_close(fd); ++} ++ ++icap_service_t ++icapServiceToType(const char *s) ++{ ++ if (!strcmp(s, "reqmod_precache")) ++ return ICAP_SERVICE_REQMOD_PRECACHE; ++ if (!strcmp(s, "reqmod_postcache")) ++ return ICAP_SERVICE_REQMOD_POSTCACHE; ++ if (!strcmp(s, "respmod_precache")) ++ return ICAP_SERVICE_RESPMOD_PRECACHE; ++ if (!strcmp(s, "respmod_postcache")) ++ return ICAP_SERVICE_RESPMOD_POSTCACHE; ++ return ICAP_SERVICE_MAX; ++} ++ ++const char * ++icapServiceToStr(const icap_service_t type) ++{ ++ if (type >= 0 && type < ICAP_SERVICE_MAX) ++ return icap_service_type_str[type]; ++ else ++ return "error"; ++} ++ ++ ++/* copied from clientAclChecklistCreate */ ++static aclCheck_t * ++icapAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http) ++{ ++ aclCheck_t *ch; ++ ConnStateData *conn = http->conn; ++ ch = aclChecklistCreate(acl, http->request, 0); ++ ch->conn = conn; ++ cbdataLock(ch->conn); ++ return ch; ++} ++ ++/* ++ * check wether we do icap for a request ++ */ ++int ++icapCheckAcl(clientHttpRequest * http) ++{ ++ icap_access *iter; ++ aclCheck_t *icapChecklist; ++ ++ for (iter = Config.icapcfg.access_head; iter; iter = iter->next) { ++ acl_access *A = iter->access; ++ icapChecklist = icapAclChecklistCreate(A, http); ++ if (aclMatchAclList(A->acl_list, icapChecklist)) { ++ debug(81, 5) ("icapCheckAcl: match for class=%s\n", ++ iter->class->name); ++ if (A->allow) { ++ /* allow rule, do icap and use associated class */ ++ http->request->class = iter->class; ++ aclChecklistFree(icapChecklist); ++ return 1; ++ } else { ++ /* deny rule, stop processing */ ++ aclChecklistFree(icapChecklist); ++ return 0; ++ } ++ } ++ aclChecklistFree(icapChecklist); ++ } ++ return 0; ++} ++ ++/* icapLineLength ++ * ++ * returns the amount of data until lineending ( \r\n ) ++ * This function is NOT tolerant of variations of \r\n. ++ */ ++size_t ++icapLineLength(const char *start, int len) ++{ ++ size_t lineLen = 0; ++ char *end = (char *) memchr(start, '\r', len); ++ if (NULL == end) ++ return 0; ++ end++; /* advance to where '\n' should be */ ++ lineLen = end - start + 1; ++ if (lineLen > len) { ++ debug(0, 0) ("icapLineLength: warning lineLen (%d) > len (%d)\n", ++ lineLen, len); ++ return 0; ++ } ++ if (*end != '\n') { ++ debug(0, 0) ("icapLineLength: warning *end (%x) != '\\n'\n", *end); ++ return 0; ++ } ++ debug(81, 7) ("icapLineLength: returning %d\n", lineLen); ++ return lineLen; ++} ++ ++/* ++ * return: ++ * -1 if EOF before getting end of ICAP header ++ * 0 if we don't have the entire ICAP header yet ++ * 1 if we got the whole header ++ */ ++int ++icapReadHeader(int fd, IcapStateData * icap, int *isIcap) ++{ ++ int headlen = 0; ++ int len = 0; ++ int peek_sz = EXPECTED_ICAP_HEADER_LEN; ++ int read_sz = 0; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ for (;;) { ++ len = recv(fd, tmpbuf, peek_sz, MSG_PEEK); ++ debug(81, 5) ("recv(FD %d, ..., MSG_PEEK) ret %d\n", fd, len); ++ if (len < 0) { ++ debug(81, 1) ("icapReadHeader: FD %d recv error: %s\n", fd, ++ xstrerror()); ++ return -1; ++ } ++ if (len == 0) { ++ debug(81, 2) ("icapReadHeader: FD %d recv EOF\n", fd); ++ return -1; ++ } ++ headlen = headersEnd(tmpbuf, len); ++ debug(81, 3) ("headlen=%d\n", headlen); ++ /* ++ * break if we now know where the ICAP headers end ++ */ ++ if (headlen) ++ break; ++ /* ++ * break if we know there is no more data to read ++ */ ++ if (len < peek_sz) ++ break; ++ /* ++ * The ICAP header is larger than (or equal to) our read ++ * buffer, so double it and try to peek again. ++ */ ++ peek_sz *= 2; ++ if (peek_sz >= SQUID_TCP_SO_RCVBUF) { ++ debug(81, ++ 1) ("icapReadHeader: Failed to find end of ICAP header\n"); ++ debug(81, 1) ("\twithin first %d bytes of response\n", ++ SQUID_TCP_SO_RCVBUF); ++ debug(81, 1) ("\tpossible persistent connection bug/confusion\n"); ++ return -1; ++ } ++ } ++ /* ++ * Now actually read the data from the kernel ++ */ ++ if (headlen) ++ read_sz = headlen; ++ else ++ read_sz = len; ++ len = FD_READ_METHOD(fd, tmpbuf, read_sz); ++ assert(len == read_sz); ++ fd_bytes(fd, len, FD_READ); ++ memBufAppend(&icap->icap_hdr, tmpbuf, len); ++ if (headlen) { ++ /* End of ICAP header found */ ++ if (icap->icap_hdr.size < 4) ++ *isIcap = 0; ++ else if (0 == strncmp(icap->icap_hdr.buf, "ICAP", 4)) ++ *isIcap = 1; ++ else ++ *isIcap = 0; ++ return 1; ++ } ++ /* ++ * We don't have all the headers yet ++ */ ++ return 0; ++} ++ ++static int ++icapParseConnectionClose(const IcapStateData * icap, const char *s, ++ const char *e) ++{ ++ char *t; ++ char *q; ++ /* ++ * s points to the start of the line "Connection: ... " ++ * e points to *after* the last character on the line ++ */ ++ s += 11; /* skip past Connection: */ ++ while (s < e && isspace(*s)) ++ s++; ++ if (e - s < 5) ++ return 0; ++ /* ++ * create a buffer that we can use strtok on ++ */ ++ t = xmalloc(e - s + 1); ++ strncpy(t, s, e - s); ++ *(t + (e - s)) = '\0'; ++ for (q = strtok(t, ","); q; q = strtok(NULL, ",")) { ++ if (0 == strcasecmp(q, "close")) { ++ xfree(t); ++ return 1; ++ } ++ } ++ xfree(t); ++ return 0; ++} ++ ++/* returns icap status, version and subversion extracted from status line or -1 on parsing failure ++ * The str_status pointr points to the text returned from the icap server. ++ * sline probably is NOT terminated with '\0' ++ */ ++int ++icapParseStatusLine(const char *sline, int slinesize, int *version_major, ++ int *version_minor, const char **str_status) ++{ ++ char *sp, *stmp, *ep = (char *) sline + slinesize; ++ int status; ++ if (slinesize < 14) /*The format of this line is: "ICAP/x.x xxx[ msg....]\r\n" */ ++ return -1; ++ ++ if (strncmp(sline, "ICAP/", 5) != 0) ++ return -1; ++ if (sscanf(sline + 5, "%d.%d", version_major, version_minor) != 2) ++ return -1; ++ ++ if (!(sp = memchr(sline, ' ', slinesize))) ++ return -1; ++ ++ while (sp < ep && xisspace(*++sp)); ++ ++ if (!xisdigit(*sp) || sp >= ep) ++ return -1; ++ ++ if ((status = strtol(sp, &stmp, 10)) <= 0) ++ return -1; ++ sp = stmp; ++ ++ while (sp < ep && xisspace(*++sp)); ++ *str_status = sp; ++ /*Must add a test for "\r\n" end headers .... */ ++ return status; ++} ++ ++ ++void ++icapSetKeepAlive(IcapStateData * icap, const char *hdrs) ++{ ++ const char *start; ++ const char *end; ++ if (0 == icap->flags.keep_alive) ++ return; ++ if (0 == icapFindHeader(hdrs, "Connection:", &start, &end)) { ++ icap->flags.keep_alive = 1; ++ return; ++ } ++ if (icapParseConnectionClose(icap, start, end)) ++ icap->flags.keep_alive = 0; ++ else ++ icap->flags.keep_alive = 1; ++} ++ ++/* ++ * icapParseChunkSize ++ * ++ * Returns the offset where the next chunk starts ++ * return parameter chunk_size; ++ */ ++static int ++icapParseChunkSize(const char *buf, int len, int *chunk_size) ++{ ++ int chunkSize = 0; ++ char c; ++ size_t start; ++ size_t end; ++ size_t nextStart = 0; ++ debug(81, 3) ("icapParseChunkSize: buf=%p, len=%d\n", buf, len); ++ do { ++ start = nextStart; ++ debug(81, 3) ("icapParseChunkSize: start=%d\n", start); ++ if (len <= start) { ++ /* ++ * end of buffer, so far no lines or only empty lines, ++ * wait for more data. read chunk size with next buffer. ++ */ ++ *chunk_size = 0; ++ return 0; ++ } ++ end = start + icapLineLength(buf + start, len - start); ++ nextStart = end; ++ if (end <= start) { ++ /* ++ * no line found, need more code here, now we are in ++ * deep trouble, buffer stops with half a chunk size ++ * line. For now stop here. ++ */ ++ debug(81, 1) ("icapParseChunkSize: WARNING in mid-line, ret 0\n"); ++ *chunk_size = 0; ++ return 0; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[start])) ++ break; ++ start++; ++ } ++ while (start < end) { ++ if (NULL == strchr(w_space, buf[end - 1])) ++ break; ++ end--; ++ } ++ /* ++ * if now end <= start we got an empty line. The previous ++ * chunk data should stop with a CRLF. In case that the ++ * other end does not follow the specs and sends no CRLF ++ * or too many empty lines, just continue till we have a ++ * non-empty line. ++ */ ++ } while (end <= start); ++ debug(81, 3) ("icapParseChunkSize: start=%d, end=%d\n", start, end); ++ ++ /* Non-empty line: Parse the chunk size */ ++ while (start < end) { ++ c = buf[start++]; ++ if (c >= 'a' && c <= 'f') { ++ chunkSize = chunkSize * 16 + c - 'a' + 10; ++ } else if (c >= 'A' && c <= 'F') { ++ chunkSize = chunkSize * 16 + c - 'A' + 10; ++ } else if (c >= '0' && c <= '9') { ++ chunkSize = chunkSize * 16 + c - '0'; ++ } else { ++ if (!(c == ';' || c == ' ' || c == '\t')) { ++ /*Syntax error: Chunksize expected. */ ++ *chunk_size = -2; /* we are done */ ++ return nextStart; ++ } ++ /* Next comes a chunk extension */ ++ break; ++ } ++ } ++ /* ++ * if we read a zero chunk, we reached the end. Mark this for ++ * icapPconnTransferDone ++ */ ++ *chunk_size = (chunkSize > 0) ? chunkSize : -2; ++ debug(81, 3) ("icapParseChunkSize: return nextStart=%d\n", nextStart); ++ return nextStart; ++} ++ ++/* ++ * icapParseChunkedBody ++ * ++ * De-chunk an HTTP entity received from the ICAP server. ++ * The 'store' function pointer is storeAppend() or memBufAppend(). ++ */ ++size_t ++icapParseChunkedBody(IcapStateData * icap, STRCB * store, void *store_data) ++{ ++ int bufOffset = 0; ++ size_t bw = 0; ++ MemBuf *cb = &icap->chunk_buf; ++ const char *buf = cb->buf; ++ int len = cb->size; ++ ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ return 0; ++ } ++ debug(81, 3) ("%s:%d: chunk_size=%d\n", __FILE__, __LINE__, ++ icap->chunk_size); ++ if (icap->chunk_size < 0) { ++ store(store_data, buf, len); ++ cb->size = 0; ++ return (size_t) len; ++ } ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ while (bufOffset < len) { ++ debug(81, 3) ("%s:%d: bufOffset=%d, len=%d\n", __FILE__, __LINE__, ++ bufOffset, len); ++ if (icap->chunk_size == 0) { ++ int x; ++ x = icapParseChunkSize(buf + bufOffset, ++ len - bufOffset, &icap->chunk_size); ++ if (x < 1) { ++ /* didn't find a valid chunk spec */ ++ break; ++ } ++ bufOffset += x; ++ debug(81, 3) ("got chunksize %d, new offset %d\n", ++ icap->chunk_size, bufOffset); ++ if (icap->chunk_size == -2) { ++ debug(81, 3) ("zero end chunk reached\n"); ++ break; ++ } ++ } ++ debug(81, 3) ("%s:%d: X\n", __FILE__, __LINE__); ++ if (icap->chunk_size > 0) { ++ if (icap->chunk_size >= len - bufOffset) { ++ store(store_data, buf + bufOffset, len - bufOffset); ++ bw += (len - bufOffset); ++ icap->chunk_size -= (len - bufOffset); ++ bufOffset = len; ++ } else { ++ store(store_data, buf + bufOffset, icap->chunk_size); ++ bufOffset += icap->chunk_size; ++ bw += icap->chunk_size; ++ icap->chunk_size = 0; ++ } ++ } ++ } ++ if (0 == bufOffset) { ++ (void) 0; ++ } else if (bufOffset == cb->size) { ++ cb->size = 0; ++ } else { ++ assert(bufOffset <= cb->size); ++ xmemmove(cb->buf, cb->buf + bufOffset, cb->size - bufOffset); ++ cb->size -= bufOffset; ++ } ++ return bw; ++} ++ ++/* ++ * icapAddAuthUserHeader ++ * ++ * Builds and adds the X-Authenticated-User header to an ICAP request headers. ++ */ ++void ++icapAddAuthUserHeader(MemBuf * mb, auth_user_request_t * auth_user_request) ++{ ++ char *user = authenticateUserRequestUsername(auth_user_request); ++ char *authuser; ++ size_t len, userlen, schemelen, userofslen; ++ char *userofs; ++ ++ if (user == NULL) { ++ debug(81, 5) ("icapAddAuthUserHeader: NULL username\n"); ++ return; ++ } ++ userlen = strlen(user); ++ schemelen = strlen(Config.icapcfg.auth_scheme); ++ len = userlen + schemelen + 1; ++ authuser = xcalloc(len, 1); ++ ++ if ((userofs = strstr(Config.icapcfg.auth_scheme, "%u")) == NULL) { ++ /* simply add user at end of string */ ++ snprintf(authuser, len, "%s%s", Config.icapcfg.auth_scheme, user); ++ } else { ++ userofslen = userofs - Config.icapcfg.auth_scheme; ++ xmemcpy(authuser, Config.icapcfg.auth_scheme, userofslen); ++ xmemcpy(authuser + userofslen, user, userlen); ++ xmemcpy(authuser + userofslen + userlen, ++ userofs + 2, schemelen - (userofslen + 2) + 1); ++ } ++ ++ memBufPrintf(mb, "X-Authenticated-User: %s\r\n", base64_encode(authuser)); ++ xfree(authuser); ++} ++ ++/* ++ * icapAddOriginIP ++ * ++ * Builds and adds the X-Server-IP header to an ICAP request headers. ++ */ ++void ++icapAddOriginIP(MemBuf * mb, const char *host) ++{ ++ const ipcache_addrs *addrs; ++ struct in_addr s; ++ ++ if (host == NULL) { ++ debug(81, 5) ("icapAddOriginIP: NULL host\n"); ++ return; ++ } ++ addrs = ipcache_gethostbyname(host, IP_LOOKUP_IF_MISS); ++ if (addrs == NULL) { ++ /* ++ * http://www.i-cap.org/spec/draft-stecher-icap-subid-00.txt : ++ * ++ * [...] If the meta information for some header is not available, ++ * the header field MUST be omitted. ++ */ ++ debug(81, 5) ("icapAddOriginIP: can't tell IP address\n"); ++ return; ++ } ++ s = addrs->in_addrs[0]; ++ memBufPrintf(mb, "X-Server-IP: %s\r\n", inet_ntoa(s)); ++} +Index: 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 17 May 2006 17:58:01 -0000 1.1.16.1 +@@ -0,0 +1,523 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client OPTIONS ++ * AUTHOR: Ralf Horstmann ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++/*************************************************************/ ++ ++/* ++ * network related functions for OPTIONS request ++ */ ++static void icapOptStart(void *data); ++static void icapOptTimeout(int fd, void *data); ++static void icapOptConnectDone(int server_fd, int status, void *data); ++static void icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data); ++static void icapOptReadReply(int fd, void *data); ++ ++/* ++ * reply parsing functions ++ */ ++static int icapOptParseReply(icap_service * s, IcapOptData * i); ++static void icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end); ++static int icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end); ++ ++/* ++ * helper functions ++ */ ++static void icapOptDataInit(IcapOptData * i); ++static void icapOptDataFree(IcapOptData * i); ++ ++/*************************************************************/ ++ ++#define TIMEOUT 10 ++ ++void ++icapOptInit() ++{ ++ icap_service *s; ++ ++ /* iterate over configured services */ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ eventAdd("icapOptStart", icapOptStart, s, 5.0, 1); ++ s = s->next; ++ } ++} ++ ++void ++icapOptShutdown() ++{ ++ icap_service *s; ++ ++ s = Config.icapcfg.service_head; ++ while (s) { ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ } ++ s = s->next; ++ } ++} ++ ++/* ++ * mark a service as unreachable ++ */ ++void ++icapOptSetUnreachable(icap_service * s) ++{ ++ s->unreachable = 1; ++ debug(81, 5) ("icapOptSetUnreachable: got called for %s\n", s->uri); ++ /* ++ * if there is an options request scheduled, delete it and add ++ * it again to reset the time to the default check_interval. ++ */ ++ if (eventFind(icapOptStart, s)) { ++ eventDelete(icapOptStart, s); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ } ++} ++ ++static void ++icapOptStart(void *data) ++{ ++ icap_service *s = data; ++ int fd; ++ int ctimeout = TIMEOUT; ++ const char *host = s->hostname; ++ unsigned short port = s->port; ++ debug(81, 3) ("icapOptStart: starting OPTIONS request for %s (%s)\n", s->name, s->uri); ++ fd = comm_open(SOCK_STREAM, ++ 0, ++ getOutgoingAddr(NULL), ++ 0, ++ COMM_NONBLOCKING, ++ "ICAP OPTIONS connection"); ++ if (fd < 0) { ++ debug(81, 4) ("icapConnectStart: %s\n", xstrerror()); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ assert(s->opt == NULL); /* if not null, another options request might be running, which should not happen */ ++ s->opt = memAllocate(MEM_ICAP_OPT_DATA); ++ icapOptDataInit(s->opt); ++ cbdataLock(s); ++ commSetTimeout(fd, ctimeout, icapOptTimeout, s); ++ commConnectStart(fd, host, port, icapOptConnectDone, s); ++} ++ ++static void ++icapOptTimeout(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ debug(81, 4) ("icapOptConnectTimeout: fd=%d, service=%s\n", fd, s->uri); ++ ++ comm_close(fd); ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ /* try again later */ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ debug(81, 3) ("icapOptConnectTimeout: unreachable=1, service=%s\n", s->uri); ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ ++} ++ ++static void ++icapOptConnectDone(int server_fd, int status, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ MemBuf request; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (status != COMM_OK) { ++ debug(81, 3) ("icapOptConnectDone: unreachable=1, service=%s\n", s->uri); ++ comm_close(server_fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ return; ++ } ++ debug(81, 3) ("icapOptConnectDone: Connection ok. Sending Options request for %s\n", s->name); ++ memBufDefInit(&request); ++ memBufPrintf(&request, "OPTIONS %s ICAP/1.0\r\n", s->uri); ++ memBufPrintf(&request, "Host: %s\r\n", s->hostname); ++ memBufPrintf(&request, "Connection: close\r\n"); ++ memBufPrintf(&request, "User-Agent: ICAP-Client-Squid/1.2\r\n"); ++ memBufPrintf(&request, "\r\n"); ++ cbdataLock(s); ++ commSetTimeout(server_fd, TIMEOUT, icapOptTimeout, s); ++ comm_write_mbuf(server_fd, request, icapOptWriteComplete, s); ++} ++ ++static void ++icapOptWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ debug(81, 5) ("icapOptWriteComplete: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag) { ++ /* cancel this for now */ ++ debug(81, 3) ("icapOptWriteComplete: unreachable=1, service=%s\n", s->uri); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ s->unreachable = 1; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ return; ++ } ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, s, 0); ++} ++ ++static void ++icapOptReadReply(int fd, void *data) ++{ ++ icap_service *s = data; ++ IcapOptData *i = s->opt; ++ int size; ++ int len = i->size - i->offset - 1; ++ int valid; ++ ++ valid = cbdataValid(s); ++ cbdataUnlock(s); ++ if (!valid) { ++ comm_close(fd); ++ icapOptDataFree(i); ++ s->opt = NULL; ++ return; ++ } ++ if (len == 0) { ++ /* Grow the request memory area to accomodate for a large request */ ++ printf("PANIC: not enough memory\n"); ++#if 0 ++ i->buf = memReallocBuf(i->buf, i->size * 2, &i->size); ++ debug(81, 2) ("icapoptReadReply: growing reply buffer: offset=%ld size=%ld\n", ++ (long) i->offset, (long) i->size); ++ len = i->size - i->offset - 1; ++#endif ++ } ++ size = FD_READ_METHOD(fd, i->buf + i->offset, len); ++ i->offset += size; ++ debug(81, 3) ("icapOptReadReply: Got %d bytes of data\n", size); ++ if (size > 0) { ++ /* do some statistics */ ++ fd_bytes(fd, size, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, size); ++ ++ /* ++ * some icap servers seem to ignore the "Connection: close" header. so ++ * after getting the complete option reply we close the connection ++ * ourself. ++ */ ++ if ((i->headlen = headersEnd(i->buf, i->offset))) { ++ debug(81, 3) ("icapOptReadReply: EndOfResponse\n"); ++ size = 0; ++ } ++ } ++ if (size < 0) { ++ debug(81, 3) ("icapOptReadReply: FD %d: read failure: %s.\n", fd, xstrerror()); ++ debug(81, 3) ("icapOptReadReply: unreachable=1, service=%s.\n", s->uri); ++ s->unreachable = 1; ++ icapOptDataFree(i); ++ s->opt = NULL; ++ eventAdd("icapOptStart", icapOptStart, s, Config.icapcfg.check_interval, 1); ++ comm_close(fd); ++ } else if (size == 0) { ++ /* no more data, now we can parse the reply */ ++ debug(81, 3) ("icapOptReadReply: FD %d: connection closed\n", fd); ++ i->buf[i->offset] = '\0'; /* for string functions */ ++ debug(81, 3) ("icapOptReadReply: unreachable=0, service=%s\n", s->uri); ++ ++ if (!icapOptParseReply(s, i)) { ++ debug(81, 3) ("icapOptReadReply: OPTIONS request not successful. scheduling again in %d seconds\n", Config.icapcfg.check_interval); ++ s->unreachable = 1; ++ } else ++ s->unreachable = 0; ++ ++ if (s->options_ttl <= 0) ++ s->options_ttl = Config.icapcfg.check_interval; ++ eventAdd("icapOptStart", icapOptStart, s, s->options_ttl, 1); ++ ++ icapOptDataFree(i); ++ s->opt = NULL; ++ comm_close(fd); ++ } else { ++ /* data received */ ++ /* commSetSelect(fd, Type, handler, client_data, timeout) */ ++ cbdataLock(s); ++ commSetSelect(fd, COMM_SELECT_READ, icapOptReadReply, data, 0); ++ } ++} ++ ++static int ++icapIsolateLine(const char **parse_start, const char **blk_start, const char **blk_end) ++{ ++ int slen = strcspn(*parse_start, "\r\n"); ++ ++ if (!(*parse_start)[slen]) /* no crlf */ ++ return 0; ++ ++ if (slen == 0) /* empty line */ ++ return 0; ++ ++ *blk_start = *parse_start; ++ *blk_end = *blk_start + slen; ++ ++ /* set it to the beginning of next line */ ++ *parse_start = *blk_end; ++ while (**parse_start == '\r') /* CR */ ++ (*parse_start)++; ++ if (**parse_start == '\n') /* LF */ ++ (*parse_start)++; ++ return 1; ++} ++ ++/* process a single header entry between blk_start and blk_end */ ++static void ++icapOptParseEntry(icap_service * s, const char *blk_start, const char *blk_end) ++{ ++ const char *name_end = strchr(blk_start, ':'); ++ const int name_len = name_end ? name_end - blk_start : 0; ++ const char *value_start = blk_start + name_len + 1; /* skip ':' */ ++ int value_len; ++ int new; ++ ++ if (!name_len || name_end > blk_end) { ++ debug(81, 5) ("icapOptParseEntry: strange header. skipping\n"); ++ return; ++ } ++ if (name_len > 65536) { ++ debug(81, 5) ("icapOptParseEntry: unusual long header item. skipping.\n"); ++ return; ++ } ++ while (xisspace(*value_start) && value_start < blk_end) { ++ value_start++; ++ } ++ if (value_start >= blk_end) { ++ debug(81, 5) ("icapOptParseEntry: no value found\n"); ++ return; ++ } ++ value_len = blk_end - value_start; ++ ++ ++ /* extract information */ ++ if (!strncasecmp("Allow", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Allow\n"); ++ if (!strncmp("204", value_start, 3)) { ++ s->flags.allow_204 = 1; ++ } else { ++ debug(81, 3) ("icapOptParseEntry: Allow value unknown"); ++ } ++ } else if (!strncasecmp("Connection", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Connection\n"); ++ } else if (!strncasecmp("Encapsulated", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Encapsulated\n"); ++ } else if (!strncasecmp("ISTAG", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found ISTAG\n"); ++ stringClean(&s->istag); ++ stringLimitInit(&s->istag, value_start, value_len); ++ } else if (!strncasecmp("Max-Connections", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Max-Connections\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Max-Connections: new value=%d\n", new); ++ s->max_connections = new; ++ } ++ } else if (!strncasecmp("Methods", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Methods\n"); ++ } else if (!strncasecmp("Options-TTL", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Options-TTL\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Options-TTL: new value=%d\n", new); ++ s->options_ttl = new; ++ } ++ } else if (!strncasecmp("Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Preview\n"); ++ errno = 0; ++ new = strtol(value_start, NULL, 10); ++ if (errno) { ++ debug(81, 5) ("icapOptParseEntry: Preview: could not parse value\n"); ++ } else { ++ debug(81, 5) ("icapOptParseEntry: Preview: new value=%d\n", new); ++ s->preview = new; ++ } ++ } else if (!strncasecmp("Service", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service\n"); ++ } else if (!strncasecmp("Service-ID", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Service-ID\n"); ++ } else if (!strncasecmp("Transfer-Preview", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Preview\n"); ++ stringClean(&s->transfer_preview); ++ stringLimitInit(&s->transfer_preview, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Ignore", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Ignore\n"); ++ stringClean(&s->transfer_ignore); ++ stringLimitInit(&s->transfer_ignore, value_start, value_len); ++ } else if (!strncasecmp("Transfer-Complete", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found Transfer-Complete\n"); ++ stringClean(&s->transfer_complete); ++ stringLimitInit(&s->transfer_complete, value_start, value_len); ++ } else if (!strncasecmp("X-Include", blk_start, name_len)) { ++ debug(81, 5) ("icapOptParseEntry: found X-Include\n"); ++ if (strstr(value_start, "X-Client-IP")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Client-IP\n"); ++ s->flags.need_x_client_ip = 1; ++ } ++ if (strstr(value_start, "X-Server-IP")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Server-IP\n"); ++ s->flags.need_x_server_ip = 1; ++ } ++ if (strstr(value_start, "X-Authenticated-User")) { ++ debug(81, 5) ("icapOptParseEntry: X-Include: found X-Authenticated-User\n"); ++ s->flags.need_x_authenticated_user = 1; ++ } ++ } else { ++ debug(81, 5) ("icapOptParseEntry: unknown options header\n"); ++ } ++} ++ ++/* parse OPTIONS reply */ ++static int ++icapOptParseReply(icap_service * s, IcapOptData * i) ++{ ++ int version_major, version_minor; ++ const char *str_status; ++ int status; ++ const char *buf = i->buf; ++ const char *parse_start; ++ const char *head_end; ++ const char *blk_start; ++ const char *blk_end; ++ ++ if ((status = ++ icapParseStatusLine(i->buf, i->offset, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 2) ("icapOptParseReply: bad status line <%s>\n", i->buf); ++ return 0; ++ } ++ debug(81, 3) ("icapOptParseReply: got reply: \n", version_major, version_minor, status, str_status); ++ ++ if (status != 200) { ++ debug(81, 3) ("icapOptParseReply: status = %d != 200\n", status); ++ return 0; ++ } ++ parse_start = buf; ++ if (i->headlen == 0) ++ i->headlen = headersEnd(parse_start, s->opt->offset); ++ ++ if (!i->headlen) { ++ debug(81, 2) ("icapOptParseReply: end of headers could not be found\n"); ++ return 0; ++ } ++ head_end = parse_start + i->headlen - 1; ++ while (*(head_end - 1) == '\r') ++ head_end--; ++ assert(*(head_end - 1) == '\n'); ++ if (*head_end != '\r' && *head_end != '\n') ++ return 0; /* failure */ ++ ++ /* skip status line */ ++ if (!icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ debug(81, 3) ("icapOptParseReply: failure in isolating status line\n"); ++ return 0; ++ ++ } ++ /* now we might start real parsing */ ++ while (icapIsolateLine(&parse_start, &blk_start, &blk_end)) { ++ if (blk_end > head_end || blk_start > head_end || blk_start >= blk_end) { ++ debug(81, 3) ("icapOptParseReply: header limit exceeded. finished.\n"); ++ break; ++ } ++ icapOptParseEntry(s, blk_start, blk_end); ++ } ++ return 1; ++} ++ ++static void ++icapOptDataInit(IcapOptData * i) ++{ ++ i->buf = memAllocBuf(HTTP_REPLY_BUF_SZ, &i->size); ++ i->offset = 0; ++ i->headlen = 0; ++} ++ ++static void ++icapOptDataFree(IcapOptData * i) ++{ ++ if (i) { ++ memFreeBuf(i->size, i->buf); ++ memFree(i, MEM_ICAP_OPT_DATA); ++ } ++} +Index: 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 13 Jun 2006 17:14:53 -0000 1.1.14.6 +@@ -0,0 +1,992 @@ ++ ++/* ++ * $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 (request) ++ http->http_ver = request->http_ver; ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Here it is possible becouse we are using as client_cookie the original http->conn ++ * if we will keep this code we must declare an icap->conn field........ ++ * Will work if pipeline_prefetch is not enabled ++ * We are using a dummy ConnStateData structure, just to free ++ * old clientHttpRequest :-( ++ * OK,all this code is a hack and possibly must not exists in cvs ...... ++ */ ++ ++ http->conn = icap->reqmod.client_cookie; ++ assert(http->conn->chr->next == NULL); ++ { ++ ConnStateData *dummyconn; ++ dummyconn = cbdataAlloc(ConnStateData); ++ dummyconn->fd = icap->reqmod.client_fd; ++ dummyconn->pinning.fd = -1; ++ dummyconn->chr = http->conn->chr; ++ dummyconn->chr->conn = dummyconn; ++ comm_add_close_handler(dummyconn->fd, connStateFree, dummyconn); ++ } ++ http->conn->chr = http; ++#else ++ http->conn = cbdataAlloc(ConnStateData); ++ http->conn->fd = icap->reqmod.client_fd; ++ http->conn->pinning.fd = -1; ++ http->conn->in.size = 0; ++ http->conn->in.buf = NULL; ++ http->conn->log_addr = icap->reqmod.log_addr; ++ http->conn->chr = http; ++ comm_add_close_handler(http->conn->fd, connStateFree, http->conn); ++#endif ++ http->icap_reqmod = NULL; ++ return http; ++} ++ ++/* ++ * icapReqModInterpretHttpRequest ++ * ++ * Interpret an HTTP request that we read from the ICAP server. ++ * Create some "fake" clientHttpRequest and ConnStateData structures ++ * so we can pass this new request off to the routines in ++ * client_side.c. ++ */ ++static void ++icapReqModInterpretHttpRequest(IcapStateData * icap, request_t * request) ++{ ++ clientHttpRequest *http = icapReqModCreateClientState(icap, request); ++ if (NULL == http) ++ return; ++ /* ++ * bits from clientReadRequest ++ */ ++ request->content_length = httpHeaderGetSize(&request->header, ++ HDR_CONTENT_LENGTH); ++ if (!urlCheckRequest(request) || ++ httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) { ++ ErrorState *err; ++ err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED); ++ 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->client_port = icap->request->client_port; ++ request->my_addr = icap->request->my_addr; ++ request->my_port = icap->request->my_port; ++ request->class = icap->request->class; ++ if (icap->request->auth_user_request) { ++ /* Copy authentification info in new request */ ++ request->auth_user_request = icap->request->auth_user_request; ++ authenticateAuthUserRequestLock(request->auth_user_request); ++ } ++ request->content_length = httpHeaderGetSize(&request->header, ++ HDR_CONTENT_LENGTH); ++ if (strBuf(icap->request->extacl_log)) ++ request->extacl_log = stringDup(&icap->request->extacl_log); ++ if (icap->request->extacl_user) ++ request->extacl_user = xstrdup(icap->request->extacl_user); ++ if (icap->request->extacl_passwd) ++ request->extacl_passwd = xstrdup(icap->request->extacl_passwd); ++#if ICAP_PROXY_KEEP_ALIVE ++ /* ++ * Copy the proxy_keepalive flag from the original request ++ */ ++ request->flags.proxy_keepalive = icap->request->flags.proxy_keepalive; ++ /* ++ * If proxy_keepalive was set for the original request, make ++ * sure that the adapated request also has the necessary headers ++ * for keepalive ++ */ ++ if (request->flags.proxy_keepalive) { ++ if (!httpMsgIsPersistent(http_ver, &request->header)) ++ request->flags.proxy_keepalive = 0; ++ } ++#endif ++ icapReqModInterpretHttpRequest(icap, request); ++ xfree(inbuf); ++} ++ ++/* ++ * icapReqModHandoffRespMod ++ * ++ * Handles the case where a REQMOD request results in an HTTP REPLY ++ * (instead of an ICAP REPLY that contains a new HTTP REQUEST). We ++ * prepare the IcapStateData for passing off to the icap_reqmod ++ * code, where we have functions for reading HTTP replies in ICAP ++ * messages. ++ */ ++static void ++icapReqModHandoffRespMod(IcapStateData * icap) ++{ ++ extern PF icapReadReply; ++ clientHttpRequest *http = icapReqModCreateClientState(icap, NULL); ++ if (NULL == http) ++ return; ++ assert(icap->request); ++ ++ http->entry = clientCreateStoreEntry(http, ++ icap->request->method, icap->request->flags); ++ icap->respmod.entry = http->entry; ++ storeLockObject(icap->respmod.entry); ++ ++ /* icap->http_flags = ? */ ++ memBufDefInit(&icap->respmod.buffer); ++ memBufDefInit(&icap->chunk_buf); ++ assert(icap->current_service); ++ icapReadReply(icap->icap_fd, icap); ++} ++ ++/* ++ * icapReqModKeepAliveOrClose ++ * ++ * Called when we are done reading from the ICAP server. ++ * Either close the connection or keep it open for a future ++ * transaction. ++ */ ++static void ++icapReqModKeepAliveOrClose(IcapStateData * icap) ++{ ++ int fd = icap->icap_fd; ++ debug(81, 3) ("%s:%d FD %d\n", __FILE__, __LINE__, fd); ++ if (fd < 0) ++ return; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } ++ if (icap->request->content_length < 0) { ++ /* no message body */ ++ debug(81, 3) ("%s:%d no message body\n", __FILE__, __LINE__); ++ if (1 != icap->reqmod.hdr_state) { ++ /* didn't get to end of HTTP headers */ ++ debug(81, 3) ("%s:%d didnt find end of headers, closing\n", ++ __FILE__, __LINE__); ++ comm_close(fd); ++ return; ++ } ++ } else if (icap->reqmod.http_entity.bytes_read != ++ icap->request->content_length) { ++ debug(81, 3) ("%s:%d bytes_read (%" PRINTF_OFF_T ") != content_length (%" PRINTF_OFF_T ")\n", ++ __FILE__, __LINE__, icap->reqmod.http_entity.bytes_read, ++ icap->request->content_length); ++ /* an error */ ++ comm_close(fd); ++ return; ++ } ++ debug(81, 3) ("%s:%d looks good, keeping alive\n", __FILE__, __LINE__); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port, NULL, NULL, 0); ++ icap->icap_fd = -1; ++ icapStateFree(-1, icap); ++} ++ ++/* ++ * icapReqModReadHttpHdrs ++ * ++ * Read the HTTP reply from the ICAP server. Uses the values ++ * from the ICAP Encapsulation header to know how many bytes ++ * to read. ++ */ ++static void ++icapReqModReadHttpHdrs(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ LOCAL_ARRAY(char, tmpbuf, SQUID_TCP_SO_RCVBUF); ++ int rl; ++ debug(81, 3) ("icapReqModReadHttpHdrs:\n"); ++ assert(fd == icap->icap_fd); ++ assert(icap->enc.req_hdr == 0); ++ if (0 == icap->reqmod.hdr_state) { ++ int expect = icapExpectedHttpReqHdrSize(icap); ++ int so_far = icap->http_header_bytes_read_so_far; ++ int needed = expect - so_far; ++ debug(81, 3) ("expect=%d\n", expect); ++ debug(81, 3) ("so_far=%d\n", so_far); ++ debug(81, 3) ("needed=%d\n", needed); ++ assert(needed >= 0); ++ if (0 == expect) { ++ fatalf("unexpected condition in %s:%d", __FILE__, __LINE__); ++ } ++ rl = FD_READ_METHOD(fd, tmpbuf, needed); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read %d bytes\n", rl); ++ if (rl < 0) { ++ fatalf("need to handle read error at %s:%d", __FILE__, __LINE__); ++ } ++ fd_bytes(fd, rl, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, rl); ++ memBufAppend(&icap->reqmod.hdr_buf, tmpbuf, rl); ++ icap->http_header_bytes_read_so_far += rl; ++ if (rl != needed) { ++ /* still more header data to read */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, ++ 0); ++ return; ++ } ++ icap->reqmod.hdr_state = 1; ++ } ++ assert(1 == icap->reqmod.hdr_state); ++ debug(81, 3) ("icapReqModReadHttpHdrs: read the entire request headers\n"); ++ icapReqModParseHttpRequest(icap); ++ if (-1 == icap->reqmod.client_fd) { ++ /* we detected that the original client_side went away */ ++ icapReqModKeepAliveOrClose(icap); ++ } else if (icap->enc.req_body > -1) { ++ icap->chunk_size = 0; ++ memBufDefInit(&icap->chunk_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpBody, icap, 0); ++ } else { ++ icapReqModKeepAliveOrClose(icap); ++ } ++} ++ ++ ++/* ++ * icapReqModReadIcapPart ++ * ++ * Read the ICAP reply header. ++ */ ++static void ++icapReqModReadIcapPart(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int version_major, version_minor; ++ const char *str_status; ++ int x; ++ const char *start; ++ const char *end; ++ int status; ++ int isIcap = 0; ++ int directResponse = 0; ++ ++ debug(81, 5) ("icapReqModReadIcapPart: FD %d httpState = %p\n", fd, data); ++ statCounter.syscalls.sock.reads++; ++ ++ x = icapReadHeader(fd, icap, &isIcap); ++ if (x < 0) { ++ /* Did not find a proper ICAP response */ ++ debug(81, 3) ("ICAP : Error path!\n"); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ if (x == 0) { ++ /* ++ * Waiting for more headers. Schedule new read hander, but ++ * don't reset timeout. ++ */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ return; ++ } ++ /* ++ * Parse the ICAP header ++ */ ++ assert(icap->icap_hdr.size); ++ debug(81, 3) ("Read icap header : <%s>\n", icap->icap_hdr.buf); ++ if ((status = ++ icapParseStatusLine(icap->icap_hdr.buf, icap->icap_hdr.size, ++ &version_major, &version_minor, &str_status)) < 0) { ++ debug(81, 1) ("BAD ICAP status line <%s>\n", icap->icap_hdr.buf); ++ /* is this correct in case of ICAP protocol error? */ ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ }; ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ icapSetKeepAlive(icap, icap->icap_hdr.buf); ++ if (icapFindHeader(icap->icap_hdr.buf, "Encapsulated:", &start, &end)) { ++ icapParseEncapsulated(icap, start, end); ++ } else { ++ debug(81, ++ 1) ++ ("WARNING: icapReqModReadIcapPart() did not find 'Encapsulated' header\n"); ++ } ++ if (icap->enc.res_hdr > -1) ++ directResponse = 1; ++ else if (icap->enc.res_body > -1) ++ directResponse = 1; ++ else ++ directResponse = 0; ++ debug(81, 3) ("icapReqModReadIcapPart: directResponse=%d\n", ++ directResponse); ++ ++ /* Check whether it is a direct reply - if so over to http part */ ++ if (directResponse) { ++ debug(81, ++ 3) ++ ("icapReqModReadIcapPart: FD %d, processing HTTP response for REQMOD!\n", ++ fd); ++ /* got the reply, no need to come here again */ ++ icap->flags.wait_for_reply = 0; ++ icap->flags.got_reply = 1; ++ icapReqModHandoffRespMod(icap); ++ return; ++ } ++ memBufDefInit(&icap->reqmod.hdr_buf); ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadHttpHdrs, icap, 0); ++ return; ++} ++ ++/* ++ * icapSendReqModDone ++ * ++ * Called after we've sent the ICAP request. Checks for errors ++ * and installs the handler functions for the next step. ++ */ ++static void ++icapSendReqModDone(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ ++ debug(81, 5) ("icapSendReqModDone: FD %d: size %d: errflag %d.\n", ++ fd, size, errflag); ++ if (size > 0) { ++ fd_bytes(fd, size, FD_WRITE); ++ kb_incr(&statCounter.icap.all.kbytes_out, size); ++ } ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ debug(81, 3) ("icapSendReqModDone: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ /* Schedule read reply. */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReqModReadIcapPart, icap, 0); ++ /* ++ * Set the read timeout here because it hasn't been set yet. ++ * We only set the read timeout after the request has been ++ * fully written to the server-side. If we start the timeout ++ * after connection establishment, then we are likely to hit ++ * the timeout for POST/PUT requests that have very large ++ * request bodies. ++ */ ++ commSetTimeout(fd, Config.Timeout.read, icapConnectTimeout, icap); ++} ++ ++ ++/* ++ * icapSendReqMod ++ * ++ * Send the ICAP request, including HTTP request, to the ICAP server ++ * after connection has been established. ++ */ ++static void ++icapSendReqMod(int fd, int status, void *data) ++{ ++ MemBuf mb; ++ MemBuf mb_hdr; ++ Packer p; ++ IcapStateData *icap = data; ++ char *client_addr; ++ int icap_fd = icap->icap_fd; ++ icap_service *service; ++ CWCB *theCallback; ++ ++ debug(81, 5) ("icapSendReqMod FD %d, status %d\n", fd, status); ++ icap->flags.connect_pending = 0; ++ ++ if (COMM_OK != status) { ++ debug(81, 1) ("Could not connect to ICAP server %s:%d: %s\n", ++ icap->current_service->hostname, ++ icap->current_service->port, xstrerror()); ++ debug(81, 3) ("icapSendReqMod: unreachable=1, service=%s\n", ++ icap->current_service->uri); ++ icapOptSetUnreachable(icap->current_service); ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_SERVICE_UNAVAILABLE, errno); ++ comm_close(fd); ++ return; ++ } ++ fd_table[fd].pconn.uses++; ++ fd_table[fd].pconn.type = 2; ++ if (icap->request->content_length > 0) ++ theCallback = icapReqModSendBodyChunk; ++ else ++ theCallback = icapSendReqModDone; ++ ++ memBufDefInit(&mb); ++ memBufDefInit(&mb_hdr); ++ memBufPrintf(&mb_hdr, "%s %s HTTP/%d.%d\r\n", ++ RequestMethodStr[icap->request->method], ++ icap->reqmod.uri, ++ icap->request->http_ver.major, icap->request->http_ver.minor); ++ packerToMemInit(&p, &mb_hdr); ++ httpHeaderPackInto(&icap->request->header, &p); ++ packerClean(&p); ++ memBufAppend(&mb_hdr, crlf, 2); ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ memBufPrintf(&mb, "REQMOD %s ICAP/1.0\r\n", service->uri); ++ memBufPrintf(&mb, "Encapsulated: req-hdr=0"); ++ /* TODO: Change the offset using 'request' if needed */ ++ if (icap->request->content_length > 0) ++ memBufPrintf(&mb, ", req-body=%d", mb_hdr.size); ++ else ++ memBufPrintf(&mb, ", null-body=%d", mb_hdr.size); ++ memBufAppend(&mb, crlf, 2); ++ ++ if (service->flags.need_x_client_ip && Config.icapcfg.send_client_ip) ++ memBufPrintf(&mb, "X-Client-IP: %s\r\n", client_addr); ++ ++ if (service->flags.need_x_server_ip && Config.icapcfg.send_server_ip) ++ icapAddOriginIP(&mb, icap->request->host); ++ ++ if ((service->flags.need_x_authenticated_user ++ && Config.icapcfg.send_auth_user) ++ && (icap->request->auth_user_request != NULL)) ++ icapAddAuthUserHeader(&mb, icap->request->auth_user_request); ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(&mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(&mb, crlf, 2); ++ memBufAppend(&mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ debug(81, 5) ("icapSendReqMod: FD %d writing {%s}\n", icap->icap_fd, ++ mb.buf); ++ comm_write_mbuf(icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModStart ++ * ++ * Initiate an ICAP REQMOD transaction. Create and fill in IcapStateData ++ * structure and request a TCP connection to the server. ++ */ ++IcapStateData * ++icapReqModStart(icap_service * service, const char *uri, request_t * request, ++ int fd, struct timeval start, struct in_addr log_addr, void *cookie) ++{ ++ IcapStateData *icap = NULL; ++ ++ debug(81, 3) ("icapReqModStart: type=%d\n", (int) service->type); ++ ++ switch (service->type) { ++ case ICAP_SERVICE_REQMOD_PRECACHE: ++ break; ++ default: ++ fatalf("icapReqModStart: unsupported service type '%s'\n", ++ icap_service_type_str[service->type]); ++ break; ++ } ++ ++ if (service->unreachable) { ++ if (service->bypass) { ++ debug(81, ++ 5) ("icapReqModStart: BYPASS because service unreachable: %s\n", ++ service->uri); ++ return NULL; ++ } else { ++ debug(81, ++ 5) ("icapReqModStart: ERROR because service unreachable: %s\n", ++ service->uri); ++ return (IcapStateData *) - 1; ++ } ++ } ++ icap = icapAllocate(); ++ if (!icap) { ++ debug(81, 3) ("icapReqModStart: icapAllocate() failed\n"); ++ return NULL; ++ } ++ icap->current_service = service; ++ icap->preview_size = service->preview; ++ icap->reqmod.uri = uri; /* XXX should be xstrdup? */ ++ icap->reqmod.start = start; ++ icap->reqmod.log_addr = log_addr; ++ icap->request = requestLink(request); ++ icap->reqmod.hdr_state = 0; ++ icap->reqmod.client_fd = fd; ++ icap->reqmod.client_cookie = cookie; ++ cbdataLock(icap->reqmod.client_cookie); ++ ++ if (!icapConnect(icap, icapSendReqMod)) ++ return NULL; ++ ++ statCounter.icap.all.requests++; ++ debug(81, 3) ("icapReqModStart: returning %p\n", icap); ++ return icap; ++} ++ ++/* ++ * icapReqModSendBodyChunk ++ * ++ * A "comm_write" callback. This is called after comm_write() does ++ * its job to let us know how things went. If there are no errors, ++ * get another chunk of the body from client_side. ++ */ ++static void ++icapReqModSendBodyChunk(int fd, char *bufnotused, size_t size, int errflag, ++ void *data) ++{ ++ IcapStateData *icap = data; ++ debug(81, 3) ("icapReqModSendBodyChunk: FD %d wrote %d errflag %d.\n", ++ fd, (int) size, errflag); ++ if (errflag == COMM_ERR_CLOSING) ++ return; ++ if (errflag) { ++ icapEntryError(icap, ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, ++ errno); ++ comm_close(fd); ++ return; ++ } ++ clientReadBody(icap->request, ++ memAllocate(MEM_8K_BUF), 8192, icapReqModBodyHandler, icap); ++} ++ ++/* ++ * icapReqModBodyHandler ++ * ++ * Called after Squid gets a chunk of the request entity from the ++ * client side. The body is chunkified and passed to comm_write. ++ * The comm_write callback depends on whether or not this is the ++ * last chunk. ++ */ ++static void ++icapReqModBodyHandler(char *buf, ssize_t size, void *data) ++{ ++ IcapStateData *icap = data; ++ MemBuf mb; ++ CWCB *theCallback = icapReqModSendBodyChunk; ++ if (size < 0) { ++ debug(81, 1) ("icapReqModBodyHandler: %s\n", xstrerror()); ++ memFree8K(buf); ++ return; ++ } ++ memBufDefInit(&mb); ++ debug(81, 3) ("icapReqModBodyHandler: writing chunk size %d\n", size); ++ memBufPrintf(&mb, "%x\r\n", size); ++ if (size) ++ memBufAppend(&mb, buf, size); ++ else ++ theCallback = icapSendReqModDone; ++ memBufAppend(&mb, crlf, 2); ++ memFree8K(buf); ++ comm_write_mbuf(icap->icap_fd, mb, theCallback, icap); ++} ++ ++/* ++ * icapReqModReadHttpBody ++ * ++ * The read handler for the client's HTTP connection when reading ++ * message bodies. Called by comm_select(). ++ */ ++static void ++icapReqModReadHttpBody(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ int len; ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d called\n", fd); ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 3) ("icapReqModReadHttpBody: read returns %d\n", len); ++ if (len < 0) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d %s\n", fd, xstrerror()); ++ if (!ignoreErrno(errno)) ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else if (0 == len) { ++ debug(81, 3) ("icapReqModReadHttpBody: FD %d EOF\n", fd); ++ icap->flags.reqmod_http_entity_eof = 1; ++ } else { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ icap->reqmod.http_entity.bytes_read += ++ icapParseChunkedBody(icap, ++ icapReqModMemBufAppend, &icap->reqmod.http_entity.buf); ++ } ++ if (icap->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 25 Jun 2006 10:43:07 -0000 1.1.14.6 +@@ -0,0 +1,1065 @@ ++ ++/* ++ * $Id$ ++ * ++ * DEBUG: section 81 Internet Content Adaptation Protocol (ICAP) Client ++ * AUTHOR: Geetha Manjunath, Hewlett Packard Company ++ * ++ * SQUID Web Proxy Cache http://www.squid-cache.org/ ++ * ---------------------------------------------------------- ++ * ++ * Squid is the result of efforts by numerous individuals from ++ * the Internet community; see the CONTRIBUTORS file for full ++ * details. Many organizations have provided support for Squid's ++ * development; see the SPONSORS file for full details. Squid is ++ * Copyrighted (C) 2001 by the Regents of the University of ++ * California; see the COPYRIGHT file for full details. Squid ++ * incorporates software developed and/or copyrighted by other ++ * sources; see the CREDITS file for full details. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. ++ * ++ */ ++ ++#include "squid.h" ++ ++static CWCB icapSendRespModDone; ++static PF icapRespModGobble; ++extern PF icapReadReply; ++static PF icapRespModReadReply; ++static void icapRespModKeepAliveOrClose(IcapStateData * icap); ++static int icapReadReply2(IcapStateData * icap); ++static void icapReadReply3(IcapStateData * icap); ++ ++#define EXPECTED_ICAP_HEADER_LEN 256 ++const char *crlf = "\r\n"; ++ ++static void ++getICAPRespModString(MemBuf * mb, int o1, int o2, int o3, ++ const char *client_addr, IcapStateData * icap, const icap_service * service) ++{ ++ memBufPrintf(mb, "RESPMOD %s ICAP/1.0\r\nEncapsulated:", service->uri); ++ if (o1 >= 0) ++ memBufPrintf(mb, " req-hdr=%1d", o1); ++ if (o2 >= 0) ++ memBufPrintf(mb, ", res-hdr=%1d", o2); ++ if (o3 >= 0) ++ memBufPrintf(mb, ", res-body=%1d", o3); ++ else ++ memBufPrintf(mb, ", null-body=%1d", -o3); ++ memBufPrintf(mb, crlf); ++ ++ if (service->flags.need_x_client_ip && Config.icapcfg.send_client_ip) { ++ memBufPrintf(mb, "X-Client-IP: %s\r\n", client_addr); ++ } ++ if (service->flags.need_x_server_ip && Config.icapcfg.send_server_ip) ++ icapAddOriginIP(mb, icap->request->host); ++ ++ if ((service->flags.need_x_authenticated_user ++ && Config.icapcfg.send_auth_user) ++ && (icap->request->auth_user_request != NULL)) { ++ icapAddAuthUserHeader(mb, icap->request->auth_user_request); ++ } ++#if NOT_YET_FINISHED ++ if (Config.icapcfg.trailers) { ++ memBufPrintf(mb, "X-TE: trailers\r\n"); ++ } ++#endif ++} ++ ++static int ++buildRespModHeader(MemBuf * mb, IcapStateData * icap, char *buf, ++ ssize_t len, int theEnd) ++{ ++ MemBuf mb_hdr; ++ char *client_addr; ++ int o2 = 0; ++ int o3 = 0; ++ int hlen; ++ int consumed; ++ icap_service *service; ++ HttpReply *r; ++ ++ if (memBufIsNull(&icap->respmod.req_hdr_copy)) ++ memBufDefInit(&icap->respmod.req_hdr_copy); ++ ++ memBufAppend(&icap->respmod.req_hdr_copy, buf, len); ++ ++ if (icap->respmod.req_hdr_copy.size > 4 && strncmp(icap->respmod.req_hdr_copy.buf, "HTTP/", 5)) { ++ debug(81, 3) ("buildRespModHeader: Non-HTTP-compliant header: '%s'\n", buf); ++ /* ++ *Possible we can consider that we did not have http responce headers ++ *(maybe HTTP 0.9 protocol), lets returning -1... ++ */ ++ consumed = -1; ++ o2 = -1; ++ memBufDefInit(&mb_hdr); ++ httpBuildRequestPrefix(icap->request, icap->request, ++ icap->respmod.entry, &mb_hdr, icap->http_flags); ++ o3 = mb_hdr.size; ++ } else { ++ ++ hlen = headersEnd(icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ debug(81, 3) ("buildRespModHeader: headersEnd = %d(%s)\n", hlen, buf); ++ if (0 == hlen) ++ return 0; ++ ++ /* ++ * calc how many bytes from this 'buf' went towards the ++ * reply header. ++ */ ++ consumed = hlen - (icap->respmod.req_hdr_copy.size - len); ++ debug(81, 3) ("buildRespModHeader: consumed = %d\n", consumed); ++ ++ ++ /* ++ * now, truncate our req_hdr_copy at the header end. ++ * this 'if' statement might be unncessary? ++ */ ++ if (hlen < icap->respmod.req_hdr_copy.size) ++ icap->respmod.req_hdr_copy.size = hlen; ++ ++ /* Copy request header */ ++ memBufDefInit(&mb_hdr); ++ httpBuildRequestPrefix(icap->request, icap->request, ++ icap->respmod.entry, &mb_hdr, icap->http_flags); ++ o2 = mb_hdr.size; ++ ++ /* Copy response header - Append to request header mbuffer */ ++ memBufAppend(&mb_hdr, ++ icap->respmod.req_hdr_copy.buf, icap->respmod.req_hdr_copy.size); ++ o3 = mb_hdr.size; ++ } ++ ++ service = icap->current_service; ++ assert(service); ++ client_addr = inet_ntoa(icap->request->client_addr); ++ ++ r = httpReplyCreate(); ++ httpReplyParse(r, icap->respmod.req_hdr_copy.buf, ++ icap->respmod.req_hdr_copy.size); ++ icap->respmod.res_body_sz = httpReplyBodySize(icap->request->method, r); ++ httpReplyDestroy(r); ++ if (icap->respmod.res_body_sz) ++ getICAPRespModString(mb, 0, o2, o3, client_addr, icap, service); ++ else ++ getICAPRespModString(mb, 0, o2, -o3, client_addr, icap, service); ++ if (Config.icapcfg.preview_enable) ++ if (icap->preview_size >= 0) { ++ memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size); ++ icap->flags.preview_done = 0; ++ } ++ if (service->keep_alive) { ++ icap->flags.keep_alive = 1; ++ memBufAppend(mb, "Connection: keep-alive\r\n", 24); ++ } else { ++ icap->flags.keep_alive = 0; ++ memBufAppend(mb, "Connection: close\r\n", 19); ++ } ++ memBufAppend(mb, crlf, 2); ++ memBufAppend(mb, mb_hdr.buf, mb_hdr.size); ++ memBufClean(&mb_hdr); ++ ++ ++ return consumed; ++} ++ ++ ++void ++icapSendRespMod(IcapStateData * icap, char *buf, int len, int theEnd) ++{ ++ MemBuf mb; ++#if ICAP_PREVIEW ++ int size; ++ const int preview_size = icap->preview_size; ++#endif ++ debug(81, 5) ("icapSendRespMod: FD %d, len %d, theEnd %d\n", ++ icap->icap_fd, len, theEnd); ++ ++ if (icap->flags.no_content) { ++ /* ++ * ICAP server said there are no modifications to make, so ++ * just append this data to the StoreEntry ++ */ ++ if (icap->respmod.resp_copy.size) { ++ /* ++ * first copy the data that we already sent to the ICAP server ++ */ ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ } ++ debug(81, 5) ("icapSendRepMod: len=%d theEnd=%d write_pending=%d\n", ++ len, theEnd, icap->flags.write_pending); ++ if (len) { ++ /* ++ * also copy any new data from the HTTP side ++ */ ++ memBufAppend(&icap->chunk_buf, buf, len); ++ } ++ (void) icapReadReply2(icap); ++ return; ++ } ++ if (theEnd) { ++ if (icap->respmod.res_body_sz) ++ icap->flags.send_zero_chunk = 1; ++ icap->flags.http_server_eof = 1; ++ } ++ /* ++ * httpReadReply is going to call us with a chunk and then ++ * right away again with an EOF if httpPconnTransferDone() is true. ++ * Since the first write is already dispatched, we'll have to ++ * hack this in somehow. ++ */ ++ if (icap->flags.write_pending) { ++ debug(81, 3) ("icapSendRespMod: oops, write_pending=1\n"); ++ assert(theEnd); ++ assert(len == 0); ++ return; ++ } ++ if (!cbdataValid(icap)) { ++ debug(81, 3) ("icapSendRespMod: failed to establish connection?\n"); ++ return; ++ } ++ memBufDefInit(&mb); ++ ++#if SUPPORT_ICAP_204 || ICAP_PREVIEW ++ /* ++ * make a copy of the response in case ICAP server gives us a 204 ++ */ ++ /* ++ * This piece of code is problematic for 204 responces outside preview. ++ * The icap->respmod.resp_copy continues to filled until we had responce ++ * If the icap server waits to gets all data before sends its responce ++ * then we are puting all downloading object to the main system memory. ++ * My opinion is that 204 responces outside preview must be disabled ..... ++ * /chtsanti ++ */ ++ ++ if (len && icap->flags.copy_response) { ++ if (memBufIsNull(&icap->respmod.resp_copy)) ++ memBufDefInit(&icap->respmod.resp_copy); ++ memBufAppend(&icap->respmod.resp_copy, buf, len); ++ } ++#endif ++ ++ if (icap->sc == 0) { ++ // http connection has been closed without sending us anything ++ if (len == 0 && theEnd == 1) { ++ ErrorState *err; ++ err = errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY); ++ 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"); ++ icapRespModKeepAliveOrClose(icap); ++ ++ debug(81, 3) ("setting icap->flags.no_content\n"); ++ icap->flags.no_content = 1; ++ /* ++ * copy the response already written to the ICAP server ++ */ ++ debug(81, 3) ("copying %d bytes from resp_copy to chunk_buf\n", ++ icap->respmod.resp_copy.size); ++ memBufAppend(&icap->chunk_buf, ++ icap->respmod.resp_copy.buf, icap->respmod.resp_copy.size); ++ icap->respmod.resp_copy.size = 0; ++ if (icapReadReply2(icap) < 0) ++ icapStateFree(-1, icap); ++ ++ /* ++ * XXX ideally want to clean icap->respmod.resp_copy here ++ * XXX ideally want to "close" ICAP server connection here ++ * OK do it.... ++ */ ++ if (!memBufIsNull(&icap->respmod.resp_copy)) ++ memBufClean(&icap->respmod.resp_copy); ++ return; ++ } ++#endif ++ if (200 != status && 201 != status) { ++ debug(81, 1) ("Unsupported status '%d' from ICAP server\n", status); ++ /* Did not find a proper ICAP response */ ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ 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; ++ debug(81, 3) ("%s:%d FD %d looks good, keeping alive\n", __FILE__, __LINE__, ++ fd); ++ commSetDefer(fd, NULL, NULL); ++ commSetTimeout(fd, -1, NULL, NULL); ++ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); ++ comm_remove_close_handler(fd, icapStateFree, icap); ++ icap->icap_fd = -1; ++ if (!icap->flags.keep_alive) { ++ debug(81, 3) ("%s:%d keep_alive not set, closing\n", __FILE__, ++ __LINE__); ++ comm_close(fd); ++ return; ++ } else { ++ pconnPush(fd, icap->current_service->hostname, icap->current_service->port, NULL, NULL, 0); ++ } ++} ++ ++ ++ ++/* ++ * copied from httpPconnTransferDone ++ * ++ */ ++static int ++icapPconnTransferDone(int fd, IcapStateData * icap) ++{ ++ debug(81, 3) ("icapPconnTransferDone: FD %d\n", fd); ++ /* ++ * Be careful with 204 responses. Normally we are done when we ++ * see the zero-end chunk, but that won't happen for 204s, so we ++ * use an EOF indicator on the HTTP side instead. ++ */ ++ if (icap->flags.no_content && icap->flags.http_server_eof) { ++ debug(81, 5) ("icapPconnTransferDone: no content, ret 1\n"); ++ return 1; ++ } ++ if (icapHttpReplyHdrState(icap) != 2) { ++ debug(81, ++ 5) ("icapPconnTransferDone: didn't see end of HTTP hdrs, ret 0\n"); ++ return 0; ++ } ++ if (icap->enc.null_body > -1) { ++ debug(81, 5) ("icapPconnTransferDone: no message body, ret 1\n"); ++ return 1; ++ } ++ if (icap->chunk_size == -2) { //AI: was != -2 ; and change content with bottom ++ /* zero end chunk reached */ ++ debug(81, 5) ("icapPconnTransferDone: got zero end chunk\n"); ++ return 1; ++ } ++ debug(81, 5) ("icapPconnTransferDone: didnt get zero end chunk yet\n"); //AI: change with second top condition ++ ++ return 0; ++} ++ ++static int ++icapExpectedHttpReplyHdrSize(IcapStateData * icap) ++{ ++ if (icap->enc.res_body > -1 && icap->enc.res_hdr > -1) ++ return (icap->enc.res_body - icap->enc.res_hdr); ++ if (icap->enc.null_body > -1 && icap->enc.res_hdr > -1) ++ return icap->enc.null_body - icap->enc.res_hdr; ++ /*The case we did not get res_hdr ..... */ ++ if (icap->enc.res_body > -1) ++ return icap->enc.res_body; ++ if (icap->enc.null_body > -1) ++ return icap->enc.null_body; ++ return -1; ++} ++ ++/* ++ * copied from httpReadReply() ++ * ++ * by the time this is called, the ICAP headers have already ++ * been read. ++ */ ++void ++icapReadReply(int fd, void *data) ++{ ++ IcapStateData *icap = data; ++ StoreEntry *entry = icap->respmod.entry; ++ const request_t *request = icap->request; ++ int len; ++ debug(81, 5) ("icapReadReply: FD %d: icap %p.\n", fd, data); ++ if (icap->flags.no_content && !icap->flags.http_server_eof) { //AI ++ ++ return; ++ } ++ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { ++ comm_close(fd); ++ return; ++ } ++ errno = 0; ++ statCounter.syscalls.sock.reads++; ++ len = memBufRead(fd, &icap->chunk_buf); ++ debug(81, 5) ("icapReadReply: FD %d: len %d.\n", fd, len); ++ if (len > 0) { ++ fd_bytes(fd, len, FD_READ); ++ kb_incr(&statCounter.icap.all.kbytes_in, len); ++ commSetTimeout(fd, Config.Timeout.read, icapReadTimeout, icap); ++ if (icap->chunk_buf.size < icap->chunk_buf.capacity) { ++ *(icap->chunk_buf.buf + icap->chunk_buf.size) = '\0'; ++ debug(81, 9) ("{%s}\n", icap->chunk_buf.buf); ++ } ++ } ++ if (len <= 0) { ++ debug(81, 2) ("icapReadReply: FD %d: read failure: %s.\n", ++ fd, xstrerror()); ++ if (ignoreErrno(errno)) { ++ debug(81, 2) ("icapReadReply: FD %d: ignored errno\n", fd); ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ } else if (entry->mem_obj->inmem_hi == 0) { ++ ErrorState *err; ++ debug(81, 2) ("icapReadReply: FD %d: generating error page\n", fd); ++ err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); ++ 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"); ++ if (icap->flags.no_content) ++ icapStateFree(-1, icap); ++ else ++ comm_close(fd); ++ } else if (icapPconnTransferDone(fd, icap)) { ++ storeComplete(entry); ++ if (icap->flags.no_content) ++ icapStateFree(-1, icap); ++ else { ++ icapRespModKeepAliveOrClose(icap); ++ icapStateFree(-1, icap); ++ } ++ } else if (!icap->flags.no_content) { ++ /* Wait for EOF condition */ ++ commSetSelect(fd, COMM_SELECT_READ, icapReadReply, icap, 0); ++ debug(81, ++ 3) ++ ("icapReadReply3: Going to read mode data throught icapReadReply\n"); ++ } else { ++ debug(81, 3) ("icapReadReply3: Nothing\n"); ++ } ++} +Index: src/main.c +=================================================================== +RCS file: /cvsroot/squid/squid/src/main.c,v +retrieving revision 1.67 +retrieving revision 1.45.4.7 +diff -p -u -b -r1.67 -r1.45.4.7 +--- src/main.c 30 Jul 2006 23:52:54 -0000 1.67 ++++ src/main.c 3 Aug 2006 17:16:04 -0000 1.45.4.7 +@@ -391,6 +391,9 @@ mainReconfigure(void) + #else + idnsShutdown(); + #endif ++#ifdef HS_FEAT_ICAP ++ icapClose(); ++#endif + redirectShutdown(); + locationRewriteShutdown(); + authenticateShutdown(); +@@ -422,6 +425,9 @@ mainReconfigure(void) + #endif + redirectInit(); + locationRewriteInit(); ++#ifdef HS_FEAT_ICAP ++ icapInit(); ++#endif + authenticateInit(&Config.authConfig); + externalAclInit(); + #if USE_WCCP +@@ -565,6 +571,9 @@ mainInitialize(void) + redirectInit(); + locationRewriteInit(); + errorMapInit(); ++#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.27 +retrieving revision 1.24.4.3 +diff -p -u -b -r1.27 -r1.24.4.3 +--- src/mem.c 20 May 2006 22:50:55 -0000 1.27 ++++ src/mem.c 26 May 2006 18:21:32 -0000 1.24.4.3 +@@ -353,6 +353,13 @@ memInit(void) + memDataInit(MEM_TLV, "storeSwapTLV", sizeof(tlv), 0); + memDataInit(MEM_SWAP_LOG_DATA, "storeSwapLogData", sizeof(storeSwapLogData), 0); + ++#ifdef HS_FEAT_ICAP ++ memDataInit(MEM_ICAP_OPT_DATA, "IcapOptData", sizeof(IcapOptData), 0); ++ memDataInit(MEM_ICAP_SERVICE_LIST, "icap_service_list", sizeof(icap_service_list), 0); ++ memDataInit(MEM_ICAP_CLASS, "icap_class", sizeof(icap_class), 0); ++ memDataInit(MEM_ICAP_ACCESS, "icap_access", sizeof(icap_access), 0); ++#endif ++ + /* init string pools */ + for (i = 0; i < mem_str_pool_count; i++) { + StrPools[i].pool = memPoolCreate(StrPoolsAttrs[i].name, StrPoolsAttrs[i].obj_size); +Index: src/mk-string-arrays.pl +=================================================================== +RCS file: /cvsroot/squid/squid/src/mk-string-arrays.pl,v +retrieving revision 1.2 +retrieving revision 1.2.180.1 +diff -p -u -b -r1.2 -r1.2.180.1 +--- src/mk-string-arrays.pl 23 Oct 2000 15:04:21 -0000 1.2 ++++ src/mk-string-arrays.pl 17 May 2006 17:58:01 -0000 1.2.180.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.10 +retrieving revision 1.9.4.2 +diff -p -u -b -r1.10 -r1.9.4.2 +--- src/pconn.c 22 May 2006 22:06:12 -0000 1.10 ++++ src/pconn.c 26 May 2006 18:21:32 -0000 1.9.4.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; +@@ -169,6 +172,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 ============================================ */ +@@ -183,6 +200,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)); +@@ -265,11 +285,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.118 +retrieving revision 1.74.4.8 +diff -p -u -b -r1.118 -r1.74.4.8 +--- src/protos.h 30 Jul 2006 23:52:54 -0000 1.118 ++++ src/protos.h 3 Aug 2006 17:16:05 -0000 1.74.4.8 +@@ -295,6 +295,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, +@@ -617,6 +619,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); +@@ -1409,4 +1412,53 @@ void storeLocateVaryDone(VaryData * data + void storeLocateVary(StoreEntry * e, int offset, const char *vary_data, String accept_encoding, STLVCB * callback, void *cbdata); + void storeAddVary(const char *url, const char *log_url, const method_t method, const cache_key * key, const char *etag, const char *vary, const char *vary_headers, const char *accept_encoding); + ++#ifdef HS_FEAT_ICAP ++/* ++ * icap_common.c ++ */ ++void icapInit(void); ++void icapClose(void); ++void icapParseEncapsulated(IcapStateData *, const char *, const char *); ++icap_service *icapService(icap_service_t, request_t *); ++int icapConnect(IcapStateData *, CNCB *); ++IcapStateData *icapAllocate(void); ++PF icapStateFree; ++PF icapConnectTimeout; ++PF icapReadTimeout; ++icap_service_t icapServiceToType(const char *); ++const char *icapServiceToStr(const icap_service_t); ++int icapCheckAcl(clientHttpRequest *); ++size_t icapLineLength(const char *, int); ++int icapReadHeader(int, IcapStateData *, int *); ++int icapFindHeader(const char *, const char *, const char **, const char **); ++int icapParseKeepAlive(const IcapStateData *, const char *, const char *); ++void icapSetKeepAlive(IcapStateData * icap, const char *hdrs); ++size_t icapParseChunkedBody(IcapStateData *, STRCB *, void *); ++void icapAddAuthUserHeader(MemBuf *, auth_user_request_t *); ++int icapParseStatusLine(const char *, int, int *, int *, const char **); ++ ++/* ++ * icap_respmod.c ++ */ ++IcapStateData *icapRespModStart(icap_service_t, request_t *, StoreEntry *, http_state_flags); ++void icapSendRespMod(IcapStateData *, char *, int, int); ++CNCB icapConnectOver; ++ ++/* ++ * icap_reqmod.c ++ */ ++IcapStateData *icapReqModStart(icap_service*, const char *, request_t *, int, struct timeval, struct in_addr, void *); ++ ++/* icap_opt.c */ ++void icapOptInit(void); ++void icapOptShutdown(void); ++void icapOptSetUnreachable(icap_service * s); ++ ++/* X-Server-IP support */ ++void icapAddOriginIP(MemBuf *, const char *); ++ ++/* for debugging purposes only */ ++void dump_icap_config(IcapConfig * cfg); ++#endif ++ + #endif /* SQUID_PROTOS_H */ +Index: src/squid.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/squid.h,v +retrieving revision 1.34 +retrieving revision 1.24.8.5 +diff -p -u -b -r1.34 -r1.24.8.5 +--- src/squid.h 8 Jun 2006 13:51:37 -0000 1.34 ++++ src/squid.h 12 Jun 2006 17:56:59 -0000 1.24.8.5 +@@ -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.34 +retrieving revision 1.26.8.7 +diff -p -u -b -r1.34 -r1.26.8.7 +--- src/stat.c 29 Jul 2006 17:50:57 -0000 1.34 ++++ src/stat.c 3 Aug 2006 17:16:06 -0000 1.26.8.7 +@@ -787,6 +787,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", +@@ -1171,6 +1182,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", +@@ -1471,8 +1493,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.35 +retrieving revision 1.21.10.7 +diff -p -u -b -r1.35 -r1.21.10.7 +--- src/store.c 17 Jul 2006 02:51:31 -0000 1.35 ++++ src/store.c 22 Jul 2006 14:12:02 -0000 1.21.10.7 +@@ -1088,8 +1088,17 @@ storeAppend(StoreEntry * e, const char * + MemObject *mem = e->mem_obj; + assert(mem != NULL); + assert(len >= 0); +- assert(e->store_status == STORE_PENDING); + mem->refresh_timestamp = squid_curtime; ++ debug(20, 3) ("storeAppend: '%s'\n", storeKeyText(e->hash.key)); ++ if (e->store_status != STORE_PENDING) { ++ /* ++ * if we're not STORE_PENDING, then probably we got aborted ++ * and there should be NO clients on this entry ++ */ ++ assert(EBIT_TEST(e->flags, ENTRY_ABORTED)); ++ assert(e->mem_obj->nclients == 0); ++ return; ++ } + if (len) { + debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n", + len, +Index: src/structs.h +=================================================================== +RCS file: /cvsroot/squid/squid/src/structs.h,v +retrieving revision 1.126 +retrieving revision 1.81.4.8 +diff -p -u -b -r1.126 -r1.81.4.8 +--- src/structs.h 30 Jul 2006 23:52:55 -0000 1.126 ++++ src/structs.h 3 Aug 2006 17:16:06 -0000 1.81.4.8 +@@ -423,6 +423,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; +@@ -808,6 +825,9 @@ struct _SquidConfig { + #endif + time_t refresh_stale_window; + int umask; ++#ifdef HS_FEAT_ICAP ++ IcapConfig icapcfg; ++#endif + }; + + struct _SquidConfig2 { +@@ -879,6 +899,10 @@ struct _fde { + comm_pending write_pending; + squid_off_t bytes_read; + squid_off_t bytes_written; ++ struct { ++ int uses; ++ int type; ++ } pconn; + int uses; /* ie # req's over persistent conn */ + struct _fde_disk { + DWCB *wrt_handle; +@@ -1075,6 +1099,131 @@ struct _http_state_flags { + unsigned int originpeer:1; + }; + ++#ifdef HS_FEAT_ICAP ++struct _IcapStateData { ++ request_t *request; ++ http_state_flags http_flags; ++ HttpStateData *httpState; /* needed to parse HTTP headers only */ ++ int icap_fd; ++ int sc; ++ icap_service *current_service; ++ MemBuf icap_hdr; ++ struct { ++ int res_hdr; ++ int res_body; ++ int req_hdr; ++ int req_body; ++ int opt_body; ++ int null_body; ++ } enc; ++ int bytes_to_gobble; ++ int chunk_size; ++ MemBuf chunk_buf; ++ int preview_size; ++ squid_off_t fake_content_length; ++ int http_header_bytes_read_so_far; ++ struct { ++ const char *uri; /* URI for REQMODs */ ++ int client_fd; ++ struct timeval start; /* for logging */ ++ struct in_addr log_addr; /* for logging */ ++ int hdr_state; ++ MemBuf hdr_buf; ++ void *client_cookie; ++ struct { ++ MemBuf buf; ++ CBCB *callback; ++ void *callback_data; ++ char *callback_buf; ++ size_t callback_bufsize; ++ squid_off_t bytes_read; ++ } http_entity; ++ } reqmod; ++ struct { ++ StoreEntry *entry; ++ MemBuf buffer; ++ MemBuf req_hdr_copy; /* XXX barf */ ++ MemBuf resp_copy; /* XXX barf^max */ ++ squid_off_t res_body_sz; ++ } respmod; ++ struct { ++ unsigned int connect_requested:1; ++ unsigned int connect_pending:1; ++ unsigned int write_pending:1; ++ unsigned int keep_alive:1; ++ unsigned int http_server_eof:1; ++ unsigned int send_zero_chunk:1; ++ unsigned int got_reply:1; ++ unsigned int wait_for_reply:1; ++ unsigned int wait_for_preview_reply:1; ++ unsigned int preview_done:1; ++ unsigned int copy_response:1; ++ unsigned int no_content:1; ++ unsigned int reqmod_http_entity_eof:1; ++ } flags; ++}; ++ ++struct _icap_service { ++ icap_service *next; ++ char *name; /* name to be used when referencing ths service */ ++ char *uri; /* uri of server/service to use */ ++ char *type_name; /* {req|resp}mod_{pre|post}cache */ ++ ++ char *hostname; ++ unsigned short int port; ++ char *resource; ++ icap_service_t type; /* parsed type */ ++ icap_method_t method; ++ ushort bypass; /* flag: bypass allowed */ ++ ushort unreachable; /* flag: set to 1 if options request fails */ ++ IcapOptData *opt; /* temp data needed during opt request */ ++ struct { ++ unsigned int allow_204:1; ++ unsigned int need_x_client_ip:1; ++ unsigned int need_x_server_ip:1; ++ unsigned int need_x_authenticated_user:1; ++ } flags; ++ int preview; ++ String istag; ++ String transfer_preview; ++ String transfer_ignore; ++ String transfer_complete; ++ int max_connections; ++ int options_ttl; ++ int keep_alive; ++}; ++ ++struct _icap_service_list { ++ icap_service_list *next; ++ icap_service *services[16]; ++ int nservices; /* Number of services already used */ ++ int last_service_used; /* Last services used, use to do a round robin */ ++}; ++ ++struct _icap_class { ++ icap_class *next; ++ char *name; ++ wordlist *services; ++ icap_service_list *isl; ++ ushort hidden; /* for unnamed classes */ ++}; ++ ++struct _icap_access { ++ icap_access *next; ++ char *service_name; ++ icap_class *class; ++ acl_access *access; ++}; ++ ++struct _IcapOptData { ++ char *buf; ++ off_t offset; ++ size_t size; ++ off_t headlen; ++}; ++ ++#endif ++ + struct _HttpStateData { + StoreEntry *entry; + request_t *request; +@@ -1086,10 +1235,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; +@@ -1198,6 +1351,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 { +@@ -1206,6 +1360,9 @@ struct _clientHttpRequest { + } redirect; + dlink_node active; + squid_off_t maxBodySize; ++#if HS_FEAT_ICAP ++ IcapStateData *icap_reqmod; ++#endif + }; + + struct _ConnStateData { +@@ -1873,6 +2030,9 @@ struct _request_t { + unsigned int done_etag:1; /* We have done clientProcessETag on this, don't attempt it again */ + char *urlgroup; /* urlgroup, returned by redirectors */ + char *peer_domain; /* Configured peer forceddomain */ ++#if HS_FEAT_ICAP ++ icap_class *class; ++#endif + BODY_HANDLER *body_reader; + void *body_reader_data; + String extacl_log; /* String to be used for access.log purposes */ +@@ -1989,7 +2149,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.39 +retrieving revision 1.32.4.6 +diff -p -u -b -r1.39 -r1.32.4.6 +--- src/typedefs.h 30 Jul 2006 23:52:55 -0000 1.39 ++++ src/typedefs.h 3 Aug 2006 17:16:07 -0000 1.32.4.6 +@@ -136,6 +136,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.17 +retrieving revision 1.14.10.4 +diff -p -u -b -r1.17 -r1.14.10.4 +--- src/url.c 17 Jun 2006 23:51:19 -0000 1.17 ++++ src/url.c 28 Jun 2006 21:12:01 -0000 1.14.10.4 +@@ -103,6 +103,9 @@ const char *ProtocolStr[] = + "whois", + "internal", + "https", ++#ifdef HS_FEAT_ICAP ++ "icap", ++#endif + "TOTAL" + }; + +@@ -217,6 +220,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; + } + +@@ -240,6 +247,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; + } Index: files/icap-2.6-bootstrap.patch =================================================================== --- files/icap-2.6-bootstrap.patch (.../www/squid26) (revision 0) +++ files/icap-2.6-bootstrap.patch (.../local/squid26) (revision 984) @@ -0,0 +1,473 @@ +Patch 2 of 2 to integrate the icap-2_6 branch into the FreeBSD squid port. + +Created by Thomas-Martin Seck . + +This patch simulates the autotools bootstrap necessary after applying the +ICAP patchset. + +Please see icap-2.6-core.patch for further information. + +Patch last updated: 2006-08-05 + +--- configure.orig Sat Aug 5 16:24:35 2006 ++++ configure Sat Aug 5 16:19:46 2006 +@@ -270,8 +270,8 @@ + # Identity of this package. + PACKAGE_NAME='Squid Web Proxy' + PACKAGE_TARNAME='squid' +-PACKAGE_VERSION='2.6.STABLE2' +-PACKAGE_STRING='Squid Web Proxy 2.6.STABLE2' ++PACKAGE_VERSION='2.6.STABLE2-CVS' ++PACKAGE_STRING='Squid Web Proxy 2.6.STABLE2-CVS' + PACKAGE_BUGREPORT='http://www.squid-cache.org/bugs/' + + ac_default_prefix=/usr/local/squid +@@ -312,7 +312,7 @@ + # include + #endif" + +-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE build build_cpu build_vendor build_os host host_cpu host_vendor host_os PKGCONFIG CGIEXT ENABLE_WIN32SPECIFIC_TRUE ENABLE_WIN32SPECIFIC_FALSE CACHE_HTTP_PORT CACHE_ICP_PORT LIBDLMALLOC LIB_MALLOC STORE _OBJS STORE_LIBS STORE_MODULES NEED_DISKD_TRUE NEED_DISKD_FALSE REPL_POLICIES REPL_OBJS REPL_LIBS ENABLE_PINGER_TRUE ENABLE_PINGER_FALSE USE_DELAY_POOLS_TRUE USE_DELAY_POOLS_FALSE USE_SNMP_TRUE USE_SNMP_FALSE SNMPLIB makesnmplib ENABLE_HTCP_TRUE ENABLE_HTCP_FALSE ENABLE_SSL_TRUE ENABLE_SSL_FALSE NEED_OWN_MD5_TRUE NEED_OWN_MD5_FALSE SSLLIB ERR_DEFAULT_LANGUAGE ERR_LANGUAGES MAKE_LEAKFINDER_TRUE MAKE_LEAKFINDER_FALSE USE_DNSSERVER_TRUE USE_DNSSERVER_FALSE OPT_DEFAULT_HOSTS AUTH_MODULES AUTH_OBJS AUTH_LIBS BASIC_AUTH_HELPERS NTLM_AUTH_HELPERS DIGEST_AUTH_HELPERS NEGOTIATE_AUTH_HELPERS EXTERNAL_ACL_HELPERS CPP EGREP LIBSASL ENABLE_UNLINKD_TRUE ENABLE_UNLINKD_FALSE RANLIB ac_ct_RANLIB LN_S SH FALSE TRUE RM MV MKDIR LN PERL AR AR_R ALLOCA CRYPTLIB LIB_EPOLL LIB_LDAP LIB_LBER LIB_DB EPOLL_LIBS USE_POLL_TRUE USE_POLL_FALSE USE_EPOLL_TRUE USE_EPOLL_FALSE USE_SELECT_TRUE USE_SELECT_FALSE USE_SELECT_WIN32_TRUE USE_SELECT_WIN32_FALSE USE_KQUEUE_TRUE USE_KQUEUE_FALSE NEED_OWN_SNPRINTF_TR UE NEED_OWN_SNPRINTF_FALSE NEED_OWN_STRSEP_TRUE NEED_OWN_STR! SEP_FALS E REGEXLIB LIBREGEX LIBOBJS XTRA_OBJS XTRA_LIBS LTLIBOBJS' ++ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE build build_cpu build_vendor build_os host host_cpu host_vendor host_os PKGCONFIG CGIEXT ENABLE_WIN32SPECIFIC_TRUE ENABLE_WIN32SPECIFIC_FALSE CACHE_HTTP_PORT CACHE_ICP_PORT LIBDLMALLOC LIB_MALLOC STORE _OBJS STORE_LIBS STORE_MODULES NEED_DISKD_TRUE NEED_DISKD_FALSE REPL_POLICIES REPL_OBJS REPL_LIBS ENABLE_PINGER_TRUE ENABLE_PINGER_FALSE USE_DELAY_POOLS_TRUE USE_DELAY_POOLS_FALSE USE_ICAP_TRUE USE_ICAP_FALSE USE_SNMP_TRUE USE_SNMP_FALSE SNMPLIB makesnmplib ENABLE_HTCP_TRUE ENABLE_HTCP_FALSE ENABLE_SSL_TRUE ENABLE_SSL_FALSE NEED_OWN_MD5_TRUE NEED_OWN_MD5_FALSE SSLLIB ERR_DEFAULT_LANGUAGE ERR_LANGUAGES MAKE_LEAKFINDER_TRUE MAKE_LEAKFINDER_FALSE USE_DNSSERVER_TRUE USE_DNSSERVER_FALSE OPT_DEFAULT_HOSTS AUTH_MODULES AUTH_OBJS AUTH_LIBS BASIC_AUTH_HELPERS NTLM_AUTH_HELPERS DIGEST_AUTH_HELPERS NEGOTIATE_AUTH_HELPERS EXTERNAL_ACL_HELPERS CPP EGREP LIBSASL ENABLE_UNLINKD_TRUE ENABLE_UNLINKD_FALSE RANLIB ac_ct_RANLIB LN_S SH FALSE TRUE RM MV MKDIR LN PERL AR AR_R ALLOCA CRYPTLIB LIB_EPOLL LIB_LDAP LIB_LBER LIB_DB EPOLL_LIBS USE_POLL_TRUE USE_POLL_FALSE USE_EPOLL_TRUE USE_EPOLL_FALSE USE_SELECT_TRUE USE_SELECT_FALSE USE_SELECT_WIN32_TRUE USE_SELECT_WIN32_FALSE USE_KQUEUE_TRUE USE_KQUE UE_FALSE NEED_OWN_SNPRINTF_TRUE NEED_OWN_SNPRINTF_FALSE NEED! _OWN_STR NSTR_TRUE NEED_OWN_STRNSTR_FALSE NEED_OWN_STRCASESTR_TRUE NEED_OWN_STRCASESTR_FALSE NEED_OWN_STRSEP_TRUE NEED_OWN_STRSEP_FALSE REGEXLIB LIBREGEX LIBOBJS XTRA_OBJS XTRA_LIBS LTLIBOBJS' + ac_subst_files='' + + # Initialize some variables set by options. +@@ -890,6 +890,7 @@ + to build your custom policy + --enable-icmp Enable ICMP pinging + --enable-delay-pools Enable delay pools to limit bandwidth usage ++ --enable-icap-support Enable ICAP client capability + --enable-useragent-log Enable logging of User-Agent header + --enable-referer-log Enable logging of Referer header + --disable-wccp Disable Web Cache Coordination V1 Protocol +@@ -3843,6 +3844,40 @@ + fi; + + ++ ++if false; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++ ++# Check whether --enable-icap-support or --disable-icap-support was given. ++if test "${enable_icap_support+set}" = set; then ++ enableval="$enable_icap_support" ++ if test "$enableval" = "yes" ; then ++ echo "ICAP support enabled" ++ ++cat >>confdefs.h <<\_ACEOF ++#define HS_FEAT_ICAP 1 ++_ACEOF ++ ++ ++ ++if true; then ++ USE_ICAP_TRUE= ++ USE_ICAP_FALSE='#' ++else ++ USE_ICAP_TRUE='#' ++ USE_ICAP_FALSE= ++fi ++ ++ fi ++ ++fi; ++ ++ + # Check whether --enable-useragent-log or --disable-useragent-log was given. + if test "${enable_useragent_log+set}" = set; then + enableval="$enable_useragent_log" +@@ -15688,6 +15725,8 @@ + srand48 \ + srandom \ + statfs \ ++ strnstr \ ++ strcasestr \ + strsep \ + strtoll \ + sysconf \ +@@ -16247,6 +16286,52 @@ + + + if false; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++ ++if test "$ac_cv_func_strnstr" = "no" || test "$ac_cv_func_vstrnstr" = "no" ; then ++ ++ ++if true; then ++ NEED_OWN_STRNSTR_TRUE= ++ NEED_OWN_STRNSTR_FALSE='#' ++else ++ NEED_OWN_STRNSTR_TRUE='#' ++ NEED_OWN_STRNSTR_FALSE= ++fi ++ ++fi ++ ++ ++ ++if false; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++ ++if test "$ac_cv_func_strcasestr" = "no" || test "$ac_cv_func_vstrcasestr" = "no"; then ++ ++ ++if true; then ++ NEED_OWN_STRCASESTR_TRUE= ++ NEED_OWN_STRCASESTR_FALSE='#' ++else ++ NEED_OWN_STRCASESTR_TRUE='#' ++ NEED_OWN_STRCASESTR_FALSE= ++fi ++ ++fi ++ ++ ++ ++if false; then + NEED_OWN_STRSEP_TRUE= + NEED_OWN_STRSEP_FALSE='#' + else +@@ -17731,6 +17816,20 @@ + Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } + fi ++if test -z "${USE_ICAP_TRUE}" && test -z "${USE_ICAP_FALSE}"; then ++ { { echo "$as_me:$LINENO: error: conditional \"USE_ICAP\" was never defined. ++Usually this means the macro was only invoked conditionally." >&5 ++echo "$as_me: error: conditional \"USE_ICAP\" was never defined. ++Usually this means the macro was only invoked conditionally." >&2;} ++ { (exit 1); exit 1; }; } ++fi ++if test -z "${USE_ICAP_TRUE}" && test -z "${USE_ICAP_FALSE}"; then ++ { { echo "$as_me:$LINENO: error: conditional \"USE_ICAP\" was never defined. ++Usually this means the macro was only invoked conditionally." >&5 ++echo "$as_me: error: conditional \"USE_ICAP\" was never defined. ++Usually this means the macro was only invoked conditionally." >&2;} ++ { (exit 1); exit 1; }; } ++fi + if test -z "${USE_SNMP_TRUE}" && test -z "${USE_SNMP_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"USE_SNMP\" was never defined. + Usually this means the macro was only invoked conditionally." >&5 +@@ -17878,6 +17977,34 @@ + Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } + fi ++if test -z "${NEED_OWN_STRNSTR_TRUE}" && test -z "${NEED_OWN_STRNSTR_FALSE}"; then ++ { { echo "$as_me:$LINENO: error: conditional \"NEED_OWN_STRNSTR\" was never defined. ++Usually this means the macro was only invoked conditionally." >&5 ++echo "$as_me: error: conditional \"NEED_OWN_STRNSTR\" was never defined. ++Usually this means the macro was only invoked conditionally." >&2;} ++ { (exit 1); exit 1; }; } ++fi ++if test -z "${NEED_OWN_STRNSTR_TRUE}" && test -z "${NEED_OWN_STRNSTR_FALSE}"; then ++ { { echo "$as_me:$LINENO: error: conditional \"NEED_OWN_STRNSTR\" was never defined. ++Usually this means the macro was only invoked conditionally." >&5 ++echo "$as_me: error: conditional \"NEED_OWN_STRNSTR\" was never defined. ++Usually this means the macro was only invoked conditionally." >&2;} ++ { (exit 1); exit 1; }; } ++fi ++if test -z "${NEED_OWN_STRCASESTR_TRUE}" && test -z "${NEED_OWN_STRCASESTR_FALSE}"; then ++ { { echo "$as_me:$LINENO: error: conditional \"NEED_OWN_STRCASESTR\" was never defined. ++Usually this means the macro was only invoked conditionally." >&5 ++echo "$as_me: error: conditional \"NEED_OWN_STRCASESTR\" was never defined. ++Usually this means the macro was only invoked conditionally." >&2;} ++ { (exit 1); exit 1; }; } ++fi ++if test -z "${NEED_OWN_STRCASESTR_TRUE}" && test -z "${NEED_OWN_STRCASESTR_FALSE}"; then ++ { { echo "$as_me:$LINENO: error: conditional \"NEED_OWN_STRCASESTR\" was never defined. ++Usually this means the macro was only invoked conditionally." >&5 ++echo "$as_me: error: conditional \"NEED_OWN_STRCASESTR\" was never defined. ++Usually this means the macro was only invoked conditionally." >&2;} ++ { (exit 1); exit 1; }; } ++fi + if test -z "${NEED_OWN_STRSEP_TRUE}" && test -z "${NEED_OWN_STRSEP_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"NEED_OWN_STRSEP\" was never defined. + Usually this means the macro was only invoked conditionally." >&5 +@@ -18536,6 +18663,8 @@ + s,@ENABLE_PINGER_FALSE@,$ENABLE_PINGER_FALSE,;t t + s,@USE_DELAY_POOLS_TRUE@,$USE_DELAY_POOLS_TRUE,;t t + s,@USE_DELAY_POOLS_FALSE@,$USE_DELAY_POOLS_FALSE,;t t ++s,@USE_ICAP_TRUE@,$USE_ICAP_TRUE,;t t ++s,@USE_ICAP_FALSE@,$USE_ICAP_FALSE,;t t + s,@USE_SNMP_TRUE@,$USE_SNMP_TRUE,;t t + s,@USE_SNMP_FALSE@,$USE_SNMP_FALSE,;t t + s,@SNMPLIB@,$SNMPLIB,;t t +@@ -18599,6 +18728,10 @@ + s,@USE_KQUEUE_FALSE@,$USE_KQUEUE_FALSE,;t t + s,@NEED_OWN_SNPRINTF_TRUE@,$NEED_OWN_SNPRINTF_TRUE,;t t + s,@NEED_OWN_SNPRINTF_FALSE@,$NEED_OWN_SNPRINTF_FALSE,;t t ++s,@NEED_OWN_STRNSTR_TRUE@,$NEED_OWN_STRNSTR_TRUE,;t t ++s,@NEED_OWN_STRNSTR_FALSE@,$NEED_OWN_STRNSTR_FALSE,;t t ++s,@NEED_OWN_STRCASESTR_TRUE@,$NEED_OWN_STRCASESTR_TRUE,;t t ++s,@NEED_OWN_STRCASESTR_FALSE@,$NEED_OWN_STRCASESTR_FALSE,;t t + s,@NEED_OWN_STRSEP_TRUE@,$NEED_OWN_STRSEP_TRUE,;t t + s,@NEED_OWN_STRSEP_FALSE@,$NEED_OWN_STRSEP_FALSE,;t t + s,@REGEXLIB@,$REGEXLIB,;t t +--- include/autoconf.h.in.orig Wed Jul 12 17:00:31 2006 ++++ include/autoconf.h.in Sat Aug 5 16:18:25 2006 +@@ -454,6 +454,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_STDLIB_H + ++/* Define to 1 if you have the `strcasestr' function. */ ++#undef HAVE_STRCASESTR ++ + /* Define to 1 if you have the `strerror' function. */ + #undef HAVE_STRERROR + +@@ -463,6 +466,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_STRING_H + ++/* Define to 1 if you have the `strnstr' function. */ ++#undef HAVE_STRNSTR ++ + /* Define to 1 if you have the `strsep' function. */ + #undef HAVE_STRSEP + +@@ -587,6 +593,9 @@ + + /* Some systems support __va_copy */ + #undef HAVE___VA_COPY ++ ++/* Content filtering via ICAP servers. */ ++#undef HS_FEAT_ICAP + + /* By default (for now anyway) Squid includes options which allows the cache + administrator to violate the HTTP protocol specification in terms of cache +--- lib/Makefile.in.orig Mon Jun 12 08:10:08 2006 ++++ lib/Makefile.in Sat Aug 5 16:19:17 2006 +@@ -62,18 +62,21 @@ + am__libmiscutil_a_SOURCES_DIST = Array.c base64.c getfullhostname.c \ + hash.c heap.c html_quote.c iso3307.c md5.c radix.c rfc1035.c \ + rfc1123.c rfc1738.c rfc2617.c safe_inet_addr.c snprintf.c \ +- splay.c Stack.c strsep.c stub_memaccount.c util.c uudecode.c ++ splay.c Stack.c strnstr.c strcasestr.c strsep.c \ ++ stub_memaccount.c util.c uudecode.c + @NEED_OWN_MD5_TRUE@am__objects_1 = md5.$(OBJEXT) + @NEED_OWN_SNPRINTF_TRUE@am__objects_2 = snprintf.$(OBJEXT) +-@NEED_OWN_STRSEP_TRUE@am__objects_3 = strsep.$(OBJEXT) ++@NEED_OWN_STRNSTR_TRUE@am__objects_3 = strnstr.$(OBJEXT) ++@NEED_OWN_STRCASESTR_TRUE@am__objects_4 = strcasestr.$(OBJEXT) ++@NEED_OWN_STRSEP_TRUE@am__objects_5 = strsep.$(OBJEXT) + am_libmiscutil_a_OBJECTS = Array.$(OBJEXT) base64.$(OBJEXT) \ + getfullhostname.$(OBJEXT) hash.$(OBJEXT) heap.$(OBJEXT) \ + html_quote.$(OBJEXT) iso3307.$(OBJEXT) $(am__objects_1) \ + radix.$(OBJEXT) rfc1035.$(OBJEXT) rfc1123.$(OBJEXT) \ + rfc1738.$(OBJEXT) rfc2617.$(OBJEXT) safe_inet_addr.$(OBJEXT) \ + $(am__objects_2) splay.$(OBJEXT) Stack.$(OBJEXT) \ +- $(am__objects_3) stub_memaccount.$(OBJEXT) util.$(OBJEXT) \ +- uudecode.$(OBJEXT) ++ $(am__objects_3) $(am__objects_4) $(am__objects_5) \ ++ stub_memaccount.$(OBJEXT) util.$(OBJEXT) uudecode.$(OBJEXT) + libmiscutil_a_OBJECTS = $(am_libmiscutil_a_OBJECTS) + libntlmauth_a_AR = $(AR) $(ARFLAGS) + libntlmauth_a_DEPENDENCIES = @LIBOBJS@ +@@ -184,6 +187,10 @@ + NEED_OWN_MD5_TRUE = @NEED_OWN_MD5_TRUE@ + NEED_OWN_SNPRINTF_FALSE = @NEED_OWN_SNPRINTF_FALSE@ + NEED_OWN_SNPRINTF_TRUE = @NEED_OWN_SNPRINTF_TRUE@ ++NEED_OWN_STRCASESTR_FALSE = @NEED_OWN_STRCASESTR_FALSE@ ++NEED_OWN_STRCASESTR_TRUE = @NEED_OWN_STRCASESTR_TRUE@ ++NEED_OWN_STRNSTR_FALSE = @NEED_OWN_STRNSTR_FALSE@ ++NEED_OWN_STRNSTR_TRUE = @NEED_OWN_STRNSTR_TRUE@ + NEED_OWN_STRSEP_FALSE = @NEED_OWN_STRSEP_FALSE@ + NEED_OWN_STRSEP_TRUE = @NEED_OWN_STRSEP_TRUE@ + NEGOTIATE_AUTH_HELPERS = @NEGOTIATE_AUTH_HELPERS@ +@@ -221,6 +228,8 @@ + USE_DNSSERVER_TRUE = @USE_DNSSERVER_TRUE@ + USE_EPOLL_FALSE = @USE_EPOLL_FALSE@ + USE_EPOLL_TRUE = @USE_EPOLL_TRUE@ ++USE_ICAP_FALSE = @USE_ICAP_FALSE@ ++USE_ICAP_TRUE = @USE_ICAP_TRUE@ + USE_KQUEUE_FALSE = @USE_KQUEUE_FALSE@ + USE_KQUEUE_TRUE = @USE_KQUEUE_TRUE@ + USE_POLL_FALSE = @USE_POLL_FALSE@ +@@ -275,6 +284,10 @@ + target_alias = @target_alias@ + @NEED_OWN_SNPRINTF_FALSE@SNPRINTFSOURCE = + @NEED_OWN_SNPRINTF_TRUE@SNPRINTFSOURCE = snprintf.c ++@NEED_OWN_STRNSTR_FALSE@STRNSTRSOURCE = ++@NEED_OWN_STRNSTR_TRUE@STRNSTRSOURCE = strnstr.c ++@NEED_OWN_STRCASESTR_FALSE@STRCASESTRSOURCE = ++@NEED_OWN_STRCASESTR_TRUE@STRCASESTRSOURCE = strcasestr.c + @NEED_OWN_STRSEP_FALSE@STRSEPSOURCE = + @NEED_OWN_STRSEP_TRUE@STRSEPSOURCE = strsep.c + @NEED_OWN_MD5_FALSE@MD5SOURCE = +@@ -316,6 +329,8 @@ + $(SNPRINTFSOURCE) \ + splay.c \ + Stack.c \ ++ $(STRNSTRSOURCE) \ ++ $(STRCASESTRSOURCE) \ + $(STRSEPSOURCE) \ + stub_memaccount.c \ + util.c \ +@@ -430,6 +445,8 @@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snprintf.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/splay.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sspwin32.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strcasestr.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strnstr.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strsep.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stub_memaccount.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ +--- src/Makefile.in.orig Sun Jul 30 10:56:18 2006 ++++ src/Makefile.in Sat Aug 5 16:19:21 2006 +@@ -90,18 +90,20 @@ + forward.c fqdncache.c ftp.c globals.h gopher.c helper.c htcp.c \ + http.c HttpStatusLine.c HttpHdrCc.c HttpHdrRange.c \ + HttpHdrContRange.c HttpHeader.c HttpHeaderTools.c HttpBody.c \ +- HttpMsg.c HttpReply.c HttpRequest.c icmp.c icp_v2.c icp_v3.c \ +- ident.c internal.c ipc.c ipcache.c leakfinder.c locrewrite.c \ +- logfile.c main.c mem.c MemPool.c MemBuf.c mime.c multicast.c \ +- neighbors.c net_db.c Packer.c pconn.c peer_digest.c \ +- peer_monitor.c peer_select.c peer_sourcehash.c peer_userhash.c \ +- protos.h redirect.c referer.c refresh.c send-announce.c \ +- snmp_core.c snmp_agent.c squid.h ssl.c ssl_support.c stat.c \ +- StatHist.c String.c stmem.c store.c store_io.c store_client.c \ +- store_digest.c store_dir.c store_key_md5.c store_log.c \ +- store_rebuild.c store_swapin.c store_swapmeta.c \ +- store_swapout.c structs.h tools.c typedefs.h unlinkd.c url.c \ +- urn.c useragent.c wais.c wccp.c wccp2.c whois.c win32.c ++ HttpMsg.c HttpReply.c HttpRequest.c icap_common.c \ ++ icap_reqmod.c icap_respmod.c icap_opt.c icmp.c icp_v2.c \ ++ icp_v3.c ident.c internal.c ipc.c ipcache.c leakfinder.c \ ++ locrewrite.c logfile.c main.c mem.c MemPool.c MemBuf.c mime.c \ ++ multicast.c neighbors.c net_db.c Packer.c pconn.c \ ++ peer_digest.c peer_monitor.c peer_select.c peer_sourcehash.c \ ++ peer_userhash.c protos.h redirect.c referer.c refresh.c \ ++ send-announce.c snmp_core.c snmp_agent.c squid.h ssl.c \ ++ ssl_support.c stat.c StatHist.c String.c stmem.c store.c \ ++ store_io.c store_client.c store_digest.c store_dir.c \ ++ store_key_md5.c store_log.c store_rebuild.c store_swapin.c \ ++ store_swapmeta.c store_swapout.c structs.h tools.c typedefs.h \ ++ unlinkd.c url.c urn.c useragent.c wais.c wccp.c wccp2.c \ ++ whois.c win32.c + @USE_EPOLL_FALSE@@USE_KQUEUE_FALSE@@USE_POLL_FALSE@@USE_SELECT_FALSE@@USE_SELECT_WIN32_TRUE@am__objects_1 = comm_select_win32.$(OBJEXT) + @USE_EPOLL_FALSE@@USE_KQUEUE_FALSE@@USE_POLL_FALSE@@USE_SELECT_TRUE@am__objects_1 = comm_select.$(OBJEXT) + @USE_EPOLL_FALSE@@USE_KQUEUE_FALSE@@USE_POLL_TRUE@am__objects_1 = comm_poll.$(OBJEXT) +@@ -112,12 +114,15 @@ + @USE_DNSSERVER_FALSE@am__objects_3 = dns_internal.$(OBJEXT) + @USE_DNSSERVER_TRUE@am__objects_3 = dns.$(OBJEXT) + @ENABLE_HTCP_TRUE@am__objects_4 = htcp.$(OBJEXT) +-@MAKE_LEAKFINDER_TRUE@am__objects_5 = leakfinder.$(OBJEXT) +-@USE_SNMP_TRUE@am__objects_6 = snmp_core.$(OBJEXT) \ ++@USE_ICAP_TRUE@am__objects_5 = icap_common.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_reqmod.$(OBJEXT) icap_respmod.$(OBJEXT) \ ++@USE_ICAP_TRUE@ icap_opt.$(OBJEXT) ++@MAKE_LEAKFINDER_TRUE@am__objects_6 = leakfinder.$(OBJEXT) ++@USE_SNMP_TRUE@am__objects_7 = snmp_core.$(OBJEXT) \ + @USE_SNMP_TRUE@ snmp_agent.$(OBJEXT) +-@ENABLE_SSL_TRUE@am__objects_7 = ssl_support.$(OBJEXT) +-@ENABLE_UNLINKD_TRUE@am__objects_8 = unlinkd.$(OBJEXT) +-@ENABLE_WIN32SPECIFIC_TRUE@am__objects_9 = win32.$(OBJEXT) ++@ENABLE_SSL_TRUE@am__objects_8 = ssl_support.$(OBJEXT) ++@ENABLE_UNLINKD_TRUE@am__objects_9 = unlinkd.$(OBJEXT) ++@ENABLE_WIN32SPECIFIC_TRUE@am__objects_10 = win32.$(OBJEXT) + am_squid_OBJECTS = access_log.$(OBJEXT) acl.$(OBJEXT) asn.$(OBJEXT) \ + authenticate.$(OBJEXT) cache_cf.$(OBJEXT) \ + CacheDigest.$(OBJEXT) cache_manager.$(OBJEXT) carp.$(OBJEXT) \ +@@ -132,27 +137,27 @@ + HttpHdrRange.$(OBJEXT) HttpHdrContRange.$(OBJEXT) \ + HttpHeader.$(OBJEXT) HttpHeaderTools.$(OBJEXT) \ + HttpBody.$(OBJEXT) HttpMsg.$(OBJEXT) HttpReply.$(OBJEXT) \ +- HttpRequest.$(OBJEXT) icmp.$(OBJEXT) icp_v2.$(OBJEXT) \ +- icp_v3.$(OBJEXT) ident.$(OBJEXT) internal.$(OBJEXT) \ +- ipc.$(OBJEXT) ipcache.$(OBJEXT) $(am__objects_5) \ +- locrewrite.$(OBJEXT) logfile.$(OBJEXT) main.$(OBJEXT) \ +- mem.$(OBJEXT) MemPool.$(OBJEXT) MemBuf.$(OBJEXT) \ +- mime.$(OBJEXT) multicast.$(OBJEXT) neighbors.$(OBJEXT) \ +- net_db.$(OBJEXT) Packer.$(OBJEXT) pconn.$(OBJEXT) \ +- peer_digest.$(OBJEXT) peer_monitor.$(OBJEXT) \ ++ HttpRequest.$(OBJEXT) $(am__objects_5) icmp.$(OBJEXT) \ ++ icp_v2.$(OBJEXT) icp_v3.$(OBJEXT) ident.$(OBJEXT) \ ++ internal.$(OBJEXT) ipc.$(OBJEXT) ipcache.$(OBJEXT) \ ++ $(am__objects_6) locrewrite.$(OBJEXT) logfile.$(OBJEXT) \ ++ main.$(OBJEXT) mem.$(OBJEXT) MemPool.$(OBJEXT) \ ++ MemBuf.$(OBJEXT) mime.$(OBJEXT) multicast.$(OBJEXT) \ ++ neighbors.$(OBJEXT) net_db.$(OBJEXT) Packer.$(OBJEXT) \ ++ pconn.$(OBJEXT) peer_digest.$(OBJEXT) peer_monitor.$(OBJEXT) \ + peer_select.$(OBJEXT) peer_sourcehash.$(OBJEXT) \ + peer_userhash.$(OBJEXT) redirect.$(OBJEXT) referer.$(OBJEXT) \ +- refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_6) \ +- ssl.$(OBJEXT) $(am__objects_7) stat.$(OBJEXT) \ ++ refresh.$(OBJEXT) send-announce.$(OBJEXT) $(am__objects_7) \ ++ ssl.$(OBJEXT) $(am__objects_8) stat.$(OBJEXT) \ + StatHist.$(OBJEXT) String.$(OBJEXT) stmem.$(OBJEXT) \ + store.$(OBJEXT) store_io.$(OBJEXT) store_client.$(OBJEXT) \ + store_digest.$(OBJEXT) store_dir.$(OBJEXT) \ + store_key_md5.$(OBJEXT) store_log.$(OBJEXT) \ + store_rebuild.$(OBJEXT) store_swapin.$(OBJEXT) \ + store_swapmeta.$(OBJEXT) store_swapout.$(OBJEXT) \ +- tools.$(OBJEXT) $(am__objects_8) url.$(OBJEXT) urn.$(OBJEXT) \ ++ tools.$(OBJEXT) $(am__objects_9) url.$(OBJEXT) urn.$(OBJEXT) \ + useragent.$(OBJEXT) wais.$(OBJEXT) wccp.$(OBJEXT) \ +- wccp2.$(OBJEXT) whois.$(OBJEXT) $(am__objects_9) ++ wccp2.$(OBJEXT) whois.$(OBJEXT) $(am__objects_10) + nodist_squid_OBJECTS = repl_modules.$(OBJEXT) auth_modules.$(OBJEXT) \ + store_modules.$(OBJEXT) globals.$(OBJEXT) \ + string_arrays.$(OBJEXT) +@@ -274,6 +279,10 @@ + NEED_OWN_MD5_TRUE = @NEED_OWN_MD5_TRUE@ + NEED_OWN_SNPRINTF_FALSE = @NEED_OWN_SNPRINTF_FALSE@ + NEED_OWN_SNPRINTF_TRUE = @NEED_OWN_SNPRINTF_TRUE@ ++NEED_OWN_STRCASESTR_FALSE = @NEED_OWN_STRCASESTR_FALSE@ ++NEED_OWN_STRCASESTR_TRUE = @NEED_OWN_STRCASESTR_TRUE@ ++NEED_OWN_STRNSTR_FALSE = @NEED_OWN_STRNSTR_FALSE@ ++NEED_OWN_STRNSTR_TRUE = @NEED_OWN_STRNSTR_TRUE@ + NEED_OWN_STRSEP_FALSE = @NEED_OWN_STRSEP_FALSE@ + NEED_OWN_STRSEP_TRUE = @NEED_OWN_STRSEP_TRUE@ + NEGOTIATE_AUTH_HELPERS = @NEGOTIATE_AUTH_HELPERS@ +@@ -311,6 +320,8 @@ + USE_DNSSERVER_TRUE = @USE_DNSSERVER_TRUE@ + USE_EPOLL_FALSE = @USE_EPOLL_FALSE@ + USE_EPOLL_TRUE = @USE_EPOLL_TRUE@ ++USE_ICAP_FALSE = @USE_ICAP_FALSE@ ++USE_ICAP_TRUE = @USE_ICAP_TRUE@ + USE_KQUEUE_FALSE = @USE_KQUEUE_FALSE@ + USE_KQUEUE_TRUE = @USE_KQUEUE_TRUE@ + USE_POLL_FALSE = @USE_POLL_FALSE@ +@@ -363,6 +374,8 @@ + sharedstatedir = @sharedstatedir@ + sysconfdir = @sysconfdir@ + target_alias = @target_alias@ ++@USE_ICAP_FALSE@ICAPSOURCE = ++@USE_ICAP_TRUE@ICAPSOURCE = icap_common.c icap_reqmod.c icap_respmod.c icap_opt.c + @USE_DNSSERVER_FALSE@DNSSOURCE = dns_internal.c + @USE_DNSSERVER_TRUE@DNSSOURCE = dns.c + @USE_DNSSERVER_FALSE@DNSSERVER = +@@ -458,6 +471,7 @@ + HttpMsg.c \ + HttpReply.c \ + HttpRequest.c \ ++ $(ICAPSOURCE) \ + icmp.c \ + icp_v2.c \ + icp_v3.c \ +@@ -768,6 +782,10 @@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/helper.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/htcp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icap_common.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icap_opt.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icap_reqmod.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icap_respmod.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icmp.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icp_v2.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icp_v3.Po@am__quote@ Index: Makefile =================================================================== --- Makefile (.../www/squid26) (revision 984) +++ Makefile (.../local/squid26) (revision 984) @@ -75,8 +75,7 @@ # Enable experimental multicast notification of cachemisses. PORTNAME= squid -PORTVERSION= 2.6.1 -PORTREVISION= 1 +PORTVERSION= 2.6.2 CATEGORIES= www MASTER_SITES= ftp://ftp.squid-cache.org/pub/%SUBDIR%/ \ ftp://archive.progeny.com/squid/%SUBDIR%/ \ @@ -88,7 +87,7 @@ ftp://ftp.ccs.neu.edu/pub/mirrors/squid.nlanr.net/pub/%SUBDIR%/ \ ${MASTER_SITE_RINGSERVER:S,%SUBDIR%,net/www/squid/&,} MASTER_SITE_SUBDIR= squid-2/STABLE -DISTNAME= squid-2.6.STABLE1 +DISTNAME= squid-2.6.STABLE2 DIST_SUBDIR= squid2.6 PATCH_SITES= http://www.squid-cache.org/%SUBDIR%/ \ @@ -101,7 +100,8 @@ http://www1.jp.squid-cache.org/%SUBDIR%/ \ http://www1.tw.squid-cache.org/%SUBDIR%/ PATCH_SITE_SUBDIR= Versions/v2/2.6/changesets/ -PATCHFILES= 10799.patch +PATCHFILES= 10894.patch 10895.patch 10896.patch 10898.patch 10899.patch \ + 10900.patch 10901.patch 10902.patch 10903.patch 10904.patch PATCH_DIST_STRIP= -p1 MAINTAINER= tmseck@netcologne.de @@ -144,7 +144,6 @@ SQUID_ARP_ACL "Enable ACLs based on ethernet address" off \ SQUID_PF "Enable transparent proxying with PF" off \ SQUID_IPFILTER "Enable transp. proxying with IPFilter" off \ - SQUID_IPFW "Enable transparent proxying with IPFW" off \ SQUID_FOLLOW_XFF "Follow X-Forwarded-For headers" off \ SQUID_ICAP "Enable ICAP client functionality" off \ SQUID_AUFS "Enable the aufs storage scheme" off \ @@ -187,7 +186,7 @@ libexec+= unlinkd .endif -sbin= RunCache squidclient squid +sbin= RunCache cossdump squidclient squid CONFIGURE_ARGS= --bindir=${PREFIX}/sbin \ --sbindir=${PREFIX}/sbin \ @@ -331,17 +330,14 @@ CONFIGURE_ARGS+= --enable-ipf-transparent .endif .endif -.if defined(WITH_SQUID_IPFW) -CFLAGS+= -DIPFW_TRANSPARENT -.endif .if defined(WITH_SQUID_FOLLOW_XFF) CONFIGURE_ARGS+= --enable-follow-x-forwarded-for .endif .if defined(WITH_SQUID_ICAP) -IGNORE= patches for ICAP support are not yet updated -EXTRA_PATCHES+= -#CONFIGURE_ARGS+= --enable-icap-support -#error_files+= ERR_ICAP_FAILURE +EXTRA_PATCHES+= ${PATCHDIR}/icap-2.6-bootstrap.patch \ + ${PATCHDIR}/icap-2.6-core.patch +CONFIGURE_ARGS+= --enable-icap-support +error_files+= ERR_ICAP_FAILURE .endif .if !defined(WITHOUT_SQUID_KQUEUE) CONFIGURE_ARGS+= --enable-kqueue >Release-Note: >Audit-Trail: >Unformatted: