commit 53074fbfbbc45d08674f1a7e490516a054d4a700
parent 431dae3c2eb57909fb781d6eef504e46797220ce
Author: finwo <finwo@pm.me>
Date: Sat, 3 Jan 2026 19:36:05 +0100
2.19
Diffstat:
34 files changed, 640 insertions(+), 121 deletions(-)
diff --git a/ChangeLog b/ChangeLog
@@ -1,12 +1,25 @@
-2.18 [KK 2008-10-08]
+2.19 [KK 2008-10-13]
+- Changed the TCP Dispatcher to allow for setting a maximum # connection
+ attempts per client, with options to either defer the connection or drop it.
+- Added a dispatch mdoe "weighted-load" for randomly picking a back end based
+ on the inverse of their respective load averages. (gem)
+- Verbose/debug options in the web interface are now rendered as
+ select boxes.
+- Web interface errors are rendered on the client as an HTML error
+ page.
+- Docs updated regarding web interface URI's.
+- Turned on compliler optimizing (had forgotten that before!)
+- Docs updated regarding web interface URI's.
+
+2.18 [KK 2008-10-08]
- Improved command line parsing, so that when --checkup-interval is
used, suppression of "--wakeup-interval 0" is no longer needed.
-2.17 [KK 2008-10-07]
+2.17 [KK 2008-10-07]
- Type sizes reported in "xr -V"
- Fixed nasty bug in sys/fdwrite.cc
-2.16 [KK 2008-10-05]
+2.16 [KK 2008-10-05]
- Enhanced web interface to show debug, verbose and traffic log states
- Altering parameters for the web interface get sent in encoded form
- Option --log-traffic was renamed to --log-traffic-dir for
@@ -27,11 +40,11 @@
- Fixed usage info for buffer size flag (should be -B, not -b).
- Implemented flag -l (--log-traffic).
-2.13 [KK 2008-09-17]
+2.13 [KK 2008-09-17]
- Porting issues resolved for older MacOSX (10.3, 10.4)
- Bugfix in XML emitting of web interface. Most browsers didn't even notice..
-2.12 [KK 2008-09-10]
+2.12 [KK 2008-09-10]
- Small code changes for g++ v3.x backward-compatibility support.
(Thanks Simon M.)
- Web interface: layout enhanced, more modification options
@@ -43,10 +56,10 @@
- Web interface returns an HTTP error page (status 500 only, no
content) during errors
-2.11 [KK 2008-09-04]
+2.11 [KK 2008-09-04]
- Bugfix in "first-active" dispatch mode. Previously XR would gobble
up fd's when no back end was available.
-- Web interface (first version) implemented.
+- Web interface (first version) implemented.
2.10 [KK 2008-09-02]
- Bugfix in host match mode. When a back end doesn't match anything,
@@ -68,7 +81,7 @@ I'd forgotten to include the 'P' into the set of allowed flags
2.07 [KK 2008-08-28]
Stupid bug in 2.06, sorry that 2.06 got out.. Fixed.
-2.06 [KK 2008-08-27]
+2.06 [KK 2008-08-27]
Upped c-conf to 1.14.
xrctl updated: 'ps' command format also suitable for SunOS.
New class Mutex implemented. Mutex-locks are now more fine-grained
@@ -86,7 +99,7 @@ Implemented dispatching algorithm "stored clent ip", in the variants
strict and lax.
Flag -n / --tryout implemented. Implemented in xrctl.
-2.03 [KK 2008-08-10]
+2.03 [KK 2008-08-10]
Updated docs regarding the mailing list.
Fixed verbose display upon accepting a client ("current back end
states").
diff --git a/Makefile b/Makefile
@@ -1,7 +1,7 @@
# Top-level Makefile for XR
# -------------------------
-VER = 2.18
+VER = 2.19
BINDIR = /usr/sbin
TAR = /tmp/crossroads-$(VER).tar.gz
AUTHOR = Karel Kubat <karel@kubat.nl>
@@ -11,11 +11,12 @@ BASE = $(shell pwd)
foo:
@echo
@echo 'Choose:'
- @echo ' make local - local program construction'
- @echo ' make install - installation to $(BINDIR)'
- @echo ' make clean - removal after local/install'
- @echo ' make tar - pack sources in an archive'
- @echo ' make commit - commit to repository (maintainer only)'
+ @echo ' make local - local program construction'
+ @echo ' make localprof - local, with profiling info'
+ @echo ' make install - installation to $(BINDIR)'
+ @echo ' make clean - removal after local/install'
+ @echo ' make tar - pack sources in an archive'
+ @echo ' make commit - commit to repository (maintainer only)'
@echo
local:
@@ -23,7 +24,10 @@ local:
xr/etc/gettools /usr/local/bin xr/etc c-conf e-ver
xr/etc/e-ver ChangeLog $(VER)
BASE=$(BASE) AUTHOR='$(AUTHOR)' MAINTAINER='$(MAINTAINER)' \
- VER='$(VER)' $(MAKE) -C xr
+ VER='$(VER)' PROF=$(PROF) $(MAKE) -C xr
+
+localprof:
+ PROF=-pg make local
install: local
mkdir -p $(BINDIR)
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/xr/DispatchAlgorithms/leastconn/target.cc b/xr/DispatchAlgorithms/leastconn/target.cc
@@ -3,7 +3,7 @@
unsigned Leastconn::target(struct in_addr clientip,
BackendVector const &targetlist) {
bool found = false;
- unsigned nconn, t;
+ unsigned nconn = 0, t = 0;
for (unsigned i = 0; i < targetlist.size(); i++) {
if (! balancer.backend(targetlist[i]).available())
diff --git a/xr/DispatchAlgorithms/roundrobin/target.cc b/xr/DispatchAlgorithms/roundrobin/target.cc
@@ -8,7 +8,7 @@ unsigned Roundrobin::target(struct in_addr clientip,
static int prev_run_index = -1;
- unsigned first_try_val;
+ unsigned first_try_val = 0;
bool first_try_set = false;
while (true) {
// See where we will start
diff --git a/xr/DispatchAlgorithms/storedip/target.cc b/xr/DispatchAlgorithms/storedip/target.cc
@@ -7,8 +7,9 @@ struct ClientData {
struct ClientDataCmp {
bool operator() (struct in_addr a, struct in_addr b) const {
- long la = *((long*)&a);
- long lb = *((long*)&b);
+ long la, lb;
+ memcpy (&la, &a, sizeof(long));
+ memcpy (&lb, &b, sizeof(long));
return (la - lb) < 0;
}
};
diff --git a/xr/DispatchAlgorithms/weightedload/target.cc b/xr/DispatchAlgorithms/weightedload/target.cc
@@ -0,0 +1,48 @@
+#include "weightedload"
+
+unsigned Weightedload::target(struct in_addr clientip,
+ BackendVector const &targetlist) {
+ // First loop thru and add up the weights.
+ double total_load = 0;
+ for (unsigned i = 0; i < targetlist.size(); i++) {
+ if (balancer.backend(targetlist[i]).loadavg() == 0)
+ total_load += 1 / 0.01;
+ else
+ total_load += 1 / balancer.backend(targetlist[i]).loadavg();
+ }
+
+ // Now pick a random number from 0 to total_load
+ double pick_load = total_load * mt_rand() * (1.0 / 4294967295.0); // 4294967295 = 2^32 - 1
+
+ if (config.verbose()) {
+ ostringstream o1, o2;
+ o1 << total_load;
+ o2 << pick_load;
+ msg ("Weighted by Load Average; load-range is " + o1.str() + ", and the " +
+ " selected load-range is " + o2.str() +
+ "\n");
+ }
+
+ // Now see which server that means!
+ total_load = 0;
+ for (unsigned i = 0; i < targetlist.size(); i++) {
+ if (balancer.backend(targetlist[i]).loadavg() == 0)
+ total_load += 1 / 0.01;
+ else
+ total_load += 1 / balancer.backend(targetlist[i]).loadavg();
+ if (total_load >= pick_load) {
+// if (config.verbose()) {
+// ostringstream o;
+// o << balancer.backend(targetlist[i]).loadavg();
+// msg ("Weighted by Load Average chose backend " +
+// (string)balancer.backend(i).description() + " which has a " +
+// "load average of " + o.str() + "\n");
+// }
+ return targetlist[i];
+ }
+ }
+
+
+ throw static_cast<Error>("Weighted-load algorithm: no available back ends ");
+ return targetlist[0]; // We need some kind of default...
+}
diff --git a/xr/DispatchAlgorithms/weightedload/weightedload b/xr/DispatchAlgorithms/weightedload/weightedload
@@ -0,0 +1,15 @@
+#ifndef _WEIGHTEDLOAD_
+#define _WEIGHTEDLOAD_
+
+#include "sys/sys"
+#include "error/error"
+#include "balancer/balancer"
+#include "DispatchAlgorithms/algorithm/algorithm"
+
+class Weightedload: public Algorithm {
+public:
+ unsigned target(struct in_addr clientip,
+ BackendVector const &targetlist);
+};
+
+#endif
diff --git a/xr/Makefile b/xr/Makefile
@@ -14,6 +14,7 @@ CONF_GETOPT_LONG = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \
libfunction getopt_long HAVE_GETOPT_LONG)
CONF_INET_ATON = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \
libfunction inet_aton HAVE_INET_ATON)
+CONF_OPTFLAGS = $(shell etc/c-conf optflags)
foo:
$(MAKE) subdirs
@@ -34,7 +35,7 @@ subdirs: $(BUILDDIR)/usage.h $(BUILDDIR)/status.xslt.h
AUTHOR='$(AUTHOR)' MAINTAINER='$(MAINTAINER)' \
CONF_CC='$(CONF_CC)' CONF_LIB='$(CONF_LIB)' \
CONF_GETOPT=$(CONF_GETOPT) CONF_GETOPT_LONG=$(CONF_GETOPT_LONG) \
- CONF_INET_ATON=$(CONF_INET_ATON) \
+ CONF_INET_ATON=$(CONF_INET_ATON) CONF_OPTFLAGS=$(CONF_OPTFLAGS) \
$(MAKE) -C $$f -f $(BASE)/xr/etc/Makefile.class \
|| exit 1; \
done
@@ -48,7 +49,8 @@ $(BUILDDIR)/status.xslt.h: etc/status.xslt
touch webinterface/answerxslt.cc
$(BIN): $(BUILDDIR)/libxr.a
- $(CONF_CC) -g -o $(BIN) -L$(BUILDDIR) -lxr $(CONF_LIB)
+ $(CONF_CC) $(PROF) $(CONF_OPTFLAGS) -g -o $(BIN) \
+ -L$(BUILDDIR) -lxr $(CONF_LIB)
clean:
rm -f $(BIN) $(LIB) core obj/*.o etc/usage.h $(BUILDDIR)/config.cache
diff --git a/xr/backend/backend b/xr/backend/backend
@@ -21,7 +21,7 @@ public:
string availablestr() const;
string livestr() const;
void live (bool state);
-
+
bool live() const { return (islive); };
int sock() const { return (clsocket); }
string const &server() const { return (bdef.server()); }
@@ -37,21 +37,24 @@ public:
unsigned connections() const { return (nconn); }
double bytesserved() const { return (bytes_served); }
unsigned clientsserved() const { return (totconn); }
+ double loadavg() const { return (loadaverage); }
+ void loadavg(double l) { loadaverage = l; }
void addbytes (unsigned n);
void startconnection();
void endconnection();
-
+
BackendDef const &backenddef() const {
return (bdef);
}
-private:
+private:
BackendDef bdef;
bool islive;
int clsocket;
unsigned nconn, totconn;
double bytes_served;
+ double loadaverage;
};
#endif
diff --git a/xr/backend/backend1.cc b/xr/backend/backend1.cc
@@ -1,5 +1,6 @@
#include "backend"
Backend::Backend () :
- islive(true), clsocket(-1), nconn(0), totconn(0), bytes_served(0) {
+ islive(true), clsocket(-1), nconn(0), totconn(0), bytes_served(0),
+ loadaverage(0.1) {
}
diff --git a/xr/backend/backend2.cc b/xr/backend/backend2.cc
@@ -2,5 +2,5 @@
Backend::Backend (BackendDef const &b) :
bdef(b), islive(true), clsocket(-1), nconn(0), totconn(0),
- bytes_served(0) {
+ bytes_served(0), loadaverage(0.1) {
}
diff --git a/xr/config/config b/xr/config/config
@@ -20,61 +20,61 @@ public:
// Accessors
bool verbose() const { return (verbose_flag); }
void verbose (bool v) { verbose_flag = v; }
-
+
bool debug() const { return (debug_flag); }
void debug (bool d) { debug_flag = d; }
-
+
Servertype::Type stype() const { return (styp.type()); }
string stypestr() const { return (styp.typestr()); }
string sipaddr() const { return (sip); }
int sport() const { return (lport); }
-
+
int backends() const { return (blist.size()); }
-
+
unsigned client_timeout() const { return (c_timeout); }
void client_timeout (unsigned c) { c_timeout = c; }
-
+
unsigned backend_timeout() const { return (b_timeout); }
void backend_timeout (unsigned b) { b_timeout = b; }
-
+
unsigned wakeupsec() const { return (wakeup); }
void wakeupsec (unsigned w) { wakeup = w; }
-
+
unsigned checkupsec() const { return (checkup); }
void checkupsec (unsigned w) { checkup = w; }
-
- unsigned buffersize() const { return (bufsize); }
+
+ unsigned buffersize() const { return (bufsize); }
void buffersize (unsigned b);
-
+
bool foregroundmode() const { return (foreground_mode); }
-
+
bool addxrversion() const { return (add_xr_version); }
void addxrversion (bool b);
-
+
bool addxforwardedfor() const { return (add_x_forwarded_for); }
void addxforwardedfor (bool b);
-
+
bool stickyhttp() const { return (sticky_http); }
void stickyhttp(bool b);
-
+
unsigned maxconn() const { return (max_conn); }
void maxconn (unsigned m);
-
+
string externalalgorithm() const { return (external_algorithm); }
-
+
string pidfile() const { return (pid_file); }
void pidfile (string const &p);
-
+
bool prefixtimestamp() const { return (prefix_timestamp); }
void prefixtimestamp (bool p);
-
+
bool fastclose() const { return (fast_close); }
void fastclose (bool f);
-
+
bool usewebinterface() const { return use_webinterface; }
string webinterfaceip() const { return webinterface_ip; }
int webinterfaceport() const { return webinterface_port; }
-
+
unsigned nserverheaders() const { return (serverheaders.size()); }
string serverheader (unsigned n) { return (serverheaders[n]); }
void addserverheader (string const &s);
@@ -83,9 +83,18 @@ public:
string dumpdir() const { return (dump_dir); }
void dumpdir (string s) { dump_dir = s; }
-
+
+ unsigned softmaxconnrate() const { return soft_maxconnrate; }
+ void softmaxconnrate(unsigned n) { soft_maxconnrate = n; }
+ unsigned hardmaxconnrate() const { return hard_maxconnrate; }
+ void hardmaxconnrate(unsigned n) { hard_maxconnrate = n; }
+ unsigned defertime() const { return defer_time; }
+ void defertime(unsigned n) { defer_time = n; }
+ unsigned connrate_time() const { return connrate_timeinterval; }
+ void connrate_time(unsigned n) { connrate_timeinterval = n; }
+
unsigned nallow() const { return (allowlist.size()); }
- unsigned ndeny() const { return (denylist.size()); }
+ unsigned ndeny() const { return (denylist.size()); }
int ipstoretimeout() const { return (ipstore_timeout); }
void ipstoretimeout(int t);
struct in_addr allow(unsigned n) const {
@@ -93,8 +102,8 @@ public:
}
struct in_addr deny(unsigned n) const {
return (denylist[n]);
- }
-
+ }
+
BackendDef const &backend (int i) const {
return (blist[i]);
}
@@ -113,7 +122,7 @@ private:
int setinteger (string s) const;
void addallow (string a);
void adddeny (string d);
-
+
static bool verbose_flag;
static int lport;
static Servertype styp;
@@ -143,6 +152,10 @@ private:
static string webinterface_ip;
static int webinterface_port;
static string dump_dir;
+ static unsigned soft_maxconnrate;
+ static unsigned hard_maxconnrate;
+ static unsigned defer_time;
+ static unsigned connrate_timeinterval;
};
extern Config config;
diff --git a/xr/config/config1.cc b/xr/config/config1.cc
@@ -29,6 +29,10 @@ bool Config::use_webinterface = false;
string Config::webinterface_ip;
int Config::webinterface_port;
string Config::dump_dir;
+unsigned Config::soft_maxconnrate = 0;
+unsigned Config::hard_maxconnrate = 0;
+unsigned Config::defer_time = 500000;
+unsigned Config::connrate_timeinterval = 1;
Config::Config () {
}
diff --git a/xr/config/parsecmdline.cc b/xr/config/parsecmdline.cc
@@ -16,8 +16,8 @@ void Config::parsecmdline (int ac, char **av) {
throw static_cast<Error>("Bad command line '") +
cmdline + "'\n" + USAGE;
-# define OPTSTRING "?a:A:B:b:c:Dd:fhH:l:m:M:nPp:Ss:t:T:vVW:w:xX"
-# ifdef HAVE_GETOPT_LONG
+# define OPTSTRING "?a:A:B:b:c:Dd:fhH:l:m:M:nPp:r:R:Ss:t:T:u:U:vVW:w:xX"
+# ifdef HAVE_GETOPT_LONG
static struct option longopts[] = {
{ "allow-from", required_argument, 0, 'a' },
{ "deny-from", required_argument, 0, 'A' },
@@ -46,20 +46,24 @@ void Config::parsecmdline (int ac, char **av) {
{ "web-interface", required_argument, 0, 'W' },
{ "add-xr-version", no_argument, 0, 'X' },
{ "add-x-forwarded-for", no_argument, 0, 'x' },
+ { "soft-maxconnrate", required_argument, 0, 'r' },
+ { "hard-maxconnrate", required_argument, 0, 'R' },
+ { "time-interval", required_argument, 0, 'u' },
+ { "defer-time", required_argument, 0, 'U' },
{ 0, 0, 0, 0 }
};
# endif
-
+
int opt;
bool backend_set = false;
bool tryout = false, wakeup_used = false;
string current_hostmatch = "";
-
+
# ifdef HAVE_GETOPT_LONG
while ( (opt = getopt_long (ac, av, OPTSTRING, longopts, 0)) > 0)
#else
while ( (opt = getopt (ac, av, OPTSTRING)) > 0 )
-#endif
+#endif
{
switch (opt) {
case 'a':
@@ -116,6 +120,12 @@ void Config::parsecmdline (int ac, char **av) {
case 'p':
pid_file = optarg;
break;
+ case 'r':
+ soft_maxconnrate = (unsigned) setinteger (optarg);
+ break;
+ case 'R':
+ hard_maxconnrate = (unsigned) setinteger (optarg);
+ break;
case 's':
setserver (optarg);
break;
@@ -128,6 +138,12 @@ void Config::parsecmdline (int ac, char **av) {
case 'T':
c_timeout = setinteger (optarg);
break;
+ case 'U':
+ defer_time = (unsigned) setinteger(optarg);
+ break;
+ case 'u':
+ connrate_timeinterval = (unsigned) setinteger (optarg);
+ break;
case 'v':
verbose_flag = true;
break;
@@ -136,6 +152,7 @@ void Config::parsecmdline (int ac, char **av) {
<< "Written by : " << AUTHOR << "\n"
<< "Maintained by : " << MAINTAINER << "\n"
<< "Compiled with : " << CONF_CC << "\n"
+ << "Optimization : " << CONF_OPTFLAGS << "\n"
<< "System : " << SYS << "\n"
<< "Libraries : " << CONF_LIB << "\n"
<< "Type sizes : ssize_t=" << sizeof(ssize_t)
@@ -148,7 +165,7 @@ void Config::parsecmdline (int ac, char **av) {
cout << "getopt.h : present\n";
# else
cout << "getopt.h : absent\n";
-# endif
+# endif
# ifdef HAVE_GETOPT_LONG
cout << "getopt_long() : present\n";
# else
@@ -159,7 +176,7 @@ void Config::parsecmdline (int ac, char **av) {
# else
cout << "INADDR_NONE : absent, defined to " << INADDR_NONE
<< "\n";
-# endif
+# endif
exit (0);
case 'W':
setwebinterface(optarg);
diff --git a/xr/config/setbackend.cc b/xr/config/setbackend.cc
@@ -7,7 +7,7 @@ void Config::setbackend (string str, string host) {
"', expected: SERVER:PORT or SERVER:PORT:MAXCONNECTIONS or "
"SERVER:PORT:MAXCONNECTIONS:WEIGHT";
- BackendDef *bdp;
+ BackendDef *bdp = 0;
if (parts.size() == 2)
bdp = new BackendDef(parts[0], parts[1]);
else if (parts.size() == 3)
diff --git a/xr/config/setdispatcmode.cc b/xr/config/setdispatcmode.cc
@@ -17,6 +17,12 @@ void Config::setdispatchmode (string s) {
dmode.mode (Dispatchmode::m_leastconn);
else if (s == "r" || s == "round-robin")
dmode.mode (Dispatchmode::m_roundrobin);
+ else if (s == "L" || s == "weighted-load" ) {
+ timeval t1;
+ gettimeofday(&t1, NULL);
+ mt_srand(t1.tv_sec * t1.tv_usec);
+ dmode.mode (Dispatchmode::m_weighted_load);
+ }
else if (s.substr(0, 2) == "s:") {
dmode.mode (Dispatchmode::m_strict_stored_ip);
ipstoretimeout(setinteger(s.substr(2)));
diff --git a/xr/dispatchmode/dispatchmode b/xr/dispatchmode/dispatchmode
@@ -7,7 +7,7 @@ using namespace std;
class Dispatchmode {
public:
-
+
enum Mode {
m_leastconn,
m_roundrobin,
@@ -17,11 +17,12 @@ public:
m_lax_hashed_ip,
m_strict_stored_ip,
m_lax_stored_ip,
+ m_weighted_load,
};
Dispatchmode() : mymode(m_leastconn) {
}
-
+
void mode (Mode m) { mymode = m; }
Mode mode() const { return (mymode); }
string modestr() const;
diff --git a/xr/etc/Makefile.class b/xr/etc/Makefile.class
@@ -7,9 +7,11 @@ class-compile: $(OBJ)
$(BASE)/xr/$(BUILDDIR)/$(DIR)_%.o: %.cc
@echo "Compiling: " `pwd` $<
- @$(CONF_CC) -DVER='"$(VER)"' -DAUTHOR='"$(AUTHOR)"' \
+ @$(CONF_CC) $(PROF) $(CONF_OPTFLAGS) \
+ -DVER='"$(VER)"' -DAUTHOR='"$(AUTHOR)"' \
-DMAINTAINER='"$(MAINTAINER)"' -DSYS='"$(SYS)"' -D$(SYS) \
-DCONF_CC='"$(CONF_CC)"' -DCONF_LIB='"$(CONF_LIB)"' \
+ -DCONF_OPTFLAGS='"$(CONF_OPTFLAGS)"' \
$(CONF_GETOPT) $(CONF_GETOPT_LONG) $(CONF_INET_ATON) \
-I$(BASE)/xr \
-c -g -Wall -o $@ $<
diff --git a/xr/etc/c-conf b/xr/etc/c-conf
@@ -4,7 +4,8 @@ use strict;
use Getopt::Std;
# Globals
-my $VER = "1.14";
+my $VER = "1.15";
+# 1.15 [KK 2008-10-12] Option "optflags" implemented.
# 1.14 [KK 2008-08-22] c-compiler and c++-compiler attempt to find by
# version, eg. '/opt/local/bin/g++-mp-4.2' is better
# than '/usr/bin/g++'
@@ -93,6 +94,7 @@ Usage:
library
$base [flags] c-compiler: Returns name of C compiler
$base [flags] c++-compiler: Returns name of C++ compiler
+ $base [flags] optflags: Returns fast-code optimization flags.
Optional flags:
-c CACHE: settings will be dynamically determined unless present in
@@ -432,7 +434,7 @@ ENDHELP
my $cc;
eval { $cc = find_c_compiler(); };
if ($@) {
- warn ($@);
+ warning ($@);
} else {
msg ("C compiler: '$cc'\n");
output ($cc);
@@ -459,6 +461,24 @@ ENDHELP
warning ("No C++ compiler found\n");
}
+# Get fast code optimization flags.
+sub optflags {
+ checkhelp <<"ENDHELP";
+'optflags' tries to determine optimization flags.
+E.g.: $base optflags
+ -> -O3
+ENDHELP
+ usage() if ($#_ > -1);
+ for my $optflag ('-fast', '-O3', '-O2') {
+ if (test_compile ("int main() {}\n", $optflag)) {
+ output ($optflag);
+ return;
+ }
+ }
+ warning ("No optimization flag found.");
+}
+
+
# Get the name for an SO.
sub so_name {
checkhelp <<"ENDHELP";
@@ -551,6 +571,7 @@ ENDHELP
}
sub test_compile {
+ my $sourcecode = shift;
my $cc = find_c_compiler();
# Create a temp .c file.
@@ -558,11 +579,16 @@ sub test_compile {
my $dst = "/tmp/$$.out";
open (my $of, ">$src")
or die ("Cannot write $src: $!\n");
- print $of (@_);
+ print $of ($sourcecode);
close ($of);
- my $cmd = "$cc $src -o $dst " .
+ my $cmd = "$cc ";
+ for my $flag (@_) {
+ $cmd .= "$flag ";
+ }
+ $cmd .= "$src -o $dst " .
cc_inc_flags() . ' ' . cc_libdir_flags() . ' ' . cc_lib_flags();
+ # print ($cmd, "\n");
my $ret = system ("$cmd >/dev/null 2>&1");
unlink ($src, $dst);
@@ -604,9 +630,9 @@ sub test_libfunction {
usage() if ($#_ != 1);
my ($func, $def) = @_;
- return (test_compile ("main () {\n",
- " void $func (void);\n",
- " $func();\n",
+ return (test_compile ("main () {\n" .
+ " void $func (void);\n" .
+ " $func();\n" .
"}\n"));
}
@@ -615,9 +641,9 @@ sub test_libvariable {
usage() if ($#_ != 1);
my ($var, $def) = @_;
- return (test_compile ("main () {\n",
- " extern int $var;\n",
- " $var = 42;\n",
+ return (test_compile ("main () {\n" .
+ " extern int $var;\n" .
+ " $var = 42;\n" .
"}\n"));
}
@@ -688,6 +714,8 @@ if ($action eq 'header') {
libfunction01 (@ARGV);
} elsif ($action eq 'libvariable01') {
libvariable01 (@ARGV);
+} elsif ($action eq 'optflags') {
+ optflags (@ARGV);
} else {
usage ();
}
diff --git a/xr/etc/status.xslt b/xr/etc/status.xslt
@@ -75,7 +75,7 @@
<tr>
<td>Dispatch mode</td>
<td colspan="3"> <xsl:value-of select="dispatchmode"/> </td>
- </tr>
+ </tr>
<tr>
<td>Checks</td>
<td>Wakeup interval</td>
@@ -158,12 +158,16 @@
<td>
<xsl:choose>
<xsl:when test="debugging/verbose = 0">
- <input type="text" size="8" name="verbose" id="verbose" value="off"
- onchange="goto('/server/verbose/', 'verbose');"/>
+ <select onchange="goto('/server/verbose/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
</xsl:when>
<xsl:otherwise>
- <input type="text" size="8" name="verbose" id="verbose" value="on"
- onchange="goto('/server/verbose/', 'verbose');"/>
+ <select onchange="goto('/server/verbose/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
</xsl:otherwise>
</xsl:choose>
</td>
@@ -174,12 +178,16 @@
<td>
<xsl:choose>
<xsl:when test="debugging/debug = 0">
- <input type="text" size="8" name="debug" id="debug" value="off"
- onchange="goto('/server/debug/', 'debug');"/>
+ <select onchange="goto('/server/debug/on', '');">
+ <option value="yes">yes</option>
+ <option value="no" selected="1">no</option>
+ </select>
</xsl:when>
<xsl:otherwise>
- <input type="text" size="8" name="debug" id="debug" value="on"
- onchange="goto('/server/debug/', 'debug');"/>
+ <select onchange="goto('/server/debug/off', '');">
+ <option value="yes" selected="1">yes</option>
+ <option value="no">no</option>
+ </select>
</xsl:otherwise>
</xsl:choose>
</td>
@@ -187,7 +195,7 @@
<tr>
<td></td>
<td colspan="2">Traffic log directory</td>
- <td>
+ <td>
<input type="text" size="8" name="logtrafficdir" id="logtrafficdir"
value="{debugging/logtrafficdir}"
onchange="goto('/server/logtrafficdir/', 'logtrafficdir');"/>
@@ -281,6 +289,15 @@
onchange="goto('/backend/{nr}/maxconnections/', 'setbackendmaxcon{nr}');"/>
</td>
</tr>
+ <tr>
+ <td></td>
+ <td colspan="2">load average</td>
+ <td>
+ <input type="text" size="8" name="setloadaverage{nr}"
+ id="setloadaverage{nr}" value="{loadavg}"
+ onchange="goto('/backend/{nr}/loadavg/', 'setloadaverage{nr}');"/>
+ </td>
+ </tr>
<xsl:if test="/status/server/type = 'http'">
<tr>
<td></td>
diff --git a/xr/etc/usage.txt b/xr/etc/usage.txt
@@ -32,12 +32,16 @@ may not exist on your platform):
connections when target back end is down
l, least-connections - back end with least connections is taken
r, round-robin - back ends take turns
+ L, weighted-load - randomly picks from back end with favor
+ given to backends with lower load average.
+ (NOTE: load average must be updated by the
+ backend, e.g. using the web interface).
s:SEC, strict-stored-ip:SEC - if client connected before within SEC
seconds, then the same backend is used.
Client is denied if that backend is down.
Else a new is found by least-connections.
S:SEC, lax-stored-ip:SEC - same as strict-stored-ip, but falls back
- to least-connections when a previously
+ to least-connections when a previously
used back end is down
Default method is l (least-connections). When external mode is selected,
program EXT is started with arguments <nbackends> <b0> <b0-availability>
@@ -67,6 +71,18 @@ may not exist on your platform):
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
+ -r, --soft-maxconnrate MAXCONS
+ Sets the "SOFT" maximum average number of connections per IP allowed
+ within a given time period (see --time-interval). If a particular IP
+ exceeds this number, then their connection is deferred (see
+ --defer-time). Defualt is 0 (disabled).
+ -R, --hard-maxconnrate MAXCONS
+ Sets the "HARD" maximum average number of connections per IP allowed
+ within a given time period (see --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
Enables sticky HTTP sessions by injecting XRTarget cookies into HTTP
streams. Only effective with -s http:....
@@ -81,6 +97,16 @@ may not exist on your platform):
-T SEC, --client-timeout SEC
Defines network timeouts for clients, default 30 sec. Use 0 to
prevent timing out.
+ -u, --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, --time-interval 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.
-v, --verbose
Increases verbosity, default is silent operation.
-V, --version
diff --git a/xr/sys/ipmatch.cc b/xr/sys/ipmatch.cc
@@ -2,8 +2,9 @@
#include "../config/config"
bool ipmatch (struct in_addr adr, struct in_addr mask) {
- long laddr = * ((long*)&adr);
- long lmask = * ((long*)&mask);
+ long laddr, lmask;
+ memcpy (&laddr, &adr, sizeof(long));
+ memcpy (&lmask, &mask, sizeof(long));
bool match = ( (laddr & lmask) == laddr );
if (config.debug())
diff --git a/xr/sys/mtrand.cc b/xr/sys/mtrand.cc
@@ -0,0 +1,198 @@
+/*
+ NOTE: All of the functions have been commented out, with two exceptions:
+ - init_genrand(seed) has been renamed to mt_srand(seed)
+ - genrand_int32(void) has been renamed to mt_rand(void)
+ The renaming was to resemble the naming of srand() and rand(). All other
+ functions were unnecessary, and as such commented out.
+*/
+
+
+
+/*
+ A C-program for MT19937, with initialization improved 2002/1/26.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
+
+ Before using, initialize the state by using init_genrand(seed)
+ or init_by_array(init_key, key_length).
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ Any feedback is very welcome.
+ http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+ email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+*/
+
+/* Period parameters */
+#define N 624
+#define M 397
+#define MATRIX_A 0x9908b0dfUL /* constant vector a */
+#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
+#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
+
+static unsigned long mt[N]; /* the array for the state vector */
+static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
+
+/* initializes mt[N] with a seed */
+void mt_srand(unsigned long s)
+{
+ mt[0]= s & 0xffffffffUL;
+ for (mti=1; mti<N; mti++) {
+ mt[mti] =
+ (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
+ /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
+ /* In the previous versions, MSBs of the seed affect */
+ /* only MSBs of the array mt[]. */
+ /* 2002/01/09 modified by Makoto Matsumoto */
+ mt[mti] &= 0xffffffffUL;
+ /* for >32 bit machines */
+ }
+}
+
+/* initialize by an array with array-length */
+/* init_key is the array for initializing keys */
+/* key_length is its length */
+/* slight change for C++, 2004/2/26 */
+// void init_by_array(unsigned long init_key[], int key_length)
+// {
+// int i, j, k;
+// init_genrand(19650218UL);
+// i=1; j=0;
+// k = (N>key_length ? N : key_length);
+// for (; k; k--) {
+// mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
+// + init_key[j] + j; /* non linear */
+// mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+// i++; j++;
+// if (i>=N) { mt[0] = mt[N-1]; i=1; }
+// if (j>=key_length) j=0;
+// }
+// for (k=N-1; k; k--) {
+// mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
+// - i; /* non linear */
+// mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+// i++;
+// if (i>=N) { mt[0] = mt[N-1]; i=1; }
+// }
+//
+// mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
+// }
+
+/* generates a random number on [0,0xffffffff]-interval */
+unsigned long mt_rand(void)
+{
+ unsigned long y;
+ static unsigned long mag01[2]={0x0UL, MATRIX_A};
+ /* mag01[x] = x * MATRIX_A for x=0,1 */
+
+ if (mti >= N) { /* generate N words at one time */
+ int kk;
+
+ if (mti == N+1) /* if init_genrand() has not been called, */
+ mt_srand(5489UL); /* a default initial seed is used */
+
+ for (kk=0;kk<N-M;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ for (;kk<N-1;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
+ mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
+
+ mti = 0;
+ }
+
+ y = mt[mti++];
+
+ /* Tempering */
+ y ^= (y >> 11);
+ y ^= (y << 7) & 0x9d2c5680UL;
+ y ^= (y << 15) & 0xefc60000UL;
+ y ^= (y >> 18);
+
+ return y;
+}
+
+/* generates a random number on [0,0x7fffffff]-interval */
+// long genrand_int31(void)
+// {
+// return (long)(genrand_int32()>>1);
+// }
+
+/* generates a random number on [0,1]-real-interval */
+// double genrand_real1(void)
+// {
+// return genrand_int32()*(1.0/4294967295.0);
+// /* divided by 2^32-1 */
+// }
+
+/* generates a random number on [0,1)-real-interval */
+// double genrand_real2(void)
+// {
+// return genrand_int32()*(1.0/4294967296.0);
+// /* divided by 2^32 */
+// }
+
+/* generates a random number on (0,1)-real-interval */
+// double genrand_real3(void)
+// {
+// return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0);
+// /* divided by 2^32 */
+// }
+
+/* generates a random number on [0,1) with 53-bit resolution*/
+// double genrand_res53(void)
+// {
+// unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6;
+// return(a*67108864.0+b)*(1.0/9007199254740992.0);
+// }
+/* These real versions are due to Isaku Wada, 2002/01/09 added */
+
+// int main(void)
+// {
+// int i;
+// unsigned long init[4]={0x123, 0x234, 0x345, 0x456}, length=4;
+// init_by_array(init, length);
+// printf("1000 outputs of genrand_int32()\n");
+// for (i=0; i<1000; i++) {
+// printf("%10lu ", genrand_int32());
+// if (i%5==4) printf("\n");
+// }
+// printf("\n1000 outputs of genrand_real2()\n");
+// for (i=0; i<1000; i++) {
+// printf("%10.8f ", genrand_real2());
+// if (i%5==4) printf("\n");
+// }
+// return 0;
+// }
diff --git a/xr/sys/socketclose.cc b/xr/sys/socketclose.cc
@@ -7,7 +7,7 @@ void socketclose (int fd) {
o << fd;
debugmsg ("Closing socket " + o.str() + "\n");
}
-
+
if (config.fastclose()) {
struct linger l;
l.l_onoff = 1;
diff --git a/xr/sys/sys b/xr/sys/sys
@@ -39,6 +39,7 @@
#include <sstream>
#include <string>
#include <vector>
+#include <queue>
/* Generic functions */
@@ -54,6 +55,8 @@ void socketclose (int fd);
vector<string> str2parts (string const &s, char sep);
void fdwrite (int fd, int timeout, char const *buf, unsigned buflen);
void warnmsg (string const &s);
+void mt_srand(unsigned long s);
+unsigned long mt_rand(void);
#ifndef HAVE_INET_ATON
int inet_aton (char const *name, struct in_addr *addr);
diff --git a/xr/tcpdispatcher/execute.cc b/xr/tcpdispatcher/execute.cc
@@ -1,10 +1,102 @@
#include "tcpdispatcher"
+static map <unsigned long, queue <time_t> > accesslog;
+static time_t accesslog_lastclean = 0;
+
void TcpDispatcher::execute() {
ostringstream o;
o << clientfd();
msg ("Dispatch request for client fd " + o.str() + "\n");
+ /* Check 'softmaxconnrate' and 'hardmaxconnrate' now! */
+ // Descend into this block if connrate_time() is set, AND
+ // either hardmaxconnrate() is set,
+ // or both softmaxconnrate() and defertime() are set.
+ if (config.connrate_time() && (config.hardmaxconnrate()
+ || (config.softmaxconnrate() && config.defertime()))) {
+ time_t now, min_ts;
+ now = time(0);
+ min_ts = now - config.connrate_time();
+ unsigned max_conns = max(config.hardmaxconnrate(),
+ config.softmaxconnrate());
+
+ Mutex::lock (&accesslog[client_ip.s_addr]);
+ accesslog[client_ip.s_addr].push(now);
+ Mutex::unlock (&accesslog[client_ip.s_addr]);
+
+ if (accesslog_lastclean < min_ts) {
+ // Clean the entire access log, it's been a while...
+
+ Mutex::lock(&accesslog_lastclean);
+ accesslog_lastclean = now;
+ Mutex::unlock(&accesslog_lastclean);
+
+ for ( map<unsigned long, queue <time_t> >::iterator i=accesslog.begin();
+ i != accesslog.end();
+ i++ ) {
+
+ if (accesslog[i->first].back() < min_ts) {
+ // This IP hasn't made ANY connections in a while -- erase!
+ accesslog.erase(i);
+ } else {
+ // Keep popping off this IP's oldest connection until we
+ // have only "recent" connections left.
+ Mutex::lock(&accesslog[i->first]);
+ while ( accesslog[i->first].front() < min_ts
+ || accesslog[i->first].size() > max_conns ) {
+ accesslog[i->first].pop();
+ }
+ Mutex::unlock(&accesslog[i->first]);
+ }
+ }
+
+ } else {
+ // The "big log" doesn't need to be fully cleaned, but this particular
+ // IP should be!
+ Mutex::lock(&accesslog[client_ip.s_addr]);
+ while ( accesslog[client_ip.s_addr].front() < min_ts
+ || accesslog[client_ip.s_addr].size() > max_conns ) {
+ accesslog[client_ip.s_addr].pop();
+ }
+ Mutex::unlock(&accesslog[client_ip.s_addr]);
+ }
+
+
+
+ if ( config.hardmaxconnrate()
+ && accesslog[client_ip.s_addr].size() >= config.hardmaxconnrate() ) {
+ // This IP has violated the "HARD" limit! Reject the connection
+ ostringstream oa, om, ot;
+ oa << accesslog[client_ip.s_addr].size();
+ om << config.hardmaxconnrate();
+ ot << config.connrate_time();
+ cerr << "WARNING: Client " << inet_ntoa(client_ip) << " has hit " <<
+ "the HARD maximum number of connections (" << om.str() <<
+ " connections in " << ot.str() << " seconds; " << oa.str() <<
+ " connections recorded). This client's connection is being " <<
+ "refused.\n";
+ socketclose(clientfd());
+ return;
+ } else if (config.softmaxconnrate()
+ && accesslog[client_ip.s_addr].size() >= config.softmaxconnrate() ) {
+ // This IP has violated the "SOFT" Limit. Go to sleep for a while.
+ ostringstream oa, od, om, ot;
+ oa << accesslog[client_ip.s_addr].size();
+ od << config.defertime();
+ om << config.softmaxconnrate();
+ ot << config.connrate_time();
+ cerr << "WARNING: Client " << inet_ntoa(client_ip) << " has hit " <<
+ "the SOFT maximum number of connections (" << om.str() <<
+ " connections in " << ot.str() << " seconds; " << oa.str() <<
+ " connections recorded). This client's connection is being " <<
+ "deferred for " << od.str() <<
+ " microseconds.\n";
+ usleep(config.defertime());
+ }
+ }
+
+
+
try {
dispatch();
} catch (Error const &e) {
@@ -20,7 +112,7 @@ void TcpDispatcher::execute() {
msg ("Dispatching client fd " + co.str() + " to " +
balancer.backend(target_backend).description() +
", fd " + bo.str() + "\n");
-
+
balancer.backend(target_backend).startconnection();
try {
@@ -30,10 +122,10 @@ void TcpDispatcher::execute() {
}
balancer.backend(target_backend).endconnection();
-
+
socketclose (clientfd());
socketclose (backendfd());
msg ("Done dispatching client fd " + co.str() + " at " +
balancer.backend(target_backend).description() + "\n");
-
+
}
diff --git a/xr/tcpdispatcher/tcpdispatcher b/xr/tcpdispatcher/tcpdispatcher
@@ -15,13 +15,14 @@
#include "DispatchAlgorithms/external/external"
#include "DispatchAlgorithms/hashedip/hashedip"
#include "DispatchAlgorithms/storedip/storedip"
+#include "DispatchAlgorithms/weightedload/weightedload"
class TcpDispatcher: public Thread {
public:
TcpDispatcher (int fd, struct in_addr ip);
virtual ~TcpDispatcher();
-
+
virtual void execute();
virtual void dispatch();
@@ -39,9 +40,9 @@ public:
unsigned databufsize() const { return data_bufsz; }
BackendVector const &targetlist() const { return target_list; }
void targetlist (BackendVector t) { target_list = t; }
-
+
unsigned readchunk (int src);
-
+
private:
string printable (char ch) const;
struct in_addr client_ip;
diff --git a/xr/tcpdispatcher/tcpdispatcher1.cc b/xr/tcpdispatcher/tcpdispatcher1.cc
@@ -26,6 +26,9 @@ TcpDispatcher::TcpDispatcher(int cfd, struct in_addr cip):
case Dispatchmode::m_lax_stored_ip:
algorithm = new StoredIp;
break;
+ case Dispatchmode::m_weighted_load:
+ algorithm = new Weightedload;
+ break;
case Dispatchmode::m_leastconn:
default:
algorithm = new Leastconn;
diff --git a/xr/webinterface/answer.cc b/xr/webinterface/answer.cc
@@ -8,6 +8,14 @@ static unsigned str2uns (string const &s, string const &desc) {
return (ret);
}
+static double str2dbl (string const &s, string const &desc) {
+ double ret;
+
+ if (sscanf (s.c_str(), "%lf", &ret) < 0)
+ throw static_cast<Error>("Bad ") + desc;
+ return (ret);
+}
+
static unsigned backendindex (string const &s) {
unsigned ret;
@@ -38,7 +46,7 @@ bool str2bool (string const &s, string const &desc) {
ret = false;
else
throw static_cast<Error>("Bad ") + desc + " switch '" + s + "'";
-
+
return (ret);
}
@@ -64,7 +72,7 @@ string decode (string const &s) {
}
// debugmsg ("Decoded: '" + s + "' into '" + ret + "'\n");
-
+
return (ret);
}
@@ -159,26 +167,18 @@ void Webinterface::answer(Httpbuffer req) {
return;
}
- // /server/verbose
- // /server/verbose/VALUE
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "verbose") ) {
- if (parts.size() == 2 || parts[2] == "off" || parts[2] == "0")
- config.verbose(false);
- else
- config.verbose(true);
+ // /server/verbose/BOOLEAN
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "verbose") {
+ config.verbose(str2bool(parts[2], "verbose"));
answer_status();
return;
}
- // /server/debug
- // /server/debug/VALUE
- if ( (parts.size() == 2 || parts.size() == 3) &&
- (parts[0] == "server" && parts[1] == "debug") ) {
- if (parts.size() == 2 || parts[2] == "off" || parts[2] == "0")
- config.debug(false);
- else
- config.debug(true);
+ // /server/debug/VERBOSE
+ if (parts.size() == 3 &&
+ parts[0] == "server" && parts[1] == "debug") {
+ config.debug(str2bool(parts[2], "debug"));
answer_status();
return;
}
@@ -248,7 +248,7 @@ void Webinterface::answer(Httpbuffer req) {
}
// /backend/NR/weight/NUMBER
- if (parts.size() == 4 &&
+ if (parts.size() == 4 &&
parts[0] == "backend" && parts[2] == "weight") {
unsigned ind = backendindex(parts[1]);
unsigned num = str2uns (parts[3], "back end weight");
@@ -269,6 +269,16 @@ void Webinterface::answer(Httpbuffer req) {
return;
}
+ // /backend/NR/loadavg/FLOAT
+ if (parts.size() == 4 &&
+ parts[0] == "backend" && parts[2] == "loadavg") {
+ unsigned ind = backendindex(parts[1]);
+ double fnum = str2dbl (parts[3], "back end loadavg");
+ balancer.backend(ind).loadavg(fnum);
+ answer_status();
+ return;
+ }
+
// /backend/NR/hostmatch/EXPRESSION
// /backend/NR/hostmatch
if ( (parts.size() == 3 || parts.size() == 4) &&
diff --git a/xr/webinterface/answerblob.cc b/xr/webinterface/answerblob.cc
@@ -10,5 +10,5 @@ void Webinterface::answer_blob (string const &blob) {
"Content-Length: " + cl.str() + "\r\n"
"\r\n" +
blob;
- fdwrite (cfd, config.client_timeout(), resp.c_str(), resp.size() - 1);
+ fdwrite (cfd, config.client_timeout(), resp.c_str(), resp.size());
}
diff --git a/xr/webinterface/answerstatus.cc b/xr/webinterface/answerstatus.cc
@@ -41,7 +41,7 @@ void Webinterface::answer_status() {
" <header>" << config.serverheader(i) << "</header>\n"
" </serverheader>\n"
;
- o <<
+ o <<
" </serverheaders>\n"
" </http>\n"
" </server>\n"
@@ -54,6 +54,7 @@ void Webinterface::answer_status() {
" <address>" << balancer.backend(i).description() << "</address>\n"
" <weight>" << balancer.backend(i).weight() << "</weight>\n"
" <maxconnections>" << balancer.backend(i).maxconn() << "</maxconnections>\n"
+ " <loadavg>" << balancer.backend(i).loadavg() << "</loadavg>\n"
" <live>" << balancer.backend(i).livestr() << "</live>\n"
" <available>" << balancer.backend(i).availablestr() << "</available>\n"
" <connections>" << balancer.backend(i).connections() << "</connections>\n"
@@ -64,6 +65,6 @@ void Webinterface::answer_status() {
;
o <<
"</status>\n\n";
-
+
answer_blob (o.str());
}
diff --git a/xr/webinterface/execute.cc b/xr/webinterface/execute.cc
@@ -36,13 +36,22 @@ void Webinterface::execute() {
}
} catch (Error const &e) {
cerr << e.what() << " (webinterface)\n";
- string err = static_cast<string>
- ("HTTP/1.0 500 Server Error\r\n") +
- "X-Reason: " + e.what() + "\r\n"
- "Content-Length: 0\r\n"
- "\r\n";
+ ostringstream m;
+ m <<
+ "<h1>Web interface error</h1>\n"
+ "XR's web interface could not handle your request.<p/>\n"
+ "<i>" << e.what() << "</i>\n";
+ ostringstream o;
+ o <<
+ "HTTP/1.0 500 Server Error\r\n"
+ "X-Reason: " << e.what() << "\r\n"
+ "Content-Length: " << m.str().length() << "\r\n"
+ "\r\n" <<
+ m.str();
+
try {
- fdwrite(cfd, config.client_timeout(), err.c_str(), err.size());
+ fdwrite(cfd, config.client_timeout(),
+ o.str().c_str(), o.str().length());
} catch (...) {
}
socketclose(cfd);