crossroads

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

commit 7123fc4787fd87f5c3c5b971e2def5e0b8555e4b
parent 53074fbfbbc45d08674f1a7e490516a054d4a700
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:36:08 +0100

2.20

Diffstat:
MChangeLog | 5+++++
MMakefile | 6++++--
Mxr/Makefile | 5++++-
Mxr/balancer/serve.cc | 49++++++++++++++++++++++++++++++++-----------------
Mxr/buffer/buffer | 13+++++++++++--
Dxr/buffer/charat.cc | 7-------
Dxr/buffer/size.cc | 5-----
Axr/buffer/strfind.cc | 9+++++++++
Mxr/config/config | 3+++
Mxr/config/config1.cc | 1+
Mxr/config/parsecmdline.cc | 34++++++++++++++++++++++++----------
Mxr/etc/Makefile.class | 2+-
Mxr/etc/usage.txt | 2++
Mxr/httpbuffer/firstline.cc | 10++++++----
Mxr/httpbuffer/headersdone.cc | 19+++++++++++++++++++
Mxr/httpbuffer/httpbuffer | 7++++---
Mxr/httpbuffer/httpbuffer1.cc | 2+-
Mxr/httpbuffer/requestmethod.cc | 2+-
Mxr/httpbuffer/requesturi.cc | 2+-
Mxr/httpdispatcher/dispatch.cc | 4++--
Axr/sys/strnstr.cc | 28++++++++++++++++++++++++++++
Mxr/sys/sys | 4++++
22 files changed, 162 insertions(+), 57 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,8 @@ +2.20 [KK 2008-10-13] +- Fixed -C flag recognition, --close-sockets-fast worked, but I'd + omitted -C from the flag set. +- Ongoing optimization work. + 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. diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ # Top-level Makefile for XR # ------------------------- -VER = 2.19 +VER = 2.20 BINDIR = /usr/sbin TAR = /tmp/crossroads-$(VER).tar.gz AUTHOR = Karel Kubat <karel@kubat.nl> @@ -43,13 +43,15 @@ install: local clean: rm -rf xr/build/* + find . -name gmon.out -exec rm {} \; -tar: +tar: clean rm -rf $(TAR) /tmp/crossroads-$(VER) cd ..; cp -r crossroads /tmp/crossroads-$(VER) cd /tmp; tar czf $(TAR) \ --exclude .git --exclude .svn --exclude crossroads-$(VER)/xr/build \ crossroads-$(VER) + rm -rf /tmp/crossroads-$(VER) @echo @echo 'Sources now tarred into $(TAR)' diff --git a/xr/Makefile b/xr/Makefile @@ -6,6 +6,7 @@ BIN = $(BUILDDIR)/xr LIB = $(BUILDDIR)/libxr.a TMPXR = /tmp/xr-$(shell whoami) CONF_CC = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache c++-compiler) +CONF_OPTFLAGS = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache optflags) CONF_LIB = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \ lib ucb nsl pthread socket m alf) CONF_GETOPT = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \ @@ -14,7 +15,8 @@ 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) +CONF_STRNSTR = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \ + libfunction strnstr HAVE_STRNSTR) foo: $(MAKE) subdirs @@ -36,6 +38,7 @@ subdirs: $(BUILDDIR)/usage.h $(BUILDDIR)/status.xslt.h 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_OPTFLAGS=$(CONF_OPTFLAGS) \ + CONF_STRNSTR=$(CONF_STRNSTR) \ $(MAKE) -C $$f -f $(BASE)/xr/etc/Makefile.class \ || exit 1; \ done diff --git a/xr/balancer/serve.cc b/xr/balancer/serve.cc @@ -31,7 +31,7 @@ void Balancer::serve() { ostringstream o; o << server_fd; msg ("Awaiting activity on fd " + o.str() +"\n"); - while (1) { + while (true) { Fdset fdset(0); fdset.add (server_fd); if (fdset.readable() < 0) { @@ -40,22 +40,6 @@ void Balancer::serve() { msg ("Interrupt seen\n"); if (terminate()) { msg ("Termination requested, XR will stop.\n"); - socketclose (server_fd); - shutdown (server_fd, SHUT_RDWR); - unsigned prev_conn = 0x19081962; - while (1) { - unsigned curr_conn = balancer.connections(); - if (!curr_conn) - break; - if (curr_conn != prev_conn) { - ostringstream o; - o << curr_conn; - msg ("There are still " + o.str() + " connections\n"); - prev_conn = curr_conn; - } - sleep (1); - } - msg ("XR is idle, stopping.\n"); break; } else if (report()) { msg ("Report requested\n"); @@ -207,8 +191,39 @@ void Balancer::serve() { d->execute(); break; } + + // If we exceed the max # of requests, stop.. + if (config.quitafter()) { + ostringstream o; + o << "Request " << requestnr() << " underway of max " + << config.quitafter() << "\n"; + msg (o.str()); + if (requestnr() >= (long)config.quitafter()) { + msg ("Max requests served, will stop.\n"); + break; + } + } } + + // We're stopping XR now. Wait for running threads to die off. + socketclose (server_fd); + shutdown (server_fd, SHUT_RDWR); + unsigned prev_conn = 0x19081962; + while (1) { + unsigned curr_conn = balancer.connections(); + if (!curr_conn) + break; + if (curr_conn != prev_conn) { + ostringstream o; + o << curr_conn; + msg ("There are still " + o.str() + " connections\n"); + prev_conn = curr_conn; + } + sleep (1); + } + msg ("XR is idle, stopping.\n"); + // If a PID stamp was created, remove it now. if (config.pidfile() != "") unlink (config.pidfile().c_str()); diff --git a/xr/buffer/buffer b/xr/buffer/buffer @@ -14,14 +14,23 @@ public: void set (char const *b, unsigned len); void add (char const *b, unsigned len); - unsigned size() const; char const *data() const; - char charat (unsigned index) const; + int strfind (char const *s) const; char &operator[] (unsigned index); string stringat (unsigned index, unsigned len) const; void removeat (unsigned index, unsigned len = 1); void insertat (unsigned index, char const *s, unsigned len); void insertat (unsigned index, string s); + + // This ones are called often so let's inline them. + unsigned size() const { + return (buf_len); + } + char charat (unsigned index) const { + if (index >= buf_len) + return (0); + return (buf_data[index]); + } private: void copy (Buffer const &other); diff --git a/xr/buffer/charat.cc b/xr/buffer/charat.cc @@ -1,7 +0,0 @@ -#include "buffer" - -char Buffer::charat (unsigned index) const { - if (index >= buf_len) - return (0); - return (buf_data[index]); -} diff --git a/xr/buffer/size.cc b/xr/buffer/size.cc @@ -1,5 +0,0 @@ -#include "buffer" - -unsigned Buffer::size() const { - return (buf_len); -} diff --git a/xr/buffer/strfind.cc b/xr/buffer/strfind.cc @@ -0,0 +1,9 @@ +#include "buffer" + +int Buffer::strfind(char const *s) const { + char *cp; + + if ( (cp = strnstr(buf_data, s, buf_len)) ) + return (cp - buf_data); + return (-1); +} diff --git a/xr/config/config b/xr/config/config @@ -24,6 +24,8 @@ public: bool debug() const { return (debug_flag); } void debug (bool d) { debug_flag = d; } + unsigned quitafter() const { return (quit_after); } + Servertype::Type stype() const { return (styp.type()); } string stypestr() const { return (styp.typestr()); } string sipaddr() const { return (sip); } @@ -156,6 +158,7 @@ private: static unsigned hard_maxconnrate; static unsigned defer_time; static unsigned connrate_timeinterval; + static unsigned quit_after; }; extern Config config; diff --git a/xr/config/config1.cc b/xr/config/config1.cc @@ -33,6 +33,7 @@ unsigned Config::soft_maxconnrate = 0; unsigned Config::hard_maxconnrate = 0; unsigned Config::defer_time = 500000; unsigned Config::connrate_timeinterval = 1; +unsigned Config::quit_after = 0; 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:l:m:M:nPp:r:R:Ss:t:T:u:U:vVW:w:xX" +# define OPTSTRING "?a:A:B:b:c:CDd:fhH:l: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' }, @@ -36,20 +36,21 @@ void Config::parsecmdline (int ac, char **av) { { "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' }, - { "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 @@ -120,6 +121,9 @@ void Config::parsecmdline (int ac, char **av) { case 'p': pid_file = optarg; break; + case 'Q': + quit_after = (unsigned) setinteger(optarg); + break; case 'r': soft_maxconnrate = (unsigned) setinteger (optarg); break; @@ -166,17 +170,27 @@ void Config::parsecmdline (int ac, char **av) { # else cout << "getopt.h : absent\n"; # endif -# ifdef HAVE_GETOPT_LONG - cout << "getopt_long() : present\n"; -# else - cout << "getopt_long() : absent (only short flags will work)\n"; -# endif # ifdef HAVE_INADDR_NONE cout << "INADDR_NONE : present\n"; # else cout << "INADDR_NONE : absent, defined to " << INADDR_NONE << "\n"; # endif +# ifdef HAVE_GETOPT_LONG + cout << "getopt_long() : present\n"; +# else + cout << "getopt_long() : absent (only short flags will work)\n"; +# endif +# ifdef HAVE_INET_ATON + cout << "inet_aton() : present\n"; +# else + cout << "inet_aton() : absent\n"; +# endif +# ifdef HAVE_STRNSTR + cout << "strnstr() : present\n"; +# else + cout << "strnstr() : absent\n"; +# endif exit (0); case 'W': setwebinterface(optarg); diff --git a/xr/etc/Makefile.class b/xr/etc/Makefile.class @@ -11,7 +11,7 @@ $(BASE)/xr/$(BUILDDIR)/$(DIR)_%.o: %.cc -DVER='"$(VER)"' -DAUTHOR='"$(AUTHOR)"' \ -DMAINTAINER='"$(MAINTAINER)"' -DSYS='"$(SYS)"' -D$(SYS) \ -DCONF_CC='"$(CONF_CC)"' -DCONF_LIB='"$(CONF_LIB)"' \ - -DCONF_OPTFLAGS='"$(CONF_OPTFLAGS)"' \ + -DCONF_OPTFLAGS='"$(CONF_OPTFLAGS)"' $(CONF_STRNSTR) \ $(CONF_GETOPT) $(CONF_GETOPT_LONG) $(CONF_INET_ATON) \ -I$(BASE)/xr \ -c -g -Wall -o $@ $< diff --git a/xr/etc/usage.txt b/xr/etc/usage.txt @@ -71,6 +71,8 @@ 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 + -Q REQUESTS, --quit-after REQUESTS + Stops the balancer after REQUESTS hits. For debugging / loadtesting. -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 diff --git a/xr/httpbuffer/firstline.cc b/xr/httpbuffer/firstline.cc @@ -1,12 +1,14 @@ #include "httpbuffer" -string Httpbuffer::firstline() const { - string ret; +string &Httpbuffer::firstline() { + if (first_line.size()) + return (first_line); + for (unsigned i = 0; i < size(); i++) { char ch = charat(i); if (ch == '\n' || ch == '\r') break; - ret += ch; + first_line += ch; } - return (ret); + return (first_line); } diff --git a/xr/httpbuffer/headersdone.cc b/xr/httpbuffer/headersdone.cc @@ -4,6 +4,12 @@ bool Httpbuffer::headersdone() { if (bodystart) return (true); +#ifdef OLDCODE + // This attempts to find \r\n\r\n in the received blob which + // marks the end of headers. It can also be \n\n for clients + // that aren't too strict. + // [KK 2008-10-13] This hoses charat() all the time - attempt + // to optimize, see below bool prevnl = false; for (unsigned i = 0; i < size(); i++) { if (charat(i) == '\n') { @@ -19,4 +25,17 @@ bool Httpbuffer::headersdone() { } return (false); +#endif + + // [KK 2008-10-13] Optimization of the above (?) + int off; + if ( (off = strfind("\r\n\r\n")) >= 0 ) { + bodystart = off + 4; + return (true); + } + if ( (off = strfind("\n\n")) >= 0 ) { + bodystart = off + 2; + return (true); + } + return (false); } diff --git a/xr/httpbuffer/httpbuffer b/xr/httpbuffer/httpbuffer @@ -22,18 +22,19 @@ public: bool headersdone(); string headerval (string var); Bodystatus bodyreceived(); - string firstline() const; + string &firstline(); bool setversion(string v); void setheader (string var, string val); void addheader (string var, string val); void addheader (string h); string cookievalue (string var); - RequestMethod requestmethod() const; - string requesturi() const; + RequestMethod requestmethod(); + string requesturi(); private: unsigned findheader (string h); unsigned bodystart; + string first_line; }; #endif diff --git a/xr/httpbuffer/httpbuffer1.cc b/xr/httpbuffer/httpbuffer1.cc @@ -1,4 +1,4 @@ #include "httpbuffer" -Httpbuffer::Httpbuffer(): bodystart(0) { +Httpbuffer::Httpbuffer(): bodystart(0), first_line("") { } diff --git a/xr/httpbuffer/requestmethod.cc b/xr/httpbuffer/requestmethod.cc @@ -1,6 +1,6 @@ #include "httpbuffer" -Httpbuffer::RequestMethod Httpbuffer::requestmethod() const { +Httpbuffer::RequestMethod Httpbuffer::requestmethod() { string first = firstline(); debugmsg ("First line of http buffer: '" + first + "'\n"); diff --git a/xr/httpbuffer/requesturi.cc b/xr/httpbuffer/requesturi.cc @@ -1,6 +1,6 @@ #include "httpbuffer" -string Httpbuffer::requesturi() const { +string Httpbuffer::requesturi() { vector<string> parts = str2parts (firstline(), ' '); return (parts.size() >= 2 ? parts[1] : ""); } diff --git a/xr/httpdispatcher/dispatch.cc b/xr/httpdispatcher/dispatch.cc @@ -15,8 +15,8 @@ void HttpDispatcher::dispatch() { throw static_cast<Error>("Didn't receive a valid " "client request.\n"); - // See if hostmatching is used. This is true when a backend matches against - // a non-dot host. + // See if hostmatching is used. This is true when a backend + // matches against a non-dot host. bool hostmatchused = false; for (unsigned i = 0; i < balancer.nbackends(); i++) if (balancer.backend(i).hostmatch() != ".") { diff --git a/xr/sys/strnstr.cc b/xr/sys/strnstr.cc @@ -0,0 +1,28 @@ +#include "sys" + +#ifndef HAVE_STRNSTR +// [KK 2008-10-13] Got this from +// http://opengrok.creo.hu/dragonfly/xref/src/lib/libc/string/strnstr.c +// and it's of course for systems that don't have this lib function +// themselves +char *strnstr(const char *s, const char *find, size_t slen) { + char c, sc; + size_t len; + + if ((c = *find++) != '\0') { + len = strlen(find); + do { + do { + if (slen < 1 || (sc = *s) == '\0') + return (0); + --slen; + ++s; + } while (sc != c); + if (len > slen) + return (0); + } while (strncmp(s, find, len) != 0); + s--; + } + return ( (char*) s); +} +#endif diff --git a/xr/sys/sys b/xr/sys/sys @@ -62,4 +62,8 @@ unsigned long mt_rand(void); int inet_aton (char const *name, struct in_addr *addr); #endif +#ifndef HAVE_STRNSTR +char *strnstr (char const *s, char const *find, size_t slen); +#endif + #endif