crossroads

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

commit 0b9caf29b3c486b8429435b39efedcacafeb94b2
parent 34ac55e5fe559d5a0bce225a0e4469bc15c2a7e8
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:37:14 +0100

2.43

Diffstat:
MChangeLog | 6++++++
MMakefile | 2+-
Mdoc/xr.odt | 0
Mdoc/xr.pdf | 0
Mtest/sampleconf.xml | 2++
Mxr/config/config | 4++++
Mxr/config/config1.cc | 1+
Mxr/config/parsecmdline.cc | 83+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mxr/etc/status.xslt | 39+++++++++++++++++++++++++++++++++++++--
Mxr/etc/usage.txt | 7++++++-
Mxr/httpbuffer/addheader.cc | 5++++-
Mxr/httpbuffer/addheader1.cc | 6+++++-
Mxr/httpbuffer/headerval.cc | 16++++++++--------
Mxr/httpbuffer/httpbuffer | 13++++++++-----
Axr/httpbuffer/replaceheader1.cc | 17+++++++++++++++++
Axr/httpbuffer/replaceheader2.cc | 16++++++++++++++++
Mxr/httpbuffer/setheader.cc | 13++++++++-----
Mxr/httpbuffer/setheader1.cc | 2+-
Mxr/httpdispatcher/handle.cc | 14++++++++++++++
Mxr/netbuffer/netbuffer | 1+
Axr/netbuffer/printable1.cc | 9+++++++++
Mxr/netbuffer/removeat.cc | 4++--
Mxr/webinterface/answer.cc | 8++++++++
Mxr/webinterface/answerstatus.cc | 39++++++++++++++++++++++++++++++---------
Mxrctl/xrctl | 3+++
25 files changed, 234 insertions(+), 76 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,9 @@ +2.43 [KK 2009-02-09] +- Added Httpbuffer::replaceheader() methods +- Added flag -I (to replace Host: headers), integrated in xrctl / + webinterface +- Webinterface reports approx. # of open fd's and the limit (in activity) + 2.42 [KK 2009-01-28] - Bugfix in "xrctl generateconfig". The activity info introduced before (which the web interface now emites), confused xrctl. diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ # Top-level Makefile for XR # ------------------------- -VER = 2.42 +VER = 2.43 PREFIX = $(DESTDIR)/usr BINDIR = $(PREFIX)/sbin MANDIR = $(PREFIX)/share/man diff --git a/doc/xr.odt b/doc/xr.odt Binary files differ. diff --git a/doc/xr.pdf b/doc/xr.pdf Binary files differ. diff --git a/test/sampleconf.xml b/test/sampleconf.xml @@ -143,10 +143,12 @@ no header for the XR version, a header X-Forwarded-For: client-ip no sticky http sessions + modification of the Host: header to the back end server name two serverheaders to insert --> <addxrversion>off</addxrversion> <addxforwardedfor>on</addxforwardedfor> <stickyhttp>off</stickyhttp> + <replacehostheader>on</replacehostheader> <serverheaders> <header>MyFirstHeader: Whatever</header> <header>MySecondHeader: WhateverElse</header> diff --git a/xr/config/config b/xr/config/config @@ -61,6 +61,9 @@ public: bool stickyhttp() const { return (sticky_http); } void stickyhttp(bool b); + bool replacehostheader() const { return replace_host_header; } + void replacehostheader(bool s) { replace_host_header = s; } + unsigned maxconn() const { return (max_conn); } void maxconn (unsigned m); @@ -166,6 +169,7 @@ private: static bool debug_flag; static bool add_x_forwarded_for; static bool sticky_http; + static bool replace_host_header; static unsigned max_conn; static string external_algorithm; static string pid_file; diff --git a/xr/config/config1.cc b/xr/config/config1.cc @@ -16,6 +16,7 @@ bool Config::add_xr_version = false; bool Config::debug_flag = false; bool Config::add_x_forwarded_for = false; bool Config::sticky_http = false; +bool Config::replace_host_header = false; unsigned Config::max_conn = 0; string Config::external_algorithm = ""; string Config::pid_file = ""; diff --git a/xr/config/parsecmdline.cc b/xr/config/parsecmdline.cc @@ -13,50 +13,50 @@ void Config::parsecmdline (int ac, char **av) { } // Not a single argument? Usage. if (ac == 1) - throw static_cast<Error>("Bad command line '") + - cmdline + "'\n" + USAGE; + throw Error("Bad command line '" + cmdline + "'\n" + USAGE); -# define OPTSTRING "?a:A:B:b:c:CDd:E:e:fF:g:hH:l:" \ +# define OPTSTRING "?a:A:B:b:c:CDd:E:e:fF:g:hH:Il:" \ "m:M:nPp:Q: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' }, - { "backend", required_argument, 0, 'b' }, - { "buffer-size", required_argument, 0, 'B' }, - { "checkup-interval", required_argument, 0, 'c' }, - { "close-sockets-fast", no_argument, 0, 'C' }, - { "debug", no_argument, 0, 'D' }, - { "dispatch-mode", required_argument, 0, 'd' }, - { "hard-maxconn-excess", required_argument, 0, 'E' }, - { "soft-maxconn-excess", required_argument, 0, 'e' }, - { "dns-cache-timeout", required_argument, 0, 'F' }, - { "foreground", no_argument, 0, 'f' }, - { "backend-check", required_argument, 0, 'g' }, - { "help", no_argument, 0, 'h' }, - { "add-server-header", required_argument, 0, 'H' }, - { "log-traffic-dir", required_argument, 0, 'l' }, - { "max-connections", required_argument, 0, 'm' }, - { "host-match", required_argument, 0, 'M' }, - { "tryout", no_argument, 0, 'n' }, - { "prefix-timestamp", no_argument, 0, 'P' }, - { "pidfile", required_argument, 0, 'p' }, - { "soft-maxconnrate", required_argument, 0, 'r' }, - { "quit-after", required_argument, 0, 'Q' }, - { "hard-maxconnrate", required_argument, 0, 'R' }, - { "server", required_argument, 0, 's' }, - { "sticky-http", no_argument, 0, 'S' }, - { "backend-timeout", required_argument, 0, 't' }, - { "client-timeout", required_argument, 0, 'T' }, - { "time-interval", required_argument, 0, 'u' }, - { "defer-time", required_argument, 0, 'U' }, - { "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 } + { "allow-from", required_argument, 0, 'a' }, + { "deny-from", required_argument, 0, 'A' }, + { "backend", required_argument, 0, 'b' }, + { "buffer-size", required_argument, 0, 'B' }, + { "checkup-interval", required_argument, 0, 'c' }, + { "close-sockets-fast", no_argument, 0, 'C' }, + { "debug", no_argument, 0, 'D' }, + { "dispatch-mode", required_argument, 0, 'd' }, + { "hard-maxconn-excess", required_argument, 0, 'E' }, + { "soft-maxconn-excess", required_argument, 0, 'e' }, + { "dns-cache-timeout", required_argument, 0, 'F' }, + { "foreground", no_argument, 0, 'f' }, + { "backend-check", required_argument, 0, 'g' }, + { "help", no_argument, 0, 'h' }, + { "add-server-header", required_argument, 0, 'H' }, + { "replace-host-header", no_argument, 0, 'I' }, + { "log-traffic-dir", required_argument, 0, 'l' }, + { "max-connections", required_argument, 0, 'm' }, + { "host-match", required_argument, 0, 'M' }, + { "tryout", no_argument, 0, 'n' }, + { "prefix-timestamp", no_argument, 0, 'P' }, + { "pidfile", required_argument, 0, 'p' }, + { "soft-maxconnrate", required_argument, 0, 'r' }, + { "quit-after", required_argument, 0, 'Q' }, + { "hard-maxconnrate", required_argument, 0, 'R' }, + { "server", required_argument, 0, 's' }, + { "sticky-http", no_argument, 0, 'S' }, + { "backend-timeout", required_argument, 0, 't' }, + { "client-timeout", required_argument, 0, 'T' }, + { "time-interval", required_argument, 0, 'u' }, + { "defer-time", required_argument, 0, 'U' }, + { "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 } }; # endif @@ -121,6 +121,9 @@ void Config::parsecmdline (int ac, char **av) { case 'H': addserverheader (optarg); break; + case 'I': + replacehostheader(true); + break; case 'l': dumpdir (optarg); break; diff --git a/xr/etc/status.xslt b/xr/etc/status.xslt @@ -128,13 +128,28 @@ <td colspan="5"><hr/></td> </tr> <tr> + <td colspan="3">Number of threads</td> + <td><xsl:value-of select="/status/activity/threadcount"/></td> + <td></td> + </tr> + <tr> + <td colspan="3">Used file descriptors (approx.)</td> + <td><xsl:value-of select="/status/activity/openfiles"/></td> + <td></td> + </tr> + <tr> + <td colspan="3">File descriptor limit</td> + <td><xsl:value-of select="/status/activity/maxopenfiles"/></td> + <td></td> + </tr> + <tr> <td><b>Thread</b></td> <td><b>Description</b></td> <td><b>Back end</b></td> <td><b>Duration</b></td> <td></td> </tr> - <xsl:apply-templates select="/status/activity/thread"> + <xsl:apply-templates select="/status/activity/threadlist/thread"> <xsl:sort select="duration" data-type="number"/> </xsl:apply-templates> </table> @@ -143,7 +158,7 @@ </table> </xsl:template> -<xsl:template match="/status/activity/thread"> +<xsl:template match="/status/activity/threadlist/thread"> <tr> <td><xsl:value-of select="id"/></td> <td><xsl:value-of select="description"/></td> @@ -700,6 +715,26 @@ </xsl:choose> </td> </tr> + <tr> + <td></td> + <td colspan="2">Replace Host: headers</td> + <td> + <xsl:choose> + <xsl:when test="replacehostheader = 0"> + <select onchange="goto('/server/replacehostheader/on', '');"> + <option value="yes">yes</option> + <option value="no" selected="1">no</option> + </select> + </xsl:when> + <xsl:otherwise> + <select onchange="goto('/server/replacehostheader/off', '');"> + <option value="yes" selected="1">yes</option> + <option value="no">no</option> + </select> + </xsl:otherwise> + </xsl:choose> + </td> + </tr> <xsl:apply-templates select="/status/server/http/serverheaders"/> </xsl:template> diff --git a/xr/etc/usage.txt b/xr/etc/usage.txt @@ -79,7 +79,12 @@ may not exist on your platform): -h, -?, --help This text. -H HDR, --add-server-header HDR - Inserts HDR into back end bound HTTP messages. + Inserts HDR into back end bound HTTP messages. The header value is + appended when a pre-existing header is present. + -I HDR, --replace-host-header + Inserts "Host: <backend>" into back end bound HTTP messages. + Pre-existing Host headers are overwritten. The value of <backend> is + the server name as in the setting of --backend (-b). -l DIR, --log-traffic-dir DIR Log passing traffic with dumps in DIR. Only for debugging, slows down the balancer. diff --git a/xr/httpbuffer/addheader.cc b/xr/httpbuffer/addheader.cc @@ -1,7 +1,10 @@ #include "httpbuffer" -void Httpbuffer::addheader (string var, string val) { +void Httpbuffer::addheader (string const &var, string const &val) { PROFILE("Httpbuffer::addheader(string,string)"); + + if (!headersreceived()) + return; string old = headerval(var); if (old.size()) { diff --git a/xr/httpbuffer/addheader1.cc b/xr/httpbuffer/addheader1.cc @@ -1,7 +1,10 @@ #include "httpbuffer" -void Httpbuffer::addheader (string h) { +void Httpbuffer::addheader (string const &h) { PROFILE("Httpbuffer::addheader(string)"); + + if (!headersreceived()) + return; unsigned i; for (i = 0; i < h.size(); i++) @@ -12,5 +15,6 @@ void Httpbuffer::addheader (string h) { i++; string val = h.substr(i); addheader (var, val); + return; } } diff --git a/xr/httpbuffer/headerval.cc b/xr/httpbuffer/headerval.cc @@ -1,28 +1,28 @@ #include "httpbuffer" -string Httpbuffer::headerval (string var) { +string Httpbuffer::headerval (string const &var) { PROFILE("Httpbuffer::headerval"); - string ret; - if (!headersreceived()) return (""); - if (var[var.size() - 1] != ':') - var += ":"; + string myvar = var; + if (myvar[myvar.size() - 1] != ':') + myvar += ":"; unsigned int start; - if ( (!(start = strfind(var.c_str()))) || + if ( (!(start = strfind(myvar.c_str()))) || (start >= bodystart) ) return (""); - start += var.size(); + start += myvar.size(); for (char ch = charat(start); ch && isspace(ch); ch = charat(++start)) ; + string ret; for (char ch = charat(start); ch && ch != '\r' && ch != '\n'; ch = charat(++start)) ret += ch; - debugmsg ("Header " + var + " '" + ret + "'\n"); + debugmsg ("Header " + myvar + " '" + ret + "'\n"); return (ret); } diff --git a/xr/httpbuffer/httpbuffer b/xr/httpbuffer/httpbuffer @@ -19,16 +19,19 @@ public: bool headersreceived(); - string headerval (string var); + string headerval (string const &var); string &firstline(); bool setversion(char v); - void setheader (string var, string val); - void setheader (string h); + void setheader (string const &var, string const &val); + void setheader (string const &h); - void addheader (string var, string val); - void addheader (string h); + void addheader (string const &var, string const &val); + void addheader (string const &h); + + void replaceheader (string const &var, string const &val); + void replaceheader (string const &h); string cookievalue (string var); diff --git a/xr/httpbuffer/replaceheader1.cc b/xr/httpbuffer/replaceheader1.cc @@ -0,0 +1,17 @@ +#include "httpbuffer" + +void Httpbuffer::replaceheader(string const &h) { + PROFILE("Httpbuffer::replacehader(string)"); + + unsigned i; + for (i = 0; i < h.size(); i++) + if (h[i] == ':') { + string var = h.substr(0, i); + i++; + while (isspace(h[i])) + i++; + string val = h.substr(i); + replaceheader(var, val); + return; + } +} diff --git a/xr/httpbuffer/replaceheader2.cc b/xr/httpbuffer/replaceheader2.cc @@ -0,0 +1,16 @@ +#include "httpbuffer" + +void Httpbuffer::replaceheader(string const &var, string const &val) { + PROFILE("Httpbuffer::replacehader(string,string)"); + + if (!headersreceived()) + return; + + unsigned off = findheader(var); + if (off) { + unsigned nl = charfind('\n', off); + if (nl) + removeat(off, nl - off + 1); + } + setheader(var, val); +} diff --git a/xr/httpbuffer/setheader.cc b/xr/httpbuffer/setheader.cc @@ -1,12 +1,15 @@ #include "httpbuffer" -void Httpbuffer::setheader (string var, string val) { +void Httpbuffer::setheader (string const &var, string const &val) { PROFILE("Httpbuffer::setheader"); - if (!bodystart) + if (!headersreceived()) return; - if (var[var.size() - 1] != ':') - var += ':'; + + string myvar = var; + + if (myvar[myvar.size() - 1] != ':') + myvar += ':'; // Find position beyond first \n unsigned i; @@ -15,6 +18,6 @@ void Httpbuffer::setheader (string var, string val) { // Poke in the header. string h; - h = var + ' ' + val + "\r\n"; + h = myvar + ' ' + val + "\r\n"; insertat(i + 1, h.c_str()); } diff --git a/xr/httpbuffer/setheader1.cc b/xr/httpbuffer/setheader1.cc @@ -1,6 +1,6 @@ #include "httpbuffer" -void Httpbuffer::setheader (string h) { +void Httpbuffer::setheader (string const &h) { PROFILE("Httpbuffer::setheader(string)"); unsigned i; diff --git a/xr/httpdispatcher/handle.cc b/xr/httpdispatcher/handle.cc @@ -12,6 +12,16 @@ void HttpDispatcher::handle() { for (unsigned n = 0; n < config.nserverheaders(); n++) buf.setheader (config.serverheader(n)); + // Patch up the Host: header if requested so. + if (config.replacehostheader()) + buf.replaceheader("Host:", + balancer.backend(targetbackend()).server()); + /* + * Httpbuffer::replaceheader() is built but not used, + * e.g.: + * buf.replaceheader("MyHeader:", "MyValue"); + */ + // Flush client info received so far to the back end. debugmsg("Sending client request to back end\n"); buf.netwrite(backendfd(), config.backend_timeout()); @@ -75,6 +85,10 @@ void HttpDispatcher::handle() { if (sock == clientfd()) { othersock = backendfd(); timeout = config.backend_timeout(); + // Re-patch Host header if requested + if (config.replacehostheader()) + buf.replaceheader("Host:", + balancer.backend(targetbackend()).server()); } else { othersock = clientfd(); timeout = config.client_timeout(); diff --git a/xr/netbuffer/netbuffer b/xr/netbuffer/netbuffer @@ -55,6 +55,7 @@ private: void check_space(unsigned extra); string printable(char c) const; + string printable() const; char *buf_data; unsigned buf_sz; diff --git a/xr/netbuffer/printable1.cc b/xr/netbuffer/printable1.cc @@ -0,0 +1,9 @@ +#include "netbuffer" + +string Netbuffer::printable() const { + string ret; + + for (unsigned i = 0; i < buf_sz; i++) + ret += printable(buf_data[i]); + return ret; +} diff --git a/xr/netbuffer/removeat.cc b/xr/netbuffer/removeat.cc @@ -3,11 +3,11 @@ bool Netbuffer::removeat(unsigned index, unsigned len) { if (index >= buf_sz) return false; - + if (index + len >= buf_sz) buf_sz = index; else { - memmove (buf_data + index + len, buf_data + index, + memmove (buf_data + index, buf_data + index + len, buf_sz - index - len); buf_sz -= len; } diff --git a/xr/webinterface/answer.cc b/xr/webinterface/answer.cc @@ -173,6 +173,14 @@ void Webinterface::answer(Httpbuffer req) { return; } + // /server/replacehostheader/BOOLEAN + if (parts.size() == 3 && + parts[0] == "server" && parts[1] == "replacehostheader") { + config.replacehostheader (str2bool(parts[2], "replacehostheader")); + answer_status(); + return; + } + // /server/newheader/NEWHEADER if (parts.size() == 3 && parts[0] == "server" && parts[1] == "newheader") { diff --git a/xr/webinterface/answerstatus.cc b/xr/webinterface/answerstatus.cc @@ -67,6 +67,7 @@ void Webinterface::answer_status() { " <addxrversion>" << config.addxrversion() << "</addxrversion>\n" " <addxforwardedfor>" << config.addxforwardedfor() << "</addxforwardedfor>\n" " <stickyhttp>" << config.stickyhttp() << "</stickyhttp>\n" + " <replacehostheader>" << config.replacehostheader() << "</replacehostheader>\n" " <serverheaders>\n" ; for (unsigned i = 0; i < config.nserverheaders(); i++) @@ -104,26 +105,46 @@ void Webinterface::answer_status() { " </backend>\n" ; - o << " <activity>\n"; + o << + " <activity>\n" + " <threadlist>\n"; + unsigned nthreads = 0, max_open_files; + struct rlimit rl; + if (getrlimit(RLIMIT_NOFILE, &rl)) + throw Error("Failed to get limit for open files"); + max_open_files = unsigned(rl.rlim_cur); for (Threadmap::iterator it = Threadlist::map().begin(); it != Threadlist::map().end(); it++) { + nthreads++; pthread_t thread_id = (*it).first; Threadinfo thread_info = (*it).second; o << - " <thread>\n" - " <id>" << thread_id << "</id>\n" - " <description>" << thread_info.desc() << "</description>\n" - " <backend>" << thread_info.backend() << "</backend>\n" - " <address>"; + " <thread>\n" + " <id>" << thread_id << "</id>\n" + " <description>" << thread_info.desc() << "</description>\n" + " <backend>" << thread_info.backend() << "</backend>\n" + " <address>"; if (thread_info.backend() >= 0) o << balancer.backend(thread_info.backend()).description(); o << "</address>\n" - " <duration>" << thread_info.timestamp().elapsed() << "</duration>\n" - " </thread>\n"; + " <duration>" << thread_info.timestamp().elapsed() << "</duration>\n" + " </thread>\n"; } - o << " </activity>\n"; + /* The estimate of the number of used fd's is: + * Base is 5 (stdin/stdout/stderr, listen-fd for service, listen-fd + * for webinterface + * Plus 2 x #-threads (1 to client, 1 to backend) + * There will be fd's in use for shared libs etc., but we don't see + * those.. + */ + o << + " </threadlist>\n" + " <threadcount>" << nthreads << "</threadcount>\n" + " <openfiles>" << nthreads * 2 + 5 << "</openfiles>\n" + " <maxopenfiles>" << max_open_files << "</maxopenfiles>\n" + " </activity>\n"; o << "</status>\n\n"; diff --git a/xrctl/xrctl b/xrctl/xrctl @@ -498,6 +498,9 @@ sub xr_cmdarr { push (@cmd, '--sticky-http') if ($sp->data('stickyhttp') and istrue($sp->data('stickyhttp'))); + push (@cmd, '--replace-host-header') + if ($sp->data('replacehostheader') and + istrue($sp->data('replacehostheader'))); for (my $i = 0; ; $i++) { my $h = $sp->data('header', $i) or last; push (@cmd, '--add-server-header', $h);