commit 159a2b25ec8fc144a766d38c1bfab6dd9b788750
parent f76cdc37c38a3fa6ff316b44507bbdaa12cd0fd1
Author: finwo <finwo@pm.me>
Date: Sat, 3 Jan 2026 19:35:46 +0100
2.11
Diffstat:
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