/*
- * DEBUG: section XXX
+ * DEBUG: section 93 Adaptation
*/
-#include "squid.h"
+#include "squid-old.h"
#include "ConfigParser.h"
#include "adaptation/ServiceConfig.h"
+#include "ip/tools.h"
+#include <set>
Adaptation::ServiceConfig::ServiceConfig():
- port(-1), method(methodNone), point(pointNone), bypass(false)
+ port(-1), method(methodNone), point(pointNone),
+ bypass(false), maxConn(-1), onOverload(srvWait),
+ routing(false), ipv6(false)
{}
const char *
bool
Adaptation::ServiceConfig::parse()
{
- char *method_point = NULL;
+ String method_point;
ConfigParser::ParseString(&key);
ConfigParser::ParseString(&method_point);
- ConfigParser::ParseBool(&bypass);
- ConfigParser::ParseString(&uri);
+ 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
+ bool grokkedUri = false;
+ bool onOverloadSet = false;
+ std::set<std::string> options;
+
+ while (char *option = strtok(NULL, w_space)) {
+ const char *name = option;
+ const char *value = "";
+ if (strcmp(option, "0") == 0) { // backward compatibility
+ 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 '='
+ }
+ }
- debugs(3, 5, HERE << cfg_filename << ':' << config_lineno << ": " <<
- key << " " << method_point << " " << bypass);
+ // 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;
+ }
+ options.insert(name);
+
+ 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;
+ }
- method = parseMethod(method_point);
- point = parseVectPoint(method_point);
+ // set default on-overload value if needed
+ if (!onOverloadSet)
+ onOverload = bypass ? srvBypass : srvWait;
- debugs(3, 5, HERE << cfg_filename << ':' << config_lineno << ": " <<
- "service_configConfig is " << methodStr() << "_" << vectPointStr());
+ // 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 << ": " <<
+ "empty adaptation service URI");
+ return false;
+ }
+
+ uri = value;
// extract scheme and use it as the service_configConfig protocol
const char *schemeSuffix = "://";
- if (const char *schemeEnd = uri.pos(schemeSuffix))
- protocol.limitInit(uri.rawBuf(), schemeEnd - uri.rawBuf()); //substring
+ const String::size_type schemeEnd = uri.find(schemeSuffix);
+ if (schemeEnd != String::npos)
+ protocol=uri.substr(0,schemeEnd);
+
debugs(3, 5, HERE << cfg_filename << ':' << config_lineno << ": " <<
"service protocol is " << protocol);
- if (!protocol.size())
+
+ if (protocol.size() == 0)
return false;
// skip scheme
bool have_port = false;
- if ((e = strchr(s, ':')) != NULL) {
- have_port = true;
- } else if ((e = strchr(s, '/')) != NULL) {
- have_port = false;
+ int len = 0;
+ if (*s == '[') {
+ const char *t;
+ if ((t = strchr(s, ']')) == NULL)
+ return false;
+
+ s++;
+ len = t - s;
+ if ((e = strchr(t, ':')) != NULL) {
+ have_port = true;
+ } else if ((e = strchr(t, '/')) != NULL) {
+ have_port = false;
+ } else {
+ return false;
+ }
} else {
- return false;
+ if ((e = strchr(s, ':')) != NULL) {
+ have_port = true;
+ } else if ((e = strchr(s, '/')) != NULL) {
+ have_port = false;
+ } else {
+ return false;
+ }
+ len = e - s;
}
- int len = e - s;
host.limitInit(s, len);
s = e;
}
resource.limitInit(s, len + 1);
+ return true;
+}
- if ((bypass != 0) && (bypass != 1)) {
+
+bool
+Adaptation::ServiceConfig::grokBool(bool &var, const char *name, const char *value)
+{
+ if (!strcmp(value, "0") || !strcmp(value, "off"))
+ var = false;
+ else if (!strcmp(value, "1") || !strcmp(value, "on"))
+ var = true;
+ else {
debugs(3, 0, HERE << cfg_filename << ':' << config_lineno << ": " <<
- "wrong bypass value; 0 or 1 expected: " << bypass);
+ "wrong value for boolean " << name << "; " <<
+ "'0', '1', 'on', or 'off' expected but got: " << value);
+ return false;
+ }
+
+ return true;
+}
+
+bool
+Adaptation::ServiceConfig::grokLong(long &var, const char *name, const char *value)
+{
+ char *bad = NULL;
+ const long p = strtol(value, &bad, 0);
+ if (p < 0 || bad == value) {
+ debugs(3, DBG_CRITICAL, "ERROR: " << cfg_filename << ':' <<
+ config_lineno << ": " << "wrong value for " << name << "; " <<
+ "a non-negative integer expected but got: " << value);
return false;
}
+ var = p;
+ return true;
+}
+bool
+Adaptation::ServiceConfig::grokOnOverload(SrvBehaviour &var, const char *value)
+{
+ if (strcmp(value, "block") == 0)
+ var = srvBlock;
+ else if (strcmp(value, "bypass") == 0)
+ var = srvBypass;
+ else if (strcmp(value, "wait") == 0)
+ var = srvWait;
+ else if (strcmp(value, "force") == 0)
+ var = srvForce;
+ else {
+ debugs(3, DBG_CRITICAL, "ERROR: " << cfg_filename << ':' <<
+ config_lineno << ": " << "wrong value for on-overload; " <<
+ "'block', 'bypass', 'wait' or 'force' expected but got: " << value);
+ return false;
+ }
return true;
}
+
+bool
+Adaptation::ServiceConfig::grokExtension(const char *name, const char *value)
+{
+ // we do not accept extensions by default
+ debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " <<
+ "ERROR: unknown adaptation service option: " <<
+ name << '=' << value);
+ return false;
+}