commit 758ebe7b9fa345fc7f59590bf9755324c128a088
parent f0cb14bcd2b19ee5dd0abbc223c9d25581c8b4c1
Author: finwo <finwo@pm.me>
Date: Sat, 3 Jan 2026 19:38:19 +0100
2.58
Diffstat:
21 files changed, 362 insertions(+), 158 deletions(-)
diff --git a/ChangeLog b/ChangeLog
@@ -1,3 +1,9 @@
+2.58 [KK 2009-10-14]
+- Added test/ftp.xml as a sample ftp service configuration.
+- Implemented URL-based dispatching: flag --url-match/-j, present in
+ xrctl, tested in test/urlmatch.xml, added to web interface
+- Usage information now shows long versions of flags first.
+
2.57 [KK 2009-09-14]
- Output of "xrctl status" colorized when a service is not running,
thanks Frederik D. for the suggestion and the code!
diff --git a/Makefile b/Makefile
@@ -1,7 +1,7 @@
# Top-level Makefile for XR
# -------------------------
-VER = 2.57
+VER = 2.58
PREFIX = $(DESTDIR)/usr
BINDIR = $(PREFIX)/sbin
MANDIR = $(PREFIX)/share/man
diff --git a/doc/xr.odt b/doc/xr.odt
Binary files differ.
diff --git a/doc/xr.pdf b/doc/xr.pdf
Binary files differ.
diff --git a/test/ftp.xml b/test/ftp.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<configuration>
+ <system>
+ <uselogger>false</uselogger>
+ <logdir>/tmp</logdir>
+ </system>
+
+ <service>
+ <name>ftp</name>
+ <server>
+ <type>tcp</type>
+ <address>127.0.0.1:21</address>
+ <dispatchmode>first-available</dispatchmode>
+ <verbose>true</verbose>
+ </server>
+ <backend>
+ <address>10.50.40.118:21</address>
+ </backend>
+ </service>
+
+ <service>
+ <name>ftpdata</name>
+ <server>
+ <type>tcp</type>
+ <address>127.0.0.1:20</address>
+ <dispatchmode>first-available</dispatchmode>
+ <verbose>true</verbose>
+ </server>
+ <backend>
+ <address>10.50.40.118:20</address>
+ </backend>
+ </service>
+
+</configuration>
diff --git a/test/urlmatch.xml b/test/urlmatch.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+ <system>
+ <uselogger>false</uselogger>
+ <logdir>/tmp</logdir>
+ <path>/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/opt/local/bin:/opt/local/sbin</path>
+ </system>
+
+ <service>
+ <name>urlmatch</name>
+ <server>
+ <type>http</type>
+ <address>0:20000</address>
+ <webinterface>0:20001</webinterface>
+ <verbose>yes</verbose>
+ <debug>yes</debug>
+ </server>
+ <backend>
+ <address>server1:10000</address>
+ <urlmatch>/$</urlmatch>
+ </backend>
+ <backend>
+ <address>server2:10000</address>
+ <urlmatch>/pages/fch</urlmatch>
+ </backend>
+ <backend>
+ <address>server3:10000</address>
+ <urlmatch>/pages/[^f][^c][^h]</urlmatch>
+ </backend>
+ </service>
+</configuration>
diff --git a/xr/Dispatchers/httpdispatcher/dispatch.cc b/xr/Dispatchers/httpdispatcher/dispatch.cc
@@ -5,6 +5,7 @@ void HttpDispatcher::dispatch() {
unsigned stickytarget;
string host_header = "";
+ string url = "";
// Try to dispatch. Since we're in HTTP mode, we must return an
// error page when dispatching fails.
@@ -17,33 +18,51 @@ void HttpDispatcher::dispatch() {
throw Error("Didn't receive a valid client request.");
msg ("Received client request: '" + buf().firstline() + "'\n");
- // See if hostmatching is used. This is true when a backend
- // matches against a non-dot host.
+ // See if hostmatching or urlmatching is used.
+ // This is true when hosts or urls are matched against non-dot.
bool hostmatchused = false;
- for (unsigned i = 0; i < balancer.nbackends(); i++)
- if (balancer.backend(i).hostmatch() != ".") {
+ bool urlmatchused = false;
+ for (unsigned i = 0; i < balancer.nbackends(); i++) {
+ if (balancer.backend(i).hostmatch() != ".")
hostmatchused = true;
- break;
- }
- // Build new target list if host matching applies.
- if (hostmatchused) {
- host_header = buf().headerval ("Host");
- msg ("Will try to dispatch request for host '" +
- host_header + "'\n");
-
- // We need to build tcpdispatcher's target list now!
- // Construct locally and poke into TcpDispatcher.
- msg ("Creating host-based target list for the HTTP dispatcher\n");
+ if (balancer.backend(i).urlmatch() != ".")
+ urlmatchused = true;
+ }
+
+ // Build new target list if host- or url matching applies.
+ if (hostmatchused || urlmatchused) {
+ msg ("Creating matched target list for the HTTP dispatcher\n");
+
+ if (hostmatchused)
+ host_header = buf().headerval("Host");
+ if (urlmatchused)
+ url = buf().url();
+
BackendVector v;
v.isdefined(true);
+
for (unsigned i = 0; i < balancer.nbackends(); i++) {
- if ( (balancer.backend(i).available()) &&
- (!regexec (&(balancer.backend(i).hostregex()),
- host_header.c_str(), 0, 0, 0)) ) {
+ if (! balancer.backend(i).available())
+ continue;
+ bool host_allowed = true, url_allowed = true;
+ if (hostmatchused &&
+ regexec(&(balancer.backend(i).hostregex()),
+ host_header.c_str(), 0, 0, 0)) {
+ debugmsg("Back end " + balancer.backend(i).description() +
+ " forbidden due to hostmatch\n");
+ host_allowed = false;
+ }
+ if (urlmatchused &&
+ regexec(&(balancer.backend(i).urlregex()),
+ url.c_str(), 0, 0, 0)) {
+ debugmsg("Back end " + balancer.backend(i).description() +
+ " forbidden due to urlmatch\n");
+ url_allowed = false;
+ }
+ if (host_allowed && url_allowed) {
v.add(i);
- if (config.verbose())
- msg (" Candidate target: " +
- balancer.backend(i).description() + "\n");
+ msg("Candidate target: " +
+ balancer.backend(i).description() + "\n");
}
}
targetlist(v);
diff --git a/xr/backend/backend b/xr/backend/backend
@@ -50,6 +50,10 @@ public:
void hostmatch(string const &s) { bdef.hostmatch(s); }
regex_t const &hostregex() const { return bdef.hostregex(); }
+ string const &urlmatch() const { return bdef.urlmatch(); }
+ void urlmatch(string const &u) { bdef.urlmatch(u); }
+ regex_t const &urlregex() const { return bdef.urlregex(); }
+
unsigned weight() const { return bdef.weight(); }
void weight (unsigned w) { bdef.weight(w); }
unsigned adjustedweight() const { return bdef.adjustedweight(); }
diff --git a/xr/backenddef/backenddef b/xr/backenddef/backenddef
@@ -25,12 +25,17 @@ public:
unsigned weight() const { return wt; }
void weight (unsigned w);
- unsigned adjustedweight() const { return min_wt + max_wt - wt; }
+ unsigned adjustedweight() const { return min_wt +
+ max_wt - wt; }
void hostmatch(string const &s);
string const &hostmatch() const { return (host_match); }
regex_t const &hostregex() const { return (host_regex); }
+ void urlmatch(string const &u);
+ string const &urlmatch() const { return (url_match); }
+ regex_t const &urlregex() const { return (url_regex); }
+
BackendCheck const &backendcheck() { return backend_check; }
void backendcheck(BackendCheck const &b) { backend_check = b; }
@@ -40,6 +45,8 @@ private:
unsigned max;
string host_match;
regex_t host_regex;
+ string url_match;
+ regex_t url_regex;
unsigned wt;
static unsigned min_wt, max_wt;
static bool minmax_wt_set;
diff --git a/xr/backenddef/hostmatch.cc b/xr/backenddef/hostmatch.cc
@@ -2,10 +2,8 @@
void BackendDef::hostmatch (string const &s) {
PROFILE("BackendDef::hostmatch");
-
- host_match = s;
- if (host_match == "")
- host_match = ".";
+
+ host_match = (s == "" ? "." : s);
if (regcomp (&host_regex, host_match.c_str(),
REG_EXTENDED | REG_ICASE | REG_NOSUB))
throw Error("Host match specifier '" +
diff --git a/xr/backenddef/urlmatch.cc b/xr/backenddef/urlmatch.cc
@@ -0,0 +1,11 @@
+#include "backenddef"
+
+void BackendDef::urlmatch (string const &s) {
+ PROFILE("BackendDef::urlmatch");
+
+ url_match = (s == "" ? "." : s);
+ if (regcomp (&url_regex, url_match.c_str(),
+ REG_EXTENDED | REG_ICASE | REG_NOSUB))
+ throw Error("Url match specifier '" +
+ url_match + "' isn't a valid regular expression");
+}
diff --git a/xr/config/config b/xr/config/config
@@ -35,6 +35,12 @@ public:
int backends() const { return (blist.size()); }
+ /* PID file */
+ void pidfile(string const &s);
+ string const &pidfile() {
+ return pid_file;
+ }
+
/* Client timeouts */
unsigned client_read_timeout() const {
return (c_timeout);
@@ -93,9 +99,6 @@ public:
return (external_algorithm);
}
- string const &pidfile() const { return (pid_file); }
- void pidfile (string const &p);
-
bool prefixtimestamp() const { return (prefix_timestamp); }
void prefixtimestamp (bool p);
@@ -188,12 +191,14 @@ public:
private:
void setbackend (string const &s, string const &hostmatch,
+ string const &urlmatch,
BackendCheck const &bc);
void setwebinterface (string s);
void setserver (string s);
void setdispatchmode (string s);
int setinteger (string s) const;
+ static string pid_file;
static bool verbose_flag;
static int lport;
static Servertype styp;
@@ -213,7 +218,6 @@ private:
static bool replace_host_header;
static unsigned max_conn;
static string external_algorithm;
- static string pid_file;
static bool prefix_timestamp;
static vector<string> serverheaders;
static vector<struct in_addr> allowlist;
diff --git a/xr/config/parsecmdline.cc b/xr/config/parsecmdline.cc
@@ -15,8 +15,8 @@ void Config::parsecmdline (int ac, char **av) {
if (ac == 1)
throw Error("Bad command line '" + cmdline + "'\n" + USAGE);
-# define OPTSTRING "?a:A:B:b:c:CDd:E:e:fF:Gg:hH:Il:" \
- "m:M:nPp:Q:r:R:Ss:t:T:u:U:vVW:w:xXy:z:Z:"
+# define OPTSTRING "?a:A:B:b:c:CDd:E:e:fF:Gg:hH:Ij:l:" \
+ "m:M:nPQ:r:R:Ss:t:T:u:U:vVW:w:xXy:z:Z:"
# ifdef HAVE_GETOPT_LONG
static struct option longopts[] = {
{ "allow-from", required_argument, 0, 'a' },
@@ -36,12 +36,13 @@ void Config::parsecmdline (int ac, char **av) {
{ "help", no_argument, 0, 'h' },
{ "add-server-header", required_argument, 0, 'H' },
{ "replace-host-header", no_argument, 0, 'I' },
+ { "url-match", required_argument, 0, 'j' },
{ "log-traffic-dir", required_argument, 0, 'l' },
{ "max-connections", required_argument, 0, 'm' },
{ "host-match", required_argument, 0, 'M' },
{ "tryout", no_argument, 0, 'n' },
- { "prefix-timestamp", no_argument, 0, 'P' },
{ "pidfile", required_argument, 0, 'p' },
+ { "prefix-timestamp", no_argument, 0, 'P' },
{ "soft-maxconnrate", required_argument, 0, 'r' },
{ "quit-after", required_argument, 0, 'Q' },
{ "hard-maxconnrate", required_argument, 0, 'R' },
@@ -68,6 +69,7 @@ void Config::parsecmdline (int ac, char **av) {
bool backend_set = false;
bool tryout = false, wakeup_used = false;
string current_hostmatch = "";
+ string current_urlmatch = "";
BackendCheck current_backendcheck;
vector<string> parts;
@@ -85,7 +87,8 @@ void Config::parsecmdline (int ac, char **av) {
adddeny (optarg);
break;
case 'b':
- setbackend (optarg, current_hostmatch, current_backendcheck);
+ setbackend (optarg, current_hostmatch, current_urlmatch,
+ current_backendcheck);
backend_set = true;
break;
case 'B':
@@ -132,6 +135,9 @@ void Config::parsecmdline (int ac, char **av) {
case 'I':
replacehostheader(true);
break;
+ case 'j':
+ current_urlmatch = optarg;
+ break;
case 'l':
dumpdir (optarg);
break;
@@ -147,9 +153,6 @@ void Config::parsecmdline (int ac, char **av) {
case 'P':
prefix_timestamp = true;
break;
- case 'p':
- pid_file = optarg;
- break;
case 'Q':
quit_after = (unsigned) setinteger(optarg);
break;
diff --git a/xr/config/setbackend.cc b/xr/config/setbackend.cc
@@ -1,6 +1,7 @@
#include "config"
void Config::setbackend (string const &str, string const &host,
+ string const &url,
BackendCheck const &backend_check) {
vector<string> parts = str2parts (str, ':');
if (parts.size() < 2 || parts.size() > 4)
@@ -18,6 +19,7 @@ void Config::setbackend (string const &str, string const &host,
if (!bdp)
throw Error("Memory fault in Config::setbackend");
bdp->hostmatch(host);
+ bdp->urlmatch(url);
bdp->backendcheck(backend_check);
blist.push_back (*bdp);
delete bdp;
diff --git a/xr/etc/status-nosavebutton.xslt b/xr/etc/status-nosavebutton.xslt
@@ -722,7 +722,28 @@
<td>
<input type="text" size="8" name="sethostmatch{nr}"
id="sethostmatch{nr}" value="{hostmatch}"
- onchange="goto('/backend/{nr}/hostmatch/', 'sethostmatch{nr}');"/>
+ onchange="goto('/backend/{nr}/hostmatch/',
+ 'sethostmatch{nr}');"/>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>URL match</td>
+ <td>
+ <xsl:choose>
+ <xsl:when test="urlmatch = '.'">
+ any url request
+ </xsl:when>
+ <xsl:otherwise>
+ (. for any url)
+ </xsl:otherwise>
+ </xsl:choose>
+ </td>
+ <td>
+ <input type="text" size="8" name="seturlmatch{nr}"
+ id="seturlmatch{nr}" value="{urlmatch}"
+ onchange="goto('/backend/{nr}/urlmatch/',
+ 'seturlmatch{nr}');"/>
</td>
</tr>
</xsl:if>
diff --git a/xr/etc/usage.txt b/xr/etc/usage.txt
@@ -3,26 +3,65 @@ This is XR, a load balancer and failover utility for TCP/HTTP services.
Usage: xr [flags], where the flags may be the following (long versions
may not exist on your platform):
- -a MASK, --allow-from MASK
+ --add-server-header HDR, -H HDR
+ Inserts HDR into back end bound HTTP messages. The header value is
+ appended when a pre-existing header is present.
+ --add-x-forwarded-for, -x
+ Adds X-Forwarded-For with external IP address to back end streams in
+ HTTP messages.
+ --add-xr-version, -X
+ Adds an XR version header to client and back end streams in HTTP
+ messages.
+ --allow-from MASK, -a MASH
Allow only clients that match MASK. MASK is e.g. 192.168.255.255, which
would allow the class B network 192.168.*.*
- -A MASK, --deny-from MASK
- Deny clients that match MASK.
- -b ADDRESS:PORT[:MAX[:WEIGHT]], --backend ADDRESS:PORT[:MAX[:WEIGHT]]
+ --backend ADDRESS:PORT[:MAX[:WEIGHT]], -b ADDRESS:PORT[:MAX[:WEIGHT]]
Specifies a back end, use multiple -b... to specify several back ends.
- At least one -b... must be given. Specifier MAX is optional: when given,
- defines the maximum connections for the back end. WEIGHT is optional:
- when given, specifies the weight (bigger means better server, default 1).
- -B SIZE, --buffer-size SIZE
+ At least one backend must be given. Specifier MAX is optional:
+ when given, defines the maximum connections for the back end.
+ WEIGHT is optional: when given, specifies the weight (bigger
+ means better server, default 1)
+ --backend-check METHOD, -g METHOD
+ Defines how back ends are checked. This flag must be specified
+ PRIOR to defining back ends with -b... The checker will then
+ apply to all next back ends. Alternatives are:
+ connect:IP:PORT - successful TCP connects at IP:PORT indicate
+ that the back end is alive. When IP is not stated, the back
+ end's IP is assumed.
+ get:IP:PORT/URI - A HTTP GET is sent to IP:PORT/URI. When an
+ HTTP status 200 is seen, the back end is assumed alive. When
+ /URI is not given, then "/" is assumed.
+ external:PROGRAM - The PROGRAM is called with the arguments
+ "IP:PORT", availability as "available" or "unavailable", and
+ the number of connections. The program must echo 0 to indicate
+ that the back end is alive.
+ The default behavior is a TCP connect, to the back end's IP, at
+ the back end's port. Use "--backend-check connect::" to reset
+ previous flags to the default.
+ --backend-timeout SEC, -t SEC
+ Defines network read timeouts for back ends, default 30 sec. Use 0 to
+ prevent timing out. Use "--backend-timeout RSEC:WSEC" to
+ specify separate timeouts for reads and writes, default 30:3.
+ --buffer-size SIZE, -B size
Sets the network buffer size, default is 2048 (in bytes)
- -C, --close-sockets-fast
- Sockets are closed faster to avoid TIME_WAIT states.
- -c SEC, --checkup-interval SEC
+ --checkup-interval SEC, -c SEC
Defines the back end checking period. Each SEC seconds, every back end
is checked whether it is alive. Default is 0 (off).
- -D, --debug
- Sets debugging on, more verbosity on top of -v
- -d METHOD, --dispatch-mode METHOD
+ --client-timeout SEC, -T SEC
+ Defines network read timeouts for clients, default 30 sec. Use 0 to
+ prevent timing out. Use "--client-timeout RSEC:WSEC" to specify
+ separate timeouts for reads and writes, default 30:5
+ --close-sockets-fast, -C
+ Sockets are closed faster to avoid TIME_WAIT states.
+ --debug, -D
+ Sets debugging on, more verbosity on top of --verbose
+ --defer-time USEC, -u USEC
+ If a connection is going to be deferred due to hitting the "soft" rate
+ (see --soft-maxconnrate), then this option sets how long the deferral
+ will last, in microseconds. Default is 500000 (0.5 seconds).
+ --deny-from MASK, -A mask
+ Deny clients that match MASK.
+ --dispatch-mode METHOD, -d METHOD
Defines how to dispatch over back ends, the method may be:
f, first-available - first live back end gets all traffic
e:EXT, external:EXT - external program EXT is queried
@@ -46,129 +85,97 @@ may not exist on your platform):
end definition, eg. "10.1.1.1:80"; <b0-availablility> is "available" or
"unavailable", <b0-connections> is the nr. of connections. The program
must reply with a back end number (0..max) on stdout.
- -E PROGRAM, --hard-maxconn-excess PROGRAM
- When a client exceeds the hard maxconnection rate, PROGRAM is
- invoked with the client's IP as argument. The program may e.g.
- invoke iptables to block the offending IP.
- -e PROGRAM, --soft-maxconn-excess PROGRAM
- When a client exceeds the soft maxconnection rate, PROGRAM is
- invoked with the client's IP as argument.
- -F SEC, --dns-cache-timeout SEC
+ --dns-cache-timeout SEC, -F SEC
DNS results for back end hostnames are cached for SEC seconds.
The default is 3600 (1 hour). Use 0 to suppress.
- -f, --foreground
+ --foreground, -f
Suppresses forking/threading, only for debugging. Also suppresses
- wakeups (-w), checkups (-c) and the webinterface (-W).
- -g METHOD, --backend-check METHOD
- Defines how back ends are checked. This flag must be specified
- PRIOR to defining back ends with -b... The checker will then
- apply to all next back ends. Alternatives are:
- connect:IP:PORT - successful TCP connects at IP:PORT indicate
- that the back end is alive. When IP is not stated, the back
- end's IP is assumed.
- get:IP:PORT/URI - A HTTP GET is sent to IP:PORT/URI. When an
- HTTP status 200 is seen, the back end is assumed alive. When
- /URI is not given, then "/" is assumed.
- external:PROGRAM - The PROGRAM is called with the arguments
- "IP:PORT", availability as "available" or "unavailable", and
- the number of connections. The program must echo 0 to indicate
- that the back end is alive.
- The default behavior is a TCP connect, to the back end's IP, at
- the back end's port. Use "-g connect::" to reset previous flags
- to the default.
- -G, --remove-reservations
- In stored-ip algorithms, outstanding reservations for expected
- clients are removed when no more back ends are available.
- -h, -?, --help
+ wakeups (--wakeup-interval), checkups (--checkup-interval) and
+ the webinterface (--web-interface).
+ --hard-maxconn-excess PROGRAM, -E PROGRAM
+ When a client exceeds the hard maxconnection rate, PROGRAM is
+ invoked with the client's IP as argument. The program may e.g.
+ invoke iptables to block the offending IP.
+ --hard-maxconnrate MAXCONS, -R MAXCONS
+ Sets the "HARD" maximum average number of connections per IP allowed
+ within a given time period (see -U, --time-interval). If a
+ particular IP exceeds this number, then their connection is
+ immediately closed. Default is 0 (disabled). If both the
+ "soft" and "hard" rates are set, and the "hard" rate is lower
+ than the "soft" rate, then only the "hard" rate is obeyed.
+ --help, -?, -h
This text.
- -H HDR, --add-server-header HDR
- Inserts HDR into back end bound HTTP messages. The header value is
- appended when a pre-existing header is present.
- -I HDR, --replace-host-header HDR
- Inserts "Host: <backend>" into back end bound HTTP messages.
- Pre-existing Host headers are overwritten. The value of <backend> is
- the server name as in the setting of --backend (-b).
- -l DIR, --log-traffic-dir DIR
+ --host-match HOST, -M HOST
+ Subsequently stated backends only apply when clients request a
+ matching host. Only available when the server is in http mode.
+ --log-traffic-dir DIR, -l DIR
Log passing traffic with dumps in DIR. Only for debugging, slows
down the balancer.
- -m MAX, --max-connections MAX
+ --max-connections MAX, -m MAX
Sets the maximum number of connections to the balancer. Default is 0,
no maximum.
- -M HOST, --host-match HOST
- Subsequently stated backends only apply when clients request a
- matching host. Only available when the server is in http mode.
- -n, --tryout
- Validates all flags and stops; does not start the balancer.
- -P, --prefix-timestamp
+ --onend CMD, -Z CMD
+ Runs CMD after successful termination of a client. For the
+ arguments of CMD see -y.
+ --onfail CMD, -y CMD
+ Runs CMD when XR fails to connect to a back end. The arguments
+ to the command are: the client's IP address, and the back end address.
+ --onstart CMD, -z CMD
+ Runs CMD just before letting a back end handle a client's connection.
+ For the arguments of CMD see -y.
+ --pidfile FILE, -p FILE
+ FILE is written with the process id of XR upon startup, and
+ removed upon exit.
+ --prefix-timestamp, -P
Messages (verbose, debug, error etc.) are prefixed with a time stamp.
- -p FILE, --pidfile FILE
- FILE is written with the PID of XR upon startup
- -Q REQUESTS, --quit-after REQUESTS
+ --quit-after REQUESTS, -Q REQUESTS
Stops the balancer after REQUESTS hits. For debugging / loadtesting.
- -r MAXCONS, --soft-maxconnrate MAXCONS
+ --remove-reservations, -G
+ In stored-ip algorithms, outstanding reservations for expected
+ clients are removed when no more back ends are available.
+ --replace-host-header HDR, -I HDR
+ Inserts "Host: <backend>" into back end bound HTTP messages.
+ Pre-existing Host headers are overwritten. The value of <backend> is
+ the server name as in the setting of --backend (-b).
+ --server TYPE:IPADDRESS:PORT, -S TYPE:IPADDRESS:PORT
+ Specifies the server. TYPE is tcp or http or udp. IPADDRESS is the IP
+ address to listen to. PORT defines the TCP port to listen; when port
+ is 0, XR will listen to stdin (inetd-mode, not available for udp).
+ Default: tcp:0:10000 (TCP balancing, on all interfaces, via port 10000).
+ --soft-maxconn-excess PROGRAM, -e PROGRAM
+ When a client exceeds the soft maxconnection rate, PROGRAM is
+ invoked with the client's IP as argument.
+ --soft-maxconnrate MAXCONS, -r MAXCONS
Sets the "SOFT" maximum average number of connections per IP allowed
within a given time period (see -U, --time-interval). If a
particular IP exceeds this number, then their connection is
deferred (see -u, --defer-time). Default is 0 (disabled).
- -R MAXCONS, --hard-maxconnrate MAXCONS
- Sets the "HARD" maximum average number of connections per IP allowed
- within a given time period (see -U, --time-interval). If a
- particular IP exceeds this number, then their connection is
- immediately closed. Default is 0 (disabled). If both the
- "soft" and "hard" rates are set, and the "hard" rate is lower
- than the "soft" rate, then only the "hard" rate is obeyed.
- -S, --sticky-http
+ --sticky-http, -S
Enables sticky HTTP sessions by injecting XRTarget cookies into HTTP
- streams. Only effective with -s http:....
- -s TYPE:IPADDRESS:PORT, --server TYPE:IPADDRESS:PORT
- Specifies the server. TYPE is tcp or http or udp. IPADDRESS is the IP
- address to listen to. PORT defines the TCP port to listen; when port
- is 0, XR will listen to stdin (inetd-mode, not available for udp).
- Default: tcp:0:10000 (TCP balancing, on all interfaces, via port 10000).
- -t SEC, --backend-timeout SEC
- Defines network read timeouts for back ends, default 30 sec. Use 0 to
- prevent timing out. Use -t RSEC:WSEC to specify separate timeouts
- for reads and writes, default 30:3.
- -T SEC, --client-timeout SEC
- Defines network read timeouts for clients, default 30 sec. Use 0 to
- prevent timing out. Use -T RSEC:WSEC to specify separate
- timeouts for reads and writes, default 30:5
- -u USEC, --defer-time USEC
- If a connection is going to be deferred due to hitting the "soft" rate
- (see --soft-maxconnrate), then this option sets how long the deferral
- will last, in microseconds. Default is 500000 (0.5 seconds).
- -U SEC, --time-interval SEC
+ streams. Only effective with "--server http:...."
+ --time-interval SEC, -U SEC
If either --soft-maxconnrate or --hard-maxconnrate is specified, this
option allows you to specify the time period to which those numbers of
- connections apply. For example, "-r 200 -U 60" would trigger the "soft"
- limit on any IP attempting more than 200 connections in any 60 second
- period. Default is 1 (second).
- -v, --verbose
+ connections apply. For example,
+ "--soft-maxconnrate 200 --time-interval 60" would trigger the
+ "soft" limit on any IP attempting more than 200 connections in
+ any 60 second period. Default is 1 (second).
+ --tryout, -n
+ Validates all flags and stops; does not start the balancer.
+ --url-match URL, -j URL
+ Subsequently stated backends only apply when clients request a
+ matching URL. Only available when the server is in http mode.
+ --web-interface IP:PORT, -W IP:PORT
+ Starts a web interface on specified IP address and port.
+ --verbose, -v
Increases verbosity, default is silent operation.
- -V, --version
+ --version, -V
Shows the version info, and author/maintainer contacts (for reporting
bugs).
- -w SEC, --wakeup-interval SEC
+ --wakeup-interval SEC, -w SEC
Defines wakeup period (rechecking) in seconds, of unavailable back
ends. Default is 5. Use -w0 to suppress.
- -W IP:PORT, --web-interface IP:PORT
- Starts a web interface on specified IP address and port.
- -X, --add-xr-version
- Adds an XR version header to client and back end streams in HTTP
- messages.
- -x, --add-x-forwarded-for
- Adds X-Forwarded-For with external IP address to back end streams in
- HTTP messages.
- -y CMD, --onfail CMD
- Runs CMD when XR fails to connect to a back end. The arguments
- to the command are: the client's IP address, and the back end address.
- -z CMD, --onstart CMD
- Runs CMD just before letting a back end handle a client's connection.
- For the arguments of CMD see -y.
- -Z CMD, --onend CMD
- Runs CMD after successful termination of a client. For the
- arguments of CMD see -y.
XR's messages are sent to stderr. Invoke XR daemons using something like
-"xr -b ... [other flags] 2>&1 | logger &", or use xrctl.
+"xr --backend ... [other flags] 2>&1 | logger &", or use xrctl.
diff --git a/xr/httpbuffer/httpbuffer b/xr/httpbuffer/httpbuffer
@@ -21,6 +21,7 @@ public:
string headerval (string const &var);
string &firstline();
+ string url();
bool setversion(char v);
diff --git a/xr/httpbuffer/url.cc b/xr/httpbuffer/url.cc
@@ -0,0 +1,33 @@
+#include "httpbuffer"
+
+static string methods[] = {
+ "HEAD", "GET", "POST", "PUT", "DELETE", "TRACE", "OPTIONS", "CONNECT"
+};
+
+string Httpbuffer::url() {
+ if (firstline().empty())
+ return "";
+
+ // The first line must be a method, followed by the URL, followed
+ // by optional mush, as in: GET /index.html HTTP/1.1.
+ // Match the method first.
+ unsigned url_start = 0;
+ for (unsigned i = 0; i < sizeof(methods) / sizeof(string) ; i++)
+ if (firstline().substr(0, methods[i].size()) == methods[i]) {
+ url_start = methods[i].size();
+ break;
+ }
+ if (!url_start)
+ return "";
+ while (firstline()[url_start] == ' ' && url_start < firstline().size())
+ url_start++;
+
+ string ret;
+ for (unsigned i = url_start;
+ firstline()[i] != ' ' && i < firstline().size();
+ i++)
+ ret += firstline()[i];
+
+ debugmsg("URL of request: " + ret + "\n");
+ return ret;
+}
diff --git a/xr/webinterface/answer.cc b/xr/webinterface/answer.cc
@@ -4,8 +4,7 @@ static void stop_backend_thread(pthread_t id) {
Threadinfo info = Threadlist::info(id);
msg((Mstr("Stopping thread ") + id) +
(Mstr(" (backend socket ") + info.backendfd()) +
- (Mstr(", client socket ") + info.clientfd()) +
- ")\n");
+ (Mstr(", client socket ") + info.clientfd()) + ")\n");
socketclose(info.backendfd());
socketclose(info.clientfd());
Threadlist::deregister(id);
@@ -519,6 +518,16 @@ void Webinterface::answer(Httpbuffer req) {
return;
}
+ // /backend/NR/urlmatch/EXPRESSION
+ // /backend/NR/urlmatch
+ if (parts.size() == 4 &&
+ parts[0] == "backend" && parts[2] == "urlmatch") {
+ unsigned ind = backendindex(parts[1]);
+ balancer.backend(ind).urlmatch(parts[3]);
+ answer_status();
+ return;
+ }
+
// /backend/NR/up/BOOL
if (parts.size() == 4 && parts[0] == "backend" && parts[2] == "up") {
unsigned ind = backendindex(parts[1]);
diff --git a/xr/webinterface/answerstatus.cc b/xr/webinterface/answerstatus.cc
@@ -108,6 +108,7 @@ void Webinterface::answer_status() {
" <bytesserved>" << balancer.backend(i).bytesserved() << "</bytesserved>\n"
" <clientsserved>" << balancer.backend(i).clientsserved() << "</clientsserved>\n"
" <hostmatch>" << balancer.backend(i).hostmatch() << "</hostmatch>\n"
+ " <urlmatch>" << balancer.backend(i).urlmatch() << "</urlmatch>\n"
" <backendcheck>" << balancer.backend(i).backendcheck().setting() << "</backendcheck>\n"
" </backend>\n"
;
diff --git a/xrctl/xrctl b/xrctl/xrctl
@@ -28,6 +28,7 @@ my $default_wakeupinterval = 5;
my $default_checkupinterval = 0;
my $default_weight = 1;
my $default_hostmatch = '.';
+my $default_urlmatch = '.';
my $default_backendcheck = 'connect::';
my $default_timeinterval = 1;
my $default_hardmaxconnrate = 0;
@@ -588,7 +589,8 @@ sub xr_cmdarr {
}
# The <backend> blocks for this service
- my $last_hostmatch = $default_hostmatch;
+ my $last_hostmatch = $default_hostmatch;
+ my $last_urlmatch = $default_urlmatch;
my $last_backendcheck = $default_backendcheck;
for (my $i = 0; ; $i++) {
my $bp = xml_backendparser($sp, $i) or last;
@@ -600,7 +602,16 @@ sub xr_cmdarr {
} elsif ($hm eq '' and $last_hostmatch ne '') {
push (@cmd, '--host-match', $default_hostmatch);
}
- $last_hostmatch = $hm;
+ $last_hostmatch = $hm;
+
+ # Handle url match
+ my $um = $bp->data('urlmatch');
+ if ($um and $um ne $last_urlmatch) {
+ push (@cmd, '--url-match', $um);
+ } elsif ($um eq '' and $last_urlmatch ne '') {
+ push (@cmd, '--url-match', $default_urlmatch);
+ }
+ $last_urlmatch = $um;
# Handle back end checks
my $bc = $bp->data('backendcheck');