crossroads

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

commit f918d5b94e797ed0459a12f0b146db505ea6cc54
parent 2dbec574cac34303db2f6f1256a7cd5b8272b0d8
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:38:59 +0100

2.75

Diffstat:
MChangeLog | 8++++++++
MMakefile | 2+-
Mxr/Dispatchers/httpdispatcher/dispatch.cc | 30+++++++++++++++++-------------
Mxr/Dispatchers/httpdispatcher/handle.cc | 17+++++++++--------
Mxr/Dispatchers/tcpdispatcher/execute.cc | 8++++++--
Mxr/backend/connect.cc | 14+++++++-------
Mxr/config/parsecmdline.cc | 5++++-
Mxr/etc/usage.txt | 7+++++--
Mxr/fdset/fdset | 8++++----
Mxr/fdset/wait.cc | 8++++++--
Axr/httpbuffer/asstring.cc | 10++++++++++
Mxr/httpbuffer/httpbuffer | 1+
Mxr/netbuffer/netread.cc | 14++++++++++++--
Mxr/sys/main.cc | 19++++++++++++++++---
14 files changed, 106 insertions(+), 45 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,11 @@ +2.75 [KK 2011-05-23] +- Signal SIGUSR1 toggles verbose logging, SIGUSR2 toggles debug + logging. +- Back end connection attempts improved (assumes "alive" unless a + failure is seen). +- Flag parsing for -p / --pidfile fixed. +- Warning: Memory leak reported with high load. Under investigation. + 2.74 [KK 2011-04-05] - Promoted to STABLE. A word of warning: DOS protection under high load may be unstable, currently under investigation. diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ # Top-level Makefile for XR # ------------------------- -VER ?= 2.74 +VER ?= 2.75 PREFIX ?= $(DESTDIR)/usr BINDIR ?= $(PREFIX)/sbin MANDIR ?= $(PREFIX)/share/man diff --git a/xr/Dispatchers/httpdispatcher/dispatch.cc b/xr/Dispatchers/httpdispatcher/dispatch.cc @@ -13,12 +13,15 @@ void HttpDispatcher::dispatch() { // Get the client's request. May need for cookie inspection or for the // host header. - while (!buf().headersreceived()) - if (!buf().netread(clientfd(), config.client_read_timeout())) { - msg ("Didn't receive a valid client request, stopping"); - return; + while (!buf().headersreceived()) { + // cout << "BUFFER fetching headers: " << buf().as_string() << '\n'; + if (! buf().netread(clientfd(), config.client_read_timeout())) { + msg("Didn't receive a valid client request, stopping"); + throw Error("No HTTP request received"); } - msg ("Received client request: '" + buf().firstline() + "'\n"); + } + msg("Received client request: '" + buf().firstline() + "'\n"); + // cout << "BUFFER with headers: " << buf().as_string() << '\n'; // See if hostmatching or urlmatching is used. // This is true when hosts or urls are matched against non-dot. @@ -33,7 +36,7 @@ void HttpDispatcher::dispatch() { // Build new target list if host- or url matching applies. if (hostmatchused || urlmatchused) { - msg ("Creating matched target list for the HTTP dispatcher\n"); + msg("Creating matched target list for the HTTP dispatcher\n"); if (hostmatchused) host_header = buf().headerval("Host"); @@ -75,10 +78,10 @@ void HttpDispatcher::dispatch() { // Dispatch as a normal backend if sticky HTTP is off, or if the // sticky target is badly specified. if (!config.stickyhttp() || - (sscanf (buf().cookievalue ("XRTarget").c_str(), - "%d", &stickytarget) < 1 && - sscanf (buf().paramvalue ("XRTarget").c_str(), - "%d", &stickytarget) < 1) || + (sscanf(buf().cookievalue("XRTarget").c_str(), + "%d", &stickytarget) < 1 && + sscanf(buf().paramvalue("XRTarget").c_str(), + "%d", &stickytarget) < 1) || stickytarget >= balancer.nbackends()) { issticky(false); TcpDispatcher::dispatch(); @@ -87,11 +90,12 @@ void HttpDispatcher::dispatch() { // to non-sticky dispatching. targetbackend(stickytarget); Backend tb = balancer.backend(stickytarget); - msg ("Sticky HTTP request for " + tb.description() + "\n"); + tb.live(true); + msg("Sticky HTTP request for " + tb.description() + "\n"); if (! tb.connect()) { balancer.backend(stickytarget).live(false); - msg ("Failed to connect to back end " + tb.description() + - ", trying to dispatch to other\n"); + msg("Failed to connect to back end " + tb.description() + + ", trying to dispatch to other\n"); issticky(false); TcpDispatcher::dispatch(); } else { diff --git a/xr/Dispatchers/httpdispatcher/handle.cc b/xr/Dispatchers/httpdispatcher/handle.cc @@ -42,16 +42,19 @@ void HttpDispatcher::handle() { readset.wait_r(); Socket sock; - if (readset.readable(clientfd())) + unsigned timeout; + if (readset.readable(clientfd())) { sock = clientfd(); - else if (readset.readable(backendfd())) + timeout = config.client_read_timeout(); + } else if (readset.readable(backendfd())) { sock = backendfd(); - else + timeout = config.backend_read_timeout(); + } else break; buf().reset(); - if (!buf().netread(sock)) + if (!buf().netread(sock, timeout)) break; if (sock == backendfd() && modify_serverheaders) { @@ -83,10 +86,9 @@ void HttpDispatcher::handle() { // Flush info to the other connected side. Socket othersock; - int timeout; if (sock == clientfd()) { othersock = backendfd(); - timeout = config.backend_read_timeout(); + timeout = config.backend_write_timeout(); // Re-patch Host header if requested if (config.replacehostheader()) buf().replaceheader("Host:", @@ -94,7 +96,7 @@ void HttpDispatcher::handle() { .server()); } else { othersock = clientfd(); - timeout = config.client_read_timeout(); + timeout = config.client_write_timeout(); } debugmsg ("Had data on " << sock.fd() << @@ -104,5 +106,4 @@ void HttpDispatcher::handle() { if (sock == backendfd()) balancer.backend(targetbackend()).addbytes(buf().bufsz()); } - } diff --git a/xr/Dispatchers/tcpdispatcher/execute.cc b/xr/Dispatchers/tcpdispatcher/execute.cc @@ -15,7 +15,9 @@ void TcpDispatcher::execute() { Threadlist::desc("Dispatching"); dispatch(); } catch (Error const &e) { - warnmsg(e.what() << '\n'); + warnmsg(e.what() << " (" + << inet2string(clientfd().clientaddr().sin_addr) + << ")\n"); return; } @@ -49,7 +51,9 @@ void TcpDispatcher::execute() { try { handle(); } catch (Error const &e) { - warnmsg(e.what() << '\n'); + warnmsg(e.what() << " (" + << inet2string(clientfd().clientaddr().sin_addr) + << ")\n"); failed = true; if (config.onfail().length()) { ostringstream o; diff --git a/xr/backend/connect.cc b/xr/backend/connect.cc @@ -3,9 +3,6 @@ bool Backend::connect() { PROFILE("Backend::connect"); - // Assume the backend is dead - islive = false; - debugmsg("About to connect to back end " << description() << '\n'); // Resolve hostname, prepare binding @@ -45,12 +42,14 @@ bool Backend::connect() { # ifdef CONNECTCHECK_ONLY_WRITABLE if (fdset.writeable(clsocket)) - islive = true; - else + live(true); + else { markconnecterror(); -# else + live(false); + } +# else if (fdset.writeable(clsocket) && !fdset.readable(clsocket)) - islive = true; + live(true); else { debugmsg("Connect socket writable: " << (fdset.writeable(clsocket) ? "yes" : "no") << @@ -58,6 +57,7 @@ bool Backend::connect() { (fdset.readable(clsocket) ? "yes" : "no") << '\n'); markconnecterror(); + live(false); } # endif } diff --git a/xr/config/parsecmdline.cc b/xr/config/parsecmdline.cc @@ -19,7 +19,7 @@ void Config::parsecmdline (int ac, char **av) { throw Error("Bad command line '" + cmdline + "'\n" + USAGE); # define OPTSTRING "?a:A:B:b:c:CDd:E:e:fF:Gg:hH:Ij:l:" \ - "m:M:nPQ:r:R:Ss:t:T:u:U:vVW:w:xXy:Y:z:Z:" + "m:M:np:PQ:r:R:Ss:t:T:u:U:vVW:w:xXy:Y:z:Z:" # ifdef HAVE_GETOPT_LONG static struct option longopts[] = { { "allow-from", required_argument, 0, 'a' }, @@ -157,6 +157,9 @@ void Config::parsecmdline (int ac, char **av) { case 'P': prefix_timestamp = true; break; + case 'p': + pidfile(optarg); + break; case 'Q': quit_after = (unsigned) setinteger(optarg); break; diff --git a/xr/etc/usage.txt b/xr/etc/usage.txt @@ -182,6 +182,9 @@ may not exist on your platform): XR's errors and warnings are sent to stderr, debugging and verbose messages go to stdout. Invoke XR daemons using something like "xr --backend ... [other flags] 2>&1 | logger &", or use xrctl. -Send signal SIGHUP (-1) to xr to have it report back end states. Other -typical signals request termination. + +Send signal SIGHUP (-1) to write back end states to the log. +Send signal SUGUSR1 (-30) to toggle verbose logging. +Send signal SIGUSR2 (-31) to toggle debug logging. +Other typical signals request termination. diff --git a/xr/fdset/fdset b/xr/fdset/fdset @@ -22,10 +22,10 @@ public: int fd(unsigned index) { return set[index]; } - void wait(bool wait_read, bool wait_write); - void wait_rw() { wait(true, true); } - void wait_r() { wait(true, false); } - void wait_w() { wait(false, true); } + double wait(bool wait_read, bool wait_write); + double wait_rw() { return wait(true, true); } + double wait_r() { return wait(true, false); } + double wait_w() { return wait(false, true); } bool readable(int fd) { return FD_ISSET(fd, &readset); } bool readable(Socket &s) { return FD_ISSET(s.fd(), &readset); } diff --git a/xr/fdset/wait.cc b/xr/fdset/wait.cc @@ -1,8 +1,9 @@ #include "fdset" -void Fdset::wait(bool wait_read, bool wait_write) { +double Fdset::wait(bool wait_read, bool wait_write) { PROFILE("Fdset::wait"); + Timestamp start; struct timeval tv, *tvp; // No fd's? Nothing to wait for. @@ -42,7 +43,7 @@ void Fdset::wait(bool wait_read, bool wait_write) { FD_ZERO(&readset); FD_ZERO(&writeset); FD_ZERO(&exceptset); - return; + return start.elapsed(); } // Check for exceptions. @@ -76,4 +77,7 @@ void Fdset::wait(bool wait_read, bool wait_write) { debugmsg(o.str()); } } + + // All done. Return microsecs since start. + return start.elapsed(); } diff --git a/xr/httpbuffer/asstring.cc b/xr/httpbuffer/asstring.cc @@ -0,0 +1,10 @@ +#include "httpbuffer" + +string Httpbuffer::as_string() const { + string ret = ""; + + for (unsigned i = 0; i < bufsz(); i++) + ret += charat(i); + + return ret; +} diff --git a/xr/httpbuffer/httpbuffer b/xr/httpbuffer/httpbuffer @@ -41,6 +41,7 @@ public: string requesturi(); + string as_string() const; void reset(); private: diff --git a/xr/netbuffer/netread.cc b/xr/netbuffer/netread.cc @@ -4,10 +4,15 @@ unsigned Netbuffer::netread (Socket &s, unsigned timeout) { PROFILE("Netbuffer::netread"); +// cout << "Netbuffer::netread: socket " << s.fd() << ", timeout " +// << timeout << '\n'; + + double usec = 0; + if (timeout) { Fdset set(timeout); set.add(s); - set.wait_r(); + usec = set.wait_r(); if (! set.readable(s)) { msg("Fd "<< s.fd() << " failed to become readable within " << timeout << " sec\n"); @@ -15,6 +20,8 @@ unsigned Netbuffer::netread (Socket &s, unsigned timeout) { } } + // cout << "fd " << s.fd() << " readable after " << usec << "usec\n"; + check_space(config.buffersize()); // Read from the network. If this fails, don't throw an exception @@ -28,12 +35,15 @@ unsigned Netbuffer::netread (Socket &s, unsigned timeout) { if (config.debug() && nread) { ostringstream o; - o << "Got " << nread << " bytes from fd " << s.fd() << ": "; + o << "Got " << nread << " bytes from fd " << s.fd() + << ", timeout " << timeout << ": "; for (unsigned i = 0; i < (unsigned)nread; i++) o << printable(buf_data[i]); o << "\n"; debugmsg (o.str()); } + // cout << "Fetched " << nread << " bytes\n"; + return nread; } diff --git a/xr/sys/main.cc b/xr/sys/main.cc @@ -57,10 +57,23 @@ static void sigcatcher (int sig) { debugmsg("Seen signal " << sig << '\n'); switch (sig) { case SIGHUP: - /* Generate a report to the log. Somewhat stale given the - * web interface. */ + // Generate a report to the log. balancer.report(true); break; + case SIGUSR1: + // Toggle verbosity. + if (config.verbose()) + config.verbose(false); + else + config.verbose(true); + break; + case SIGUSR2: + // Toggle debugging. + if (config.debug()) + config.debug(false); + else + config.debug(true); + break; default: balancer.terminate(true); break; @@ -72,7 +85,7 @@ int main (int argc, char **argv) { PROFILE("main"); static int relevant_sig[] = { - SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGTERM, SIGSTOP, + SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGTERM, SIGSTOP, SIGUSR1, SIGUSR2 }; try {