Bug 3349: Bad support for adaptation service URIs with '=' Currently using URIs which include "=" is not supported by ecap_service/icap_service configuration parameters. Also the squid.conf documentation about ecap_service is outdated. This patch - Fixes the [e|i]cap_service line parser to allow URIs with "=" - Changes the [e|i]cap_service configuration parameter to use the following syntax: ecap_service id vectoring_point uri [name=value ...] - Check for duplicated options - Fixes the related documentation - Also the older [e|i]cap_service syntax forms are supported: ecap_service id vectoring_point [1|0] uri ecap_service id vectoring_point [name=value ...] uri - The "uri" options is not documented but supported. This is a Measurement Factory project. === modified file 'src/adaptation/ServiceConfig.cc' --- src/adaptation/ServiceConfig.cc 2011-05-24 22:26:21 +0000 +++ src/adaptation/ServiceConfig.cc 2011-10-06 09:10:48 +0000 @@ -1,28 +1,29 @@ /* * DEBUG: section 93 Adaptation */ #include "squid.h" #include "ConfigParser.h" #include "adaptation/ServiceConfig.h" #include "ip/tools.h" +#include Adaptation::ServiceConfig::ServiceConfig(): port(-1), method(methodNone), point(pointNone), bypass(false), maxConn(-1), onOverload(srvWait), routing(false), ipv6(false) {} const char * Adaptation::ServiceConfig::methodStr() const { return Adaptation::methodStr(method); } const char * Adaptation::ServiceConfig::vectPointStr() const { return Adaptation::vectPointStr(point); } Adaptation::Method @@ -52,98 +53,106 @@ if (!strcasecmp(t, "postcache")) return Adaptation::pointPostCache; return Adaptation::pointNone; } bool Adaptation::ServiceConfig::parse() { String method_point; ConfigParser::ParseString(&key); ConfigParser::ParseString(&method_point); method = parseMethod(method_point.termedBuf()); point = parseVectPoint(method_point.termedBuf()); // reset optional parameters in case we are reconfiguring bypass = routing = false; // handle optional service name=value parameters - const char *lastOption = NULL; bool grokkedUri = false; bool onOverloadSet = false; + std::set options; + while (char *option = strtok(NULL, w_space)) { + const char *name = option; + const char *value = ""; if (strcmp(option, "0") == 0) { // backward compatibility - bypass = false; - continue; - } - if (strcmp(option, "1") == 0) { // backward compatibility - bypass = true; - continue; + name = "bypass"; + value = "off"; + debugs(3, opt_parse_cfg_only?0:1, "UPGRADE: Please use 'bypass=off' option to disable service bypass"); + } else if (strcmp(option, "1") == 0) { // backward compatibility + name = "bypass"; + value = "on"; + debugs(3, opt_parse_cfg_only?0:1, "UPGRADE: Please use 'bypass=on' option to enable service bypass"); + } else { + char *eq = strstr(option, "="); + const char *sffx = strstr(option, "://"); + if (!eq || (sffx && sffx < eq)) { //no "=" or has the form "icap://host?arg=val" + name = "uri"; + value = option; + } else { // a normal name=value option + *eq = '\0'; // terminate option name + value = eq + 1; // skip '=' + } } - const char *name = option; - char *value = strstr(option, "="); - if (!value) { - lastOption = option; - break; + // Check if option is set twice + if (options.find(name) != options.end()) { + debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " << + "Duplicate option \"" << name << "\" in adaptation service definition"); + return false; } - *value = '\0'; // terminate option name - ++value; // skip '=' + options.insert(name); - // TODO: warn if option is set twice? bool grokked = false; if (strcmp(name, "bypass") == 0) { grokked = grokBool(bypass, name, value); } else if (strcmp(name, "routing") == 0) grokked = grokBool(routing, name, value); else if (strcmp(name, "uri") == 0) grokked = grokkedUri = grokUri(value); else if (strcmp(name, "ipv6") == 0) { grokked = grokBool(ipv6, name, value); if (grokked && ipv6 && !Ip::EnableIpv6) debugs(3, DBG_IMPORTANT, "WARNING: IPv6 is disabled. ICAP service option ignored."); } else if (strcmp(name, "max-conn") == 0) grokked = grokLong(maxConn, name, value); else if (strcmp(name, "on-overload") == 0) { grokked = grokOnOverload(onOverload, value); onOverloadSet = true; } else grokked = grokExtension(name, value); if (!grokked) return false; } // set default on-overload value if needed if (!onOverloadSet) onOverload = bypass ? srvBypass : srvWait; - // what is left must be the service URI - if (!grokkedUri && !grokUri(lastOption)) - return false; - - // there should be nothing else left - if (const char *tail = strtok(NULL, w_space)) { - debugs(3, 0, cfg_filename << ':' << config_lineno << ": " << - "garbage after adaptation service URI: " << tail); + // is the service URI set? + if (!grokkedUri) { + debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " << + "No \"uri\" option in adaptation service definition"); return false; } debugs(3,5, cfg_filename << ':' << config_lineno << ": " << "adaptation_service " << key << ' ' << methodStr() << "_" << vectPointStr() << ' ' << bypass << routing << ' ' << uri); return true; } bool Adaptation::ServiceConfig::grokUri(const char *value) { // TODO: find core code that parses URLs and extracts various parts // AYJ: most of this is duplicate of urlParse() in src/url.cc if (!value || !*value) { debugs(3, 0, HERE << cfg_filename << ':' << config_lineno << ": " << === modified file 'src/cf.data.pre' --- src/cf.data.pre 2011-09-22 00:46:26 +0000 +++ src/cf.data.pre 2011-10-06 08:42:00 +0000 @@ -6579,51 +6579,53 @@ DOC_END NAME: icap_client_username_encode TYPE: onoff IFDEF: ICAP_CLIENT COMMENT: on|off LOC: Adaptation::Icap::TheConfig.client_username_encode DEFAULT: off DOC_START Whether to base64 encode the authenticated client username. DOC_END NAME: icap_service TYPE: icap_service_type IFDEF: ICAP_CLIENT LOC: Adaptation::Icap::TheConfig DEFAULT: none DOC_START Defines a single ICAP service using the following format: - icap_service service_name vectoring_point [options] service_url + icap_service id vectoring_point uri [option ...] - service_name: ID - an opaque identifier which must be unique in squid.conf + id: ID + an opaque identifier or name which is used to direct traffic to + this specific service. Must be unique among all adaptation + services in squid.conf. vectoring_point: reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache This specifies at which point of transaction processing the ICAP service should be activated. *_postcache vectoring points are not yet supported. - service_url: icap://servername:port/servicepath + uri: icap://servername:port/servicepath ICAP server and service location. ICAP does not allow a single service to handle both REQMOD and RESPMOD transactions. Squid does not enforce that requirement. You can specify services with the same service_url and different vectoring_points. You can even specify multiple identical services as long as their service_names differ. Service options are separated by white space. ICAP services support the following name=value options: bypass=on|off|1|0 If set to 'on' or '1', the ICAP service is treated as optional. If the service cannot be reached or malfunctions, Squid will try to ignore any errors and process the message as if the service was not enabled. No all ICAP errors can be bypassed. If set to 0, the ICAP service is treated as essential and all ICAP errors will result in an error page returned to the HTTP client. @@ -6658,42 +6660,42 @@ * bypass: ignore the "over-connected" ICAP service * wait: wait (in a FIFO queue) for an ICAP connection slot * force: proceed, ignoring the Max-Connections limit In SMP mode with N workers, each worker assumes the service connection limit is Max-Connections/N, even though not all workers may use a given service. The default value is "bypass" if service is bypassable, otherwise it is set to "wait". max-conn=number Use the given number as the Max-Connections limit, regardless of the Max-Connections value given by the service, if any. Older icap_service format without optional named parameters is deprecated but supported for backward compatibility. Example: -icap_service svcBlocker reqmod_precache bypass=0 icap://icap1.mydomain.net:1344/reqmod -icap_service svcLogger reqmod_precache routing=on icap://icap2.mydomain.net:1344/respmod +icap_service svcBlocker reqmod_precache icap://icap1.mydomain.net:1344/reqmod bypass=0 +icap_service svcLogger reqmod_precache icap://icap2.mydomain.net:1344/respmod routing=on DOC_END NAME: icap_class TYPE: icap_class_type IFDEF: ICAP_CLIENT LOC: none DEFAULT: none DOC_START This deprecated option was documented to define an ICAP service chain, even though it actually defined a set of similar, redundant services, and the chains were not supported. To define a set of redundant services, please use the adaptation_service_set directive. For service chains, use adaptation_service_chain. DOC_END NAME: icap_access TYPE: icap_access_type IFDEF: ICAP_CLIENT @@ -6711,59 +6713,90 @@ COMMENT_END NAME: ecap_enable TYPE: onoff IFDEF: USE_ECAP COMMENT: on|off LOC: Adaptation::Ecap::TheConfig.onoff DEFAULT: off DOC_START Controls whether eCAP support is enabled. DOC_END NAME: ecap_service TYPE: ecap_service_type IFDEF: USE_ECAP LOC: Adaptation::Ecap::TheConfig DEFAULT: none DOC_START Defines a single eCAP service - ecap_service servicename vectoring_point bypass service_url + ecap_service id vectoring_point uri [option ...] - vectoring_point = reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache + id: ID + an opaque identifier or name which is used to direct traffic to + this specific service. Must be unique among all adaptation + services in squid.conf. + + vectoring_point: reqmod_precache|reqmod_postcache|respmod_precache|respmod_postcache This specifies at which point of transaction processing the eCAP service should be activated. *_postcache vectoring points are not yet supported. - bypass = 1|0 - If set to 1, the eCAP service is treated as optional. If the - service cannot be reached or malfunctions, Squid will try to - ignore any errors and process the message as if the service + + uri: ecap://vendor/service_name?custom&cgi=style¶meters=optional + Squid uses the eCAP service URI to match this configuration + line with one of the dynamically loaded services. Each loaded + eCAP service must have a unique URI. Obtain the right URI from + the service provider. + + + Service options are separated by white space. eCAP services support + the following name=value options: + + bypass=on|off|1|0 + If set to 'on' or '1', the eCAP service is treated as optional. + If the service cannot be reached or malfunctions, Squid will try + to ignore any errors and process the message as if the service was not enabled. No all eCAP errors can be bypassed. - If set to 0, the eCAP service is treated as essential and all - eCAP errors will result in an error page returned to the + If set to 'off' or '0', the eCAP service is treated as essential + and all eCAP errors will result in an error page returned to the HTTP client. - service_url = ecap://vendor/service_name?custom&cgi=style¶meters=optional + + Bypass is off by default: services are treated as essential. + + routing=on|off|1|0 + If set to 'on' or '1', the eCAP service is allowed to + dynamically change the current message adaptation plan by + returning a chain of services to be used next. + + Dynamic adaptation plan may cross or cover multiple supported + vectoring points in their natural processing order. + + Routing is not allowed by default. + + Older ecap_service format without optional named parameters is + deprecated but supported for backward compatibility. + Example: -ecap_service service_1 reqmod_precache 0 ecap://filters-R-us/leakDetector?on_error=block -ecap_service service_2 respmod_precache 1 icap://filters-R-us/virusFilter?config=/etc/vf.cfg +ecap_service s1 reqmod_precache ecap://filters.R.us/leakDetector?on_error=block bypass=off +ecap_service s2 respmod_precache ecap://filters.R.us/virusFilter config=/etc/vf.cfg bypass=on DOC_END NAME: loadable_modules TYPE: wordlist IFDEF: USE_LOADABLE_MODULES LOC: Config.loadable_module_names DEFAULT: none DOC_START Instructs Squid to load the specified dynamic module(s) or activate preloaded module(s). Example: loadable_modules @DEFAULT_PREFIX@/lib/MinimalAdapter.so DOC_END COMMENT_START MESSAGE ADAPTATION OPTIONS ----------------------------------------------------------------------------- COMMENT_END NAME: adaptation_service_set