crossroads

Git mirror of https://crossroads.e-tunity.com/
git clone git://git.finwo.net/app/crossroads
Log | Files | Refs | LICENSE

commit 758ebe7b9fa345fc7f59590bf9755324c128a088
parent f0cb14bcd2b19ee5dd0abbc223c9d25581c8b4c1
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:38:19 +0100

2.58

Diffstat:
MChangeLog | 6++++++
MMakefile | 2+-
Mdoc/xr.odt | 0
Mdoc/xr.pdf | 0
Atest/ftp.xml | 35+++++++++++++++++++++++++++++++++++
Atest/urlmatch.xml | 32++++++++++++++++++++++++++++++++
Mxr/Dispatchers/httpdispatcher/dispatch.cc | 61++++++++++++++++++++++++++++++++++++++++---------------------
Mxr/backend/backend | 4++++
Mxr/backenddef/backenddef | 9++++++++-
Mxr/backenddef/hostmatch.cc | 6++----
Axr/backenddef/urlmatch.cc | 11+++++++++++
Mxr/config/config | 12++++++++----
Mxr/config/parsecmdline.cc | 17++++++++++-------
Mxr/config/setbackend.cc | 2++
Mxr/etc/status-nosavebutton.xslt | 23++++++++++++++++++++++-
Mxr/etc/usage.txt | 237+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mxr/httpbuffer/httpbuffer | 1+
Axr/httpbuffer/url.cc | 33+++++++++++++++++++++++++++++++++
Mxr/webinterface/answer.cc | 13+++++++++++--
Mxr/webinterface/answerstatus.cc | 1+
Mxrctl/xrctl | 15+++++++++++++--
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');