crossroads

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

commit 159a2b25ec8fc144a766d38c1bfab6dd9b788750
parent f76cdc37c38a3fa6ff316b44507bbdaa12cd0fd1
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:35:46 +0100

2.11

Diffstat:
MChangeLog | 4++++
MMakefile | 2+-
Mdoc/xr.odt | 0
Mdoc/xr.pdf | 0
Atest/status.xml | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/status.xslt | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mxr/DispatchAlgorithms/firstactive/target.cc | 3++-
Mxr/Makefile | 7++++++-
Mxr/backend/backend | 3+++
Mxr/balancer/balancer | 3++-
Mxr/balancer/init.cc | 6++++++
Mxr/config/config | 9+++++++++
Mxr/config/config1.cc | 3+++
Mxr/config/parsecmdline.cc | 6+++++-
Mxr/config/setbackend.cc | 11+----------
Mxr/config/setserver.cc | 10+---------
Axr/config/setwebinterface.cc | 12++++++++++++
Axr/etc/status.xslt | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mxr/etc/usage.txt | 2++
Mxr/httpbuffer/httpbuffer | 7+++++++
Axr/httpbuffer/requestmethod.cc | 12++++++++++++
Axr/httpbuffer/requesturi.cc | 6++++++
Mxr/servertype/servertype | 1+
Axr/servertype/typestr.cc | 10++++++++++
Axr/sys/str2parts.cc | 26++++++++++++++++++++++++++
Mxr/sys/sys | 1+
Axr/webinterface/answer.cc | 43+++++++++++++++++++++++++++++++++++++++++++
Axr/webinterface/answerblob.cc | 27+++++++++++++++++++++++++++
Axr/webinterface/answerstatus.cc | 39+++++++++++++++++++++++++++++++++++++++
Axr/webinterface/answerxslt.cc | 6++++++
Axr/webinterface/execute.cc | 30++++++++++++++++++++++++++++++
Axr/webinterface/serve.cc | 22++++++++++++++++++++++
Axr/webinterface/webinterface | 24++++++++++++++++++++++++
33 files changed, 610 insertions(+), 24 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,7 @@ +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. 2.10 [KK 2008-09-02] - Bugfix in host match mode. When a back end doesn't match anything, diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ # Top-level Makefile for XR # ------------------------- -VER = 2.10 +VER = 2.11 BINDIR = /usr/sbin TAR = /tmp/crossroads-$(VER).tar.gz AUTHOR = Karel Kubat <karel@kubat.nl> 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/status.xml b/test/status.xml @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/xsl" href="status.xslt"?> +<status> + <backend> + <address>localhost:3129</address> + <weight>1</weight> + <maxconnections>0</maxconnections> + <hostmatch></hostmatch> + <live>dead</live> + + <available>unavailable</available> + <connections>0</connections> + <bytesserved>00</bytesserved> + <clientsserved></clientsserved> + </backend> + <backend> + <address>aab-proxy-1:8080</address> + + <weight>1</weight> + <maxconnections>0</maxconnections> + <hostmatch></hostmatch> + <live>dead</live> + <available>unavailable</available> + <connections>0</connections> + + <bytesserved>00</bytesserved> + <clientsserved></clientsserved> + </backend> + <backend> + <address>aab-proxy-2:80</address> + <weight>1</weight> + <maxconnections>0</maxconnections> + + <hostmatch></hostmatch> + <live>dead</live> + <available>unavailable</available> + <connections>0</connections> + <bytesserved>00</bytesserved> + <clientsserved></clientsserved> + </backend> + + <backend> + <address>localhost:3128</address> + <weight>1</weight> + <maxconnections>0</maxconnections> + <hostmatch></hostmatch> + <live>alive</live> + <available>available</available> + + <connections>8</connections> + <bytesserved>1276168</bytesserved> + <clientsserved></clientsserved> + </backend> +</status> diff --git a/test/status.xslt b/test/status.xslt @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> +<xsl:output method="html"/> + +<xsl:template match="/"> + <html> + <head> + <title>Crossroads Status Overview</title> + <style type="text/css"> + h1 { + font-family: Verdana,Helvetica; + font-size: 12pt; + color: blue; + } + body { + font-family: Verdana,Helvetica; + font-size: 10pt; + } + td { + font-family: Verdana,Helvetica; + font-size: 10pt; + } + .backend { background-color: #f3f099; } + .info { font-size: 8pt; background-color: #ffff99; } + .footer { color: gray; } + </style> + </head> + <body> + <h1>XR Status Overview</h1> + <hr/> + <xsl:apply-templates/> + </body> + </html> +</xsl:template> + +<xsl:template match="/status"> + <table> + <xsl:apply-templates/> + </table> +</xsl:template> + +<xsl:template match="/status/backend"> + <tr> <td colspan="2"></td></tr> + <tr> + <td class="backend" colspan="2"> + <b> Back end <xsl:value-of select="address"/> </b> + </td> + </tr> + <tr> + <td class="info">Weight</td> + <td class="info"><xsl:value-of select="weight"/></td> + </tr> + <tr> + <td class="info">Max. connections</td> + <td class="info"><xsl:value-of select="maxconnections"/></td> + </tr> + <tr> + <td class="info">Host match</td> + <td class="info"><xsl:value-of select="hostmatch"/></td> + </tr> + <tr> + <td class="info">Health state</td> + <td class="info"><xsl:value-of select="live"/></td> + </tr> + <tr> + <td class="info">Availability</td> + <td class="info"><xsl:value-of select="available"/></td> + </tr> + <tr> + <td class="info">Connections</td> + <td class="info"><xsl:value-of select="connections"/></td> + </tr> + <tr> + <td class="info">Bytes served</td> + <td class="info"><xsl:value-of select="bytesserved"/></td> + </tr> + <tr> + <td class="info">Clients served</td> + <td class="info"><xsl:value-of select="clientsserved"/></td> + </tr> +</xsl:template> + +<xsl:template match="*"/> + +</xsl:stylesheet> + diff --git a/xr/DispatchAlgorithms/firstactive/target.cc b/xr/DispatchAlgorithms/firstactive/target.cc @@ -2,7 +2,8 @@ unsigned Firstactive::target(struct in_addr clientip, BackendVector const &targetlist) { - if (targetlist.size() == 0) + if ( targetlist.size() == 0 || + ! balancer.backend(targetlist[0]).available() ) throw static_cast<Error>("First-active algorithm: " "no available back ends"); return (targetlist[0]); diff --git a/xr/Makefile b/xr/Makefile @@ -26,7 +26,7 @@ $(BINDIR)/xr: $(BUILDDIR)/xr install $(TMPXR) $(BINDIR)/xr rm -f $(TMPXR) -subdirs: $(BUILDDIR)/usage.h +subdirs: $(BUILDDIR)/usage.h $(BUILDDIR)/status.xslt.h @echo 'About to build in class dirs: $(DIRS)' @for f in $(DIRS) ; do \ echo "Making: $$f"; \ @@ -42,6 +42,10 @@ subdirs: $(BUILDDIR)/usage.h ranlib $(LIB) $(BUILDDIR)/usage.h: etc/usage.txt etc/e-txt2c USAGE <etc/usage.txt >$(BUILDDIR)/usage.h + touch config/parsecmdline.cc +$(BUILDDIR)/status.xslt.h: etc/status.xslt + etc/e-txt2c XSLT <etc/status.xslt >$(BUILDDIR)/status.xslt.h + touch webinterface/answerxslt.cc $(BIN): $(BUILDDIR)/libxr.a $(CONF_CC) -g -o $(BIN) -L$(BUILDDIR) -lxr $(CONF_LIB) @@ -49,3 +53,4 @@ $(BIN): $(BUILDDIR)/libxr.a clean: rm -f $(BIN) $(LIB) core obj/*.o etc/usage.h $(BUILDDIR)/config.cache find . -name \*.bak -exec rm {} \; + diff --git a/xr/backend/backend b/xr/backend/backend @@ -27,8 +27,11 @@ public: string const &server() const { return (bdef.server()); } int port() const { return (bdef.port()); } unsigned maxconn() const { return (bdef.maxconn()); } + void maxconn (unsigned m) { bdef.maxconn(m); } string const &hostmatch() const { return (bdef.hostmatch()); } regex_t const &hostregex() const { return (bdef.hostregex()); } + unsigned weight() const { return (bdef.weight()); } + void weight (unsigned w) { bdef.weight(w); } unsigned adjustedweight() const { return (bdef.adjustedweight()); } unsigned connections() const { return (nconn); } double bytesserved() const { return (bytes_served); } diff --git a/xr/balancer/balancer b/xr/balancer/balancer @@ -9,9 +9,10 @@ #include "fdset/fdset" #include "error/error" -// Check ups of back ends +// Check ups of back ends and the web interface #include "Checkers/wakeupthread/wakeupthread" #include "Checkers/checkupthread/checkupthread" +#include "webinterface/webinterface" using namespace std; diff --git a/xr/balancer/init.cc b/xr/balancer/init.cc @@ -8,6 +8,12 @@ void Balancer::init() { else server_fd = 0; + // Start the web interface if requested. + if (config.usewebinterface()) { + Webinterface *w = new Webinterface(); + w->start(); + } + // Add workable back ends, display initial states. for (int i = 0; i < config.backends(); i++) addbackend (config.backend(i)); diff --git a/xr/config/config b/xr/config/config @@ -19,6 +19,7 @@ public: // Accessors bool verbose() const { return (verbose_flag); } 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()); } @@ -34,6 +35,7 @@ public: bool addxforwardedfor() const { return (add_x_forwarded_for); } bool stickyhttp() const { return (sticky_http); } unsigned maxconn() const { return (max_conn); } + void maxconn (unsigned m) const { max_conn = m; } string externalalgorithm() const { return (external_algorithm); } string pidfile() const { return (pid_file); } void pidfile (string p) { pid_file = p; } @@ -49,6 +51,9 @@ public: int ipstoretimeout() const { return (ipstore_timeout); } void ipstoretimeout(int t) { ipstore_timeout = t; } bool hostmatchused() const { return (hostmatch_used); } + bool usewebinterface() const { return use_webinterface; } + string webinterfaceip() const { return webinterface_ip; } + int webinterfaceport() const { return webinterface_port; } struct in_addr allow(unsigned n) const { return (allowlist[n]); @@ -66,6 +71,7 @@ public: private: void setbackend (string s, string hostmatch); + void setwebinterface (string s); void setserver (string s); void setdispatchmode (string s); int setinteger (string s) const; @@ -98,6 +104,9 @@ private: static bool fast_close; static int ipstore_timeout; static bool hostmatch_used; + static bool use_webinterface; + static string webinterface_ip; + static int webinterface_port; }; extern Config config; diff --git a/xr/config/config1.cc b/xr/config/config1.cc @@ -26,6 +26,9 @@ vector<struct in_addr> Config::denylist; bool Config::fast_close = false; int Config::ipstore_timeout; bool Config::hostmatch_used = false; +bool Config::use_webinterface = false; +string Config::webinterface_ip; +int Config::webinterface_port; Config::Config () { } diff --git a/xr/config/parsecmdline.cc b/xr/config/parsecmdline.cc @@ -16,7 +16,7 @@ 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:m:M:nPp:Ss:t:T:vVw:xX" +# define OPTSTRING "?a:A:B:b:c:Dd:fhH:m:M:nPp:Ss:t:T:vVW:w:xX" # ifdef HAVE_GETOPT_LONG static struct option longopts[] = { { "allow-from", required_argument, 0, 'a' }, @@ -42,6 +42,7 @@ void Config::parsecmdline (int ac, char **av) { { "verbose", no_argument, 0, 'v' }, { "version", no_argument, 0, 'V' }, { "wakeup-interval", required_argument, 0, 'w' }, + { "web-interface", required_argument, 0, 'W' }, { "add-xr-version", no_argument, 0, 'X' }, { "add-x-forwarded-for", no_argument, 0, 'x' }, { 0, 0, 0, 0 } @@ -152,6 +153,9 @@ void Config::parsecmdline (int ac, char **av) { << "\n"; # endif exit (0); + case 'W': + setwebinterface(optarg); + break; case 'w': wakeup = setinteger (optarg); break; diff --git a/xr/config/setbackend.cc b/xr/config/setbackend.cc @@ -1,16 +1,7 @@ #include "config" void Config::setbackend (string str, string host) { - vector<string> parts; - int pos; - - while ( (pos = str.find_first_of(":")) > 0) { - if (pos > 0) - parts.push_back (str.substr(0, pos)); - str = str.substr(pos + 1); - } - if (str.length() > 0) - parts.push_back (str); + vector<string> parts = str2parts (str, ':'); if (parts.size() < 2 || parts.size() > 4) throw static_cast<Error>("Bad back end specifier in '-b") + str + "', expected: SERVER:PORT or SERVER:PORT:MAXCONNECTIONS or " diff --git a/xr/config/setserver.cc b/xr/config/setserver.cc @@ -2,15 +2,7 @@ void Config::setserver (string str) { // Split into 3 parts - vector<string> parts; - int pos; - while ( (pos = str.find_first_of(":")) > 0) { - if (pos > 0) - parts.push_back (str.substr(0, pos)); - str = str.substr(pos + 1); - } - if (str.length() > 0) - parts.push_back (str); + vector<string> parts = str2parts (str, ':'); if (parts.size() != 3) throw static_cast<Error> ("Bad server specifier, expected: TYPE:IPADDRESS:PORT"); diff --git a/xr/config/setwebinterface.cc b/xr/config/setwebinterface.cc @@ -0,0 +1,12 @@ +#include "config" + +void Config::setwebinterface (string str) { + vector<string> parts = str2parts (str, ':'); + if (parts.size() != 2) + throw static_cast<Error>("Bad webinterface specifier in '-W") + + str + "', expected: IP:PORT"; + + use_webinterface = true; + webinterface_ip = parts[0]; + webinterface_port = setinteger(parts[1]); +} diff --git a/xr/etc/status.xslt b/xr/etc/status.xslt @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> +<xsl:output method="html"/> + +<xsl:template match="/"> + <html> + <head> + <title>XR Status Overview</title> + <style type="text/css"> + h1 { + font-family: Verdana,Helvetica; + font-size: 12pt; + color: blue; + } + body { + font-family: Verdana,Helvetica; + font-size: 10pt; + } + td { + font-family: Verdana,Helvetica; + font-size: 10pt; + } + input { + font-size: 8pt; + } + .server { background-color: #f3f399; } + .backend { background-color: #f3f099; } + .info { font-size: 8pt; background-color: #ffff99; } + .footer { color: gray; } + </style> + <script type="text/javascript"> + function goto(uri, input) { + var el = document.getElementById(input); + if (el) { + var value = el.value; + if (value != "") + document.location = uri + value; + } + } + </script> + </head> + <body> + <h1>XR Status Overview</h1> + <hr/> + <xsl:apply-templates/> + </body> + </html> +</xsl:template> + +<xsl:template match="/status"> + <table> + <xsl:apply-templates/> + </table> +</xsl:template> + +<xsl:template match="/status/server"> + <tr> + <td class="server" colspan="3"> + <b>Server <xsl:value-of select="address"/> </b> + </td> + </tr> + <tr> + <td class="info"> Type </td> + <td class="info" colspan="3"> <xsl:value-of select="type"/> </td> + </tr> + <tr> + <td class="info"> Max. connections </td> + <td class="info"> + <xsl:choose> + <xsl:when test="maxconnections = 0"> + unlimited + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="maxconnections"/> + </xsl:otherwise> + </xsl:choose> + </td> + <td> + <input type="text" size="5" name="setservermaxcon" + id="setservermaxcon" value="" + onchange="goto('/server/maxconnections/', 'setservermaxcon');"/> + </td> + </tr> +</xsl:template> + +<xsl:template match="/status/backend"> + <tr> <td colspan="3"></td></tr> + <tr> + <td class="backend" colspan="3"> + <b> Back end <xsl:value-of select="address"/> </b> + </td> + </tr> + <tr> + <td class="info">Weight</td> + <td class="info"><xsl:value-of select="weight"/></td> + <td> + <input type="text" size="5" name="setbackendweight{nr}" + id="setbackendweight{nr}" value="" + onchange="goto('/backend/{nr}/weight/', 'setbackendweight{nr}');"/> + </td> + </tr> + <tr> + <td class="info">Max. connections</td> + <td class="info"> + <xsl:choose> + <xsl:when test="maxconnections = 0"> + unlimited + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="maxconnections"/> + </xsl:otherwise> + </xsl:choose> + </td> + <td> + <input type="text" size="5" name="setmaxconnections{nr}" + id="setmaxconnections{nr}" value="" + onchange="goto('/backend/{nr}/maxconnections/', 'setmaxconnections{nr}');"/> + </td> + </tr> + <xsl:if test="/server/type = http"> + <tr> + <td class="info">Host match</td> + <td class="info"><xsl:value-of select="hostmatch"/></td> + <td class="info"></td> + </tr> + </xsl:if> + <tr> + <td class="info">Health state</td> + <td class="info"><xsl:value-of select="live"/></td> + <td class="info"></td> + </tr> + <tr> + <td class="info">Availability</td> + <td class="info"><xsl:value-of select="available"/></td> + <td class="info"></td> + </tr> + <tr> + <td class="info">Connections</td> + <td class="info"><xsl:value-of select="connections"/></td> + <td class="info"></td> + </tr> + <tr> + <td class="info">Bytes served</td> + <td class="info"><xsl:value-of select="bytesserved"/></td> + <td class="info"></td> + </tr> + <tr> + <td class="info">Clients served</td> + <td class="info"><xsl:value-of select="clientsserved"/></td> + <td class="info"></td> + </tr> +</xsl:template> + +<xsl:template match="*"/> + +</xsl:stylesheet> + diff --git a/xr/etc/usage.txt b/xr/etc/usage.txt @@ -86,6 +86,8 @@ may not exist on your platform): -w SEC, --wakeup-interval 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. diff --git a/xr/httpbuffer/httpbuffer b/xr/httpbuffer/httpbuffer @@ -12,6 +12,11 @@ public: b_is_not_received, b_unknown, }; + enum RequestMethod { + m_get, + // More can be added ad lib, modify requestmethod.cc if you do + m_other, + }; Httpbuffer(); bool headersdone(); @@ -23,6 +28,8 @@ public: void addheader (string var, string val); void addheader (string h); string cookievalue (string var); + RequestMethod requestmethod() const; + string requesturi() const; private: unsigned findheader (string h); diff --git a/xr/httpbuffer/requestmethod.cc b/xr/httpbuffer/requestmethod.cc @@ -0,0 +1,12 @@ +#include "httpbuffer" + +Httpbuffer::RequestMethod Httpbuffer::requestmethod() const { + string first = firstline(); + debugmsg ("First line of http buffer: '" + first + "'\n"); + + if (!first.compare (0, 3, "GET")) + return (m_get); + + return (m_other); +} + diff --git a/xr/httpbuffer/requesturi.cc b/xr/httpbuffer/requesturi.cc @@ -0,0 +1,6 @@ +#include "httpbuffer" + +string Httpbuffer::requesturi() const { + vector<string> parts = str2parts (firstline(), ' '); + return (parts.size() >= 2 ? parts[1] : ""); +} diff --git a/xr/servertype/servertype b/xr/servertype/servertype @@ -20,6 +20,7 @@ public: } void type (string id); + string typestr() const; Type type() const { return (t); } diff --git a/xr/servertype/typestr.cc b/xr/servertype/typestr.cc @@ -0,0 +1,10 @@ +#include "servertype" + +string Servertype::typestr() const { + if (t == t_tcp) + return ("tcp"); + else if (t == t_http) + return ("http"); + else + return ("server type unknown"); +} diff --git a/xr/sys/str2parts.cc b/xr/sys/str2parts.cc @@ -0,0 +1,26 @@ +#include "sys" +#include "config/config" + +vector<string> str2parts (string const &s, char sep) { + string str = s; + int pos; + vector<string> parts; + + while ( (pos = str.find_first_of(sep)) >= 0) { + if (pos > 0) + parts.push_back (str.substr(0, pos)); + str = str.substr(pos + 1); + } + if (str.length() > 0) + parts.push_back (str); + + if (config.debug()) { + ostringstream o; + o << "String '" << s << "' split by '" << sep << "': "; + for (unsigned i = 0; i < parts.size(); i++) + o << " '" << parts[i] << "'"; + debugmsg (o.str() + "\n"); + } + + return (parts); +} diff --git a/xr/sys/sys b/xr/sys/sys @@ -51,6 +51,7 @@ int serversocket (string addr, int port, string description); string timestamp(time_t s = 0); bool ipmatch (struct in_addr addr, struct in_addr mask); void socketclose (int fd); +vector<string> str2parts (string const &s, char sep); #ifndef HAVE_INET_ATON int inet_aton (char const *name, struct in_addr *addr); diff --git a/xr/webinterface/answer.cc b/xr/webinterface/answer.cc @@ -0,0 +1,43 @@ +#include "webinterface" + +void Webinterface::answer(Httpbuffer req) { + if (req.requestmethod() != Httpbuffer::m_get) + throw static_cast<Error>("Only request method GET supported"); + + string uri = req.requesturi(); + if (uri == "/") + answer_status(); + else if (uri == "/xslt") + answer_xslt(); + else { + vector<string> parts = str2parts (uri, '/'); + unsigned ind, num; + + if (parts.size() == 3 && + parts[0] == "server" && parts[1] == "maxconnections" && + sscanf(parts[2].c_str(), "%u", &num) > 0) { + // /server/maxconnections/NUMBER + config.maxconn(num); + answer_status(); + } else if (parts.size() == 4 && + parts[0] == "backend" && + sscanf(parts[1].c_str(), "%u", &ind) > 0 && + ind < balancer.nbackends() && + parts[2] == "weight" && + sscanf(parts[3].c_str(), "%u", &num)) { + // /backend/NR/weight/NUMBER + balancer.backend(ind).weight(num); + answer_status(); + } else if (parts.size() == 4 && + parts[0] == "backend" && + sscanf(parts[1].c_str(), "%u", &ind) > 0 && + ind < balancer.nbackends() && + parts[2] == "maxconnections" && + sscanf(parts[3].c_str(), "%u", &num)) { + // /backend/NR/maxconnections/NUMBER + balancer.backend(ind).maxconn(num); + answer_status(); + } else + throw static_cast<Error>("No action for URI '") + uri; + } +} diff --git a/xr/webinterface/answerblob.cc b/xr/webinterface/answerblob.cc @@ -0,0 +1,27 @@ +#include "webinterface" + +void Webinterface::answer_blob (string const &blob) { + ostringstream cl; + cl << blob.size(); + string resp = static_cast<string> + ("HTTP/1.0 200 OK\r\n") + + "Content-Type: text/xml\r\n" + "Connection: close\r\n" + "Content-Length: " + cl.str() + "\r\n" + "\r\n" + + blob; + + unsigned totwritten = 0; + unsigned towrite = resp.size(); + while (totwritten < towrite) { + Fdset set (config.client_timeout()); + set.add (cfd); + if (set.writeable() == cfd) { + ssize_t nwritten = write (cfd, resp.c_str() + totwritten, + towrite - totwritten); + if (nwritten < 1) + throw static_cast<Error>("Write failed"); + totwritten += nwritten; + } + } +} diff --git a/xr/webinterface/answerstatus.cc b/xr/webinterface/answerstatus.cc @@ -0,0 +1,39 @@ +#include "webinterface" + +void Webinterface::answer_status() { + string xml = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<?xml-stylesheet type=\"text/xsl\" href=\"/xslt\"?>\n" + "<status>\n"; + + ostringstream o; + o << + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<?xml-stylesheet type=\"text/xsl\" href=\"/xslt\"?>\n" + "<status>\n" + " <server>\n" + " <address>" << config.sipaddr() << ":" << config.sport() << "</address>\n" + " <type>" << config.stypestr() << "</type>\n" + " <maxconnections>" << config.maxconn() << "</maxconnections>\n" + " </server>\n" + ; + + for (unsigned i = 0; i < balancer.nbackends(); i++) + o << + " <backend>\n" + " <nr>" << i << "</nr>\n" + " <address>" << balancer.backend(i).description() << "</address>\n" + " <weight>" << balancer.backend(i).weight() << "</weight>\n" + " <maxconnections>" << balancer.backend(i).maxconn() << "</maxconnections>\n" + " <live>" << balancer.backend(i).livestr() << "</live>\n" + " <available>" << balancer.backend(i).availablestr() << "</available>\n" + " <connections>" << balancer.backend(i).connections() << "</connections>\n" + " <bytesserved>" << balancer.backend(i).bytesserved() << "</bytesserved>\n" + " <clientsserved>" << balancer.backend(i).clientsserved() << "</clientsserved>\n" + " </backend>\n" + ; + o << + "</status>\n"; + + answer_blob (o.str()); +} diff --git a/xr/webinterface/answerxslt.cc b/xr/webinterface/answerxslt.cc @@ -0,0 +1,6 @@ +#include "webinterface" +#include "../build/status.xslt.h" + +void Webinterface::answer_xslt() { + answer_blob (XSLT); +} diff --git a/xr/webinterface/execute.cc b/xr/webinterface/execute.cc @@ -0,0 +1,30 @@ +#include "webinterface" + +void Webinterface::execute() { + int sfd = serversocket (config.webinterfaceip(), config.webinterfaceport(), + "web interface"); + ostringstream o; + o << sfd; + msg ("Web interface listening on socket " + o.str() + "\n"); + + while (!balancer.terminate()) { + try { + Fdset fdset(0); + fdset.add (sfd); + if (fdset.readable() == sfd) { + int size; + struct sockaddr_in clname; + if ( (cfd = accept (sfd, (struct sockaddr *) &clname, + (socklen_t *)&size)) > 0 ) + serve (); + socketclose(cfd); + } + } catch (Error const &e) { + socketclose(cfd); + cerr << "Webinterface: " << e.what() << "\n"; + } + } + msg ("Web interface stopping.\n"); + socketclose(sfd); + shutdown (sfd, SHUT_RDWR); +} diff --git a/xr/webinterface/serve.cc b/xr/webinterface/serve.cc @@ -0,0 +1,22 @@ +#include "webinterface" + +void Webinterface::serve () { + ostringstream o; + o << cfd; + msg ("Webinterface serving request on client fd " + o.str() + "\n"); + + char databuf[config.buffersize()]; + Httpbuffer clientrequest; + do { + Fdset set (config.client_timeout()); + set.add(cfd); + int nread; + if (set.readable() != cfd || + ( nread = read(cfd, databuf, config.buffersize()) ) < 0) + throw static_cast<Error>("Read failure on fd ") + cfd; + clientrequest.add(databuf, nread); + } while (clientrequest.bodyreceived() == Httpbuffer::b_is_not_received); + msg ("Webinterface request: " + clientrequest.firstline() + "\n"); + + answer(clientrequest); +} diff --git a/xr/webinterface/webinterface b/xr/webinterface/webinterface @@ -0,0 +1,24 @@ +#ifndef _WEBINTERFACE_ +#define _WEBINTERFACE_ + +#include "sys/sys" +#include "ThreadsAndMutexes/thread/thread" +#include "fdset/fdset" +#include "httpbuffer/httpbuffer" +#include "balancer/balancer" + +class Webinterface: public Thread { +public: + void execute(); +private: + void serve(); + void answer(Httpbuffer r); + void answer_status(); + void answer_xslt(); + + void answer_blob (string const &b); + + int cfd; +}; + +#endif