commit 7123fc4787fd87f5c3c5b971e2def5e0b8555e4b
parent 53074fbfbbc45d08674f1a7e490516a054d4a700
Author: finwo <finwo@pm.me>
Date: Sat, 3 Jan 2026 19:36:08 +0100
2.20
Diffstat:
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