crossroads

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

commit 53074fbfbbc45d08674f1a7e490516a054d4a700
parent 431dae3c2eb57909fb781d6eef504e46797220ce
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:36:05 +0100

2.19

Diffstat:
MChangeLog | 31++++++++++++++++++++++---------
MMakefile | 18+++++++++++-------
Mdoc/xr.odt | 0
Mdoc/xr.pdf | 0
Mxr/DispatchAlgorithms/leastconn/target.cc | 2+-
Mxr/DispatchAlgorithms/roundrobin/target.cc | 2+-
Mxr/DispatchAlgorithms/storedip/target.cc | 5+++--
Axr/DispatchAlgorithms/weightedload/target.cc | 48++++++++++++++++++++++++++++++++++++++++++++++++
Axr/DispatchAlgorithms/weightedload/weightedload | 15+++++++++++++++
Mxr/Makefile | 6++++--
Mxr/backend/backend | 9++++++---
Mxr/backend/backend1.cc | 3++-
Mxr/backend/backend2.cc | 2+-
Mxr/config/config | 63++++++++++++++++++++++++++++++++++++++-------------------------
Mxr/config/config1.cc | 4++++
Mxr/config/parsecmdline.cc | 31++++++++++++++++++++++++-------
Mxr/config/setbackend.cc | 2+-
Mxr/config/setdispatcmode.cc | 6++++++
Mxr/dispatchmode/dispatchmode | 5+++--
Mxr/etc/Makefile.class | 4+++-
Mxr/etc/c-conf | 48++++++++++++++++++++++++++++++++++++++----------
Mxr/etc/status.xslt | 37+++++++++++++++++++++++++++----------
Mxr/etc/usage.txt | 28+++++++++++++++++++++++++++-
Mxr/sys/ipmatch.cc | 5+++--
Axr/sys/mtrand.cc | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mxr/sys/socketclose.cc | 2+-
Mxr/sys/sys | 3+++
Mxr/tcpdispatcher/execute.cc | 98++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mxr/tcpdispatcher/tcpdispatcher | 7++++---
Mxr/tcpdispatcher/tcpdispatcher1.cc | 3+++
Mxr/webinterface/answer.cc | 48+++++++++++++++++++++++++++++-------------------
Mxr/webinterface/answerblob.cc | 2+-
Mxr/webinterface/answerstatus.cc | 5+++--
Mxr/webinterface/execute.cc | 21+++++++++++++++------
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);