crossroads

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

commit 3bb3099779ff845dc1e34c7112bcf08bbef357ff
parent 6b70b8c84664f605bf793999e3bb4633bd67a9d4
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:09:15 +0100

1.07

Diffstat:
MChangeLog | 5+++++
Mdoc/Makefile | 1+
Mdoc/benchmarking.yo | 42+++++++++++++++++++++---------------------
Mdoc/compiling.yo | 164++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdoc/config.yo | 167+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mdoc/crossroads.html | 854++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mdoc/crossroads.man | 162+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mdoc/crossroads.pdf | 0
Mdoc/crossroads.yo | 18+++++++++---------
Mdoc/defs.yo | 22+++++++++++-----------
Mdoc/impatient.yo | 68++++++++++++++++++++++++++++++++++----------------------------------
Mdoc/intro.yo | 180++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdoc/tips.yo | 182++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mdoc/using.yo | 54+++++++++++++++++++++++++++---------------------------
Metc/Makefile.def | 2+-
Msrc/crossroads.h | 8+++++++-
Asrc/httpinsertheader.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/httpserve.c | 27++++++++++++++++++---------
Asrc/httpsetrealipheader.c | 10++++++++++
Msrc/httpsetstickycookie.c | 53++++++-----------------------------------------------
Msrc/lexer.l | 23+++++++++++++++++------
Msrc/main.c | 1-
Msrc/parser.y | 672+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mtest/client | 5++---
Mtest/server | 17+++++++++++------
Mtest/t1.conf | 2+-
Mtest/t2.conf | 2+-
Atest/t4.conf | 35+++++++++++++++++++++++++++++++++++
Atools/untab | 31+++++++++++++++++++++++++++++++
29 files changed, 1651 insertions(+), 1206 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,6 +1,11 @@ ChangeLog for Crossroads ------------------------------------------------------------------------------ +1.07 [KK 2006-07-25] Next development (trunk) version. Directive + 'insertrealip' implemented. Docs updated. While creating docs, + leading tabs are now expanded (needed for PDF documentation). + [KK 2006-08-28] 1.07 promoted to next stable version. + 1.06 [KK 2006-07-25] Small documentation fix. Embedded c-conf upped to 1.03 (protection from endless recursive directory scans). Optimized http_serve() for short messages (no diff --git a/doc/Makefile b/doc/Makefile @@ -1,6 +1,7 @@ include ../etc/Makefile.def foo: + perl ../tools/untab *.yo yo2html -dVER=$(VER) crossroads yo2man -dVER=$(VER) crossroads yo2pdf -dVER=$(VER) crossroads diff --git a/doc/benchmarking.yo b/doc/benchmarking.yo @@ -8,13 +8,13 @@ subsect(Benchmark 1: Accessing a proxy via crossroads or directly) The benchmark was run on a system where the following was varied: enumeration( - eit() A website was recursively spidered through a local squid - proxy. The spidering was repeated 10 times, the total was recorded. + eit() A website was recursively spidered through a local squid + proxy. The spidering was repeated 10 times, the total was recorded. - eit() Crossroads was placed in front of the squid proxy, and - the website was again recursively spidered. Again, the - spidering was repeated 10 times and the total was recorded.) - + eit() Crossroads was placed in front of the squid proxy, and + the website was again recursively spidered. Again, the + spidering was repeated 10 times and the total was recorded.) + The crossroads configuration of the second alternative is shown below: verb(\ @@ -68,16 +68,16 @@ This worst case scenario will however (fortunately) occur only very seldom in the real world: itemization( - it() Normally network issues, such as the above mentioned host - name lookups or throughput restrictions, will add - significantly to the duration of a request. The 'twice as - many' read/writes caused by crossroads are then relatively - irrelevant. + it() Normally network issues, such as the above mentioned host + name lookups or throughput restrictions, will add + significantly to the duration of a request. The 'twice as + many' read/writes caused by crossroads are then relatively + irrelevant. - it() Normally a significant amount of time will be spent in a - back end, due to processing (e.g., when calling a servlet on a - back end). Again, this processing time will weigh much heavier - than the multiple read/writes.) + it() Normally a significant amount of time will be spent in a + back end, due to processing (e.g., when calling a servlet on a + back end). Again, this processing time will weigh much heavier + than the multiple read/writes.) subsect(Benchmark 2: Crossroads versus Linux Virtual Server (LVS)) @@ -126,16 +126,16 @@ In the first test, ports 80 and 81 on the balancer were 'bombed' with following timings where measured: itemization( - it() How long it takes to establish a connection; - it() How long it takes to retrieve the page.) + it() How long it takes to establish a connection; + it() How long it takes to retrieve the page.) The results of this test were: itemization( - it() On average, each client took 0.12 seconds to connect - to LVS, and each page was retrieved in 0.14 seconds; - it() On average, each client took 0.11 seconds to connect to - crossroads, and each page was retrieved in 0.13 seconds.) + it() On average, each client took 0.12 seconds to connect + to LVS, and each page was retrieved in 0.14 seconds; + it() On average, each client took 0.11 seconds to connect to + crossroads, and each page was retrieved in 0.13 seconds.) In this setup there seems to be no difference between the performance of LVS and crossroads! diff --git a/doc/compiling.yo b/doc/compiling.yo @@ -3,14 +3,14 @@ subsect(Prerequisites) The creation of crossroads requires: itemization( - it() Standard Unix tools, such as tt(sed), tt(awk), tt(Perl) - (5.00 or better); + it() Standard Unix tools, such as tt(sed), tt(awk), tt(Perl) + (5.00 or better); - it() A POSIX-compliant C compiler; + it() A POSIX-compliant C compiler; - it() The grammar generation tools tt(bison) and tt(flex); + it() The grammar generation tools tt(bison) and tt(flex); - it() Support for SYSV IPC, networking and so on. + it() Support for SYSV IPC, networking and so on. ) Basically a Linux or Apple MacOSX box will do nicely once you make @@ -21,58 +21,58 @@ crossroads, follow these steps. subsect(Compiling and installing) itemization( - it() Obtain the source distribution. It can be found on - lurl(http://crossroads.e-tunity.com). The distribution comes as an - archive tt(crossroads-)em(type)tt(.tar.gz), where em(type) is - tt(stable) or tt(devel). - - it() Unpack the archive in a sources directory using tt(tar - xzf crossroads-)em(X.YY)tt(.tar.gz). The contents spill into a - subdirectory tt(crossroads-)em(X.YY/). - - it() Change-dir into the directory. - - it() Next, edit tt(etc/Makefile.def) and verify that all - compilation settings are to your likings. The settings are - explained in the file. bf(Note that) the default distribution - of tt(Makefile.def) is suited for Linux or Apple MacOSX - systems. On other Unices, or on non-Unix systems, you must - particularly pay attention to tt(SET_PROC_TITLE_BY...). When - in doubt, comment out all tt(SET_PROC_TITLE...) - settings. Crossroads will work nevertheless, but it won't show - nice titles in tt(ps) listings. Also there's a macro - tt(EXTRA_LIBS) to add linkage flags (an example for a Solaris - build is included). - - it() Now crossroads is ready for compilation. Do a tt(make - local) followed by tt(make install). The latter step may have - to be done by the user tt(root) if the tt(BINDIR) setting of - tt(etc/Makefile.def) points to a root-owned directory. - - it() The documentation doesn't install in this process. If you - want to install the documentation, then proceed as follows: - - itemization( - it() Optionally, tt(cp doc/crossroads.html) - em(htmldirectory/); where em(htmldirectory) is the destination - directory for your HTML manuals; - - it() Optionally, tt(cp doc/crossroads.pdf) - em(pdfdirectory/); where em(pdfdirectory) is the - destination directory for your PDF manuals; - - it() Optionally, tt(cp doc/crossroads.man) - em(manualdirectory)tt(/man1/crossroads.1), where - em(manualdirectory) is e.g. tt(/usr/man), - tt(/usr/share), tt(/usr/local/man), - tt(/usr/local/share). Any possibility is valid, as - long as em(manualdirectory) has a subdirectory - tt(man1/); - - it() If your manual page system supports compressed - manual pages, then you can save some space with - tt(gzip) em(manualdirectory)tt(/man1/crossroads.1).) - + it() Obtain the source distribution. It can be found on + lurl(http://crossroads.e-tunity.com). The distribution comes as an + archive tt(crossroads-)em(type)tt(.tar.gz), where em(type) is + tt(stable) or tt(devel). + + it() Unpack the archive in a sources directory using tt(tar + xzf crossroads-)em(X.YY)tt(.tar.gz). The contents spill into a + subdirectory tt(crossroads-)em(X.YY/). + + it() Change-dir into the directory. + + it() Next, edit tt(etc/Makefile.def) and verify that all + compilation settings are to your likings. The settings are + explained in the file. bf(Note that) the default distribution + of tt(Makefile.def) is suited for Linux or Apple MacOSX + systems. On other Unices, or on non-Unix systems, you must + particularly pay attention to tt(SET_PROC_TITLE_BY...). When + in doubt, comment out all tt(SET_PROC_TITLE...) + settings. Crossroads will work nevertheless, but it won't show + nice titles in tt(ps) listings. Also there's a macro + tt(EXTRA_LIBS) to add linkage flags (an example for a Solaris + build is included). + + it() Now crossroads is ready for compilation. Do a tt(make + local) followed by tt(make install). The latter step may have + to be done by the user tt(root) if the tt(BINDIR) setting of + tt(etc/Makefile.def) points to a root-owned directory. + + it() The documentation doesn't install in this process. If you + want to install the documentation, then proceed as follows: + + itemization( + it() Optionally, tt(cp doc/crossroads.html) + em(htmldirectory/); where em(htmldirectory) is the destination + directory for your HTML manuals; + + it() Optionally, tt(cp doc/crossroads.pdf) + em(pdfdirectory/); where em(pdfdirectory) is the + destination directory for your PDF manuals; + + it() Optionally, tt(cp doc/crossroads.man) + em(manualdirectory)tt(/man1/crossroads.1), where + em(manualdirectory) is e.g. tt(/usr/man), + tt(/usr/share), tt(/usr/local/man), + tt(/usr/local/share). Any possibility is valid, as + long as em(manualdirectory) has a subdirectory + tt(man1/); + + it() If your manual page system supports compressed + manual pages, then you can save some space with + tt(gzip) em(manualdirectory)tt(/man1/crossroads.1).) + ) @@ -87,31 +87,31 @@ tt(crossroads start). Test the availability of your services and back ends. Monitor how crossroads is doing with: itemization( - it() In one terminal, run the script: - verb(\ + it() In one terminal, run the script: + verb(\ while [ 1 ] ; do tput clear crossroads status sleep 3 done) - bf(Note) that depending on your system you might need - tt(sleep 3s), i.e., with an tt(s) appended. + bf(Note) that depending on your system you might need + tt(sleep 3s), i.e., with an tt(s) appended. - it() In another terminal, run: - verb(\ + it() In another terminal, run: + verb(\ while [ 1 ] ; do tput clear ps ax | grep crossroads | grep -v grep sleep 3 done) - bf(Note) that depending on your system you might need - tt(ps -ef) instead of tt(ps ax). + bf(Note) that depending on your system you might need + tt(ps -ef) instead of tt(ps ax). - it() In yet another terminal, run tt(tail -f - /var/log/messages) (supply the appropriate system log file if - tt(/var/log/messages) doesn't work for you).) + it() In yet another terminal, run tt(tail -f + /var/log/messages) (supply the appropriate system log file if + tt(/var/log/messages) doesn't work for you).) Now thoroughly test the availability of your back ends through crossroads. The status display will show an updated view of which back @@ -135,33 +135,33 @@ inserting scripts into the boot sequence, but otherwise the steps will resemble the following. itemization( - it() Create a script tt(crossroads) in tt(/etc/init.d) similar to the - following: + it() Create a script tt(crossroads) in tt(/etc/init.d) similar to the + following: verb(\ #!/bin/sh /usr/local/bin/crossroads -v $@) - The stated directory tt(/usr/local/bin) must correspond with - the installation path. The flag tt(-v) causes the startup to - be more 'verbose'. However, once daemonized, the verbosity is - controlled by the appropriate statements in the configuration. + The stated directory tt(/usr/local/bin) must correspond with + the installation path. The flag tt(-v) causes the startup to + be more 'verbose'. However, once daemonized, the verbosity is + controlled by the appropriate statements in the configuration. - it() Determine your 'runlevel': usually 3 when your system is - running in text-mode only, or 5 when you are using a graphical - interface. If your runlevel is 3, then: + it() Determine your 'runlevel': usually 3 when your system is + running in text-mode only, or 5 when you are using a graphical + interface. If your runlevel is 3, then: verb(\ root> cd /etc/rc.d/rc3.d root> ln -s /etc/init.d/crossroads S99crossroads root> ln -s /etc/init.d/crossroads K99crossroads) - This creates startup (tt(S*)) and stop (tt(K*)) links that - will be run when the system enters or leaves a given runlevel. + This creates startup (tt(S*)) and stop (tt(K*)) links that + will be run when the system enters or leaves a given runlevel. - If your runlevel is 5, then the right tt(cd) command is to - tt(/etc/rc.d/rc5.d). Alternatively, you can create the - symlinks in both runlevel directories.) + If your runlevel is 5, then the right tt(cd) command is to + tt(/etc/rc.d/rc5.d). Alternatively, you can create the + symlinks in both runlevel directories.) subsubsect(BSD Style Startup) diff --git a/doc/config.yo b/doc/config.yo @@ -51,12 +51,12 @@ character tt(;). If a string must contain a semicolon, then it must be enclosed in single or double quotes: itemization( - it() tt(This is a string;) is a string that starts at tt(T) - and ends with tt(g) - it() tt("This is a string";) is the same, the double quotes - are not necessary - it() tt("This is ; a string";) has double quotes to protect - the inner ;) + it() tt(This is a string;) is a string that starts at tt(T) + and ends with tt(g) + it() tt("This is a string";) is the same, the double quotes + are not necessary + it() tt("This is ; a string";) has double quotes to protect + the inner ;) Finally, an argument can be a 'boolean' value. Crossroads knows tt(true), tt(false), tt(yes), tt(no), tt(on), tt(off). The keywords @@ -89,20 +89,20 @@ startdit() dit(The type statement) defines how crossroads handles the stated service. There are currently two types: tt(any) and -tt(stickyhttp). The type tt(any) means that crossroads doesn't +tt(http). The type tt(any) means that crossroads doesn't interpret the contents of a TCP stream, but only distributes streams -over back ends. The type tt(stickyhttp) means that crossroads has to -analyze what's in the messages, does magical HTTP cookie tricks, and -so on -- to ensure that multiple connections are treated as one -session. +over back ends. The type tt(http) means that crossroads has to +analyze what's in the messages, does magical HTTP header tricks, and +so on -- all to ensure that multiple connections are treated as one +session, or that the back end is notified of the client's IP address. -Unless you really need sticky HTTP sessions, use the type tt(any) (the +Unless you really need such special features, use the type tt(any) (the default), even for HTTP protocols. itemization( - it() Syntax: tt(type) em(typespecifier) tt(;) - it() Where em(typespecifier) is tt(any) or tt(stickyhttp) - it() Default: tt(any)) + it() Syntax: tt(type) em(typespecifier) tt(;) + it() Where em(typespecifier) is tt(any) or tt(http) + it() Default: tt(any)) dit(The port statement) defines to which TCP port a service 'listens'. E.g. tt(port 8000) says that this service will accept @@ -120,10 +120,10 @@ serviced. By default, crossroads binds a service to all presently active IP addresses at the invoking host. itemization( - it() Syntax: tt(bindto) em(ip-address) tt(;) - it() where em(ip-address) is a numeric IP address, such as - tt(192.168.1.45), or the keyword tt(any) - it() Default: tt(any)) + it() Syntax: tt(bindto) em(ip-address) tt(;) + it() where em(ip-address) is a numeric IP address, such as + tt(192.168.1.45), or the keyword tt(any) + it() Default: tt(any)) dit(Verbosity statements) come in two forms: tt(verbosity on) or tt(verbosity off). When 'on', log messages to tt(/var/log/messages) @@ -136,7 +136,7 @@ tt(verbosity). itemization( it() Syntax: tt(verbosity) em(setting) tt(;) - it() Or: tt(verbose) em(setting) tt(;) + it() Or: tt(verbose) em(setting) tt(;) it() where em(setting) is tt(true), tt(yes) or tt(on) to turn verbosity on; or tt(false), tt(no), tt(off) to turn it off. it() Default: tt(off).) @@ -157,7 +157,7 @@ itemization( it() tt(dispatchmode random): Random selection. Probably only for stress testing, though when used with weights (see below) - it is a good distributor of new connections too. + it is a good distributor of new connections too. it() tt(dispatchmode bysize [ over) em(connections) tt(]): The next back end is the one @@ -177,10 +177,10 @@ itemization( that served connections for the shortest time. This mechanism assumes that the longer the connection, the heavier the load. - it() tt(dispatchmode byconnections): The next back end is the one - with the least active connections. This mechanism assumes that - each connection to a back end represents load. It is usable - for e.g. database connections. + it() tt(dispatchmode byconnections): The next back end is the one + with the least active connections. This mechanism assumes that + each connection to a back end represents load. It is usable + for e.g. database connections. it() tt(dispatchmode byorder): The first back end is selected every time, unless it's unavailable. In that case the second @@ -190,12 +190,12 @@ The selection algorithm is only used when clients are serviced that aren't part of a sticky HTTP session. This is the case during: itemization( - it() all client requests of a service type tt(any); - it() new sessions of a service type tt(stickyhttp).) + it() all client requests of a service type tt(any); + it() new sessions of a service type tt(http).) -When tt(stickyhttp) is in effect and a session is underway, then the +When type tt(http) is in effect and a session is underway, then the previously used back end is always selected -- regardless of -dispatching mode. +dispatching mode. Your 'right' dispatch mode will depend on the type of service. Given the fact that crossroads doesn't know (and doesn't care) how to @@ -209,7 +209,7 @@ happen when: itemization( it() The back end cannot be reached (network connection - fails); + fails); it() The network connection to the back end suddenly dies.) An example of the definition is tt(revivinginterval 10). When this @@ -321,7 +321,7 @@ startdit() itemization( it() Syntax: tt(server) em(servername) tt(;) - it() Or: tt(server) em(servername)tt(:)em(port) tt(;) + it() Or: tt(server) em(servername)tt(:)em(port) tt(;) it() There is no default. This is a required setting.) dit(Port:) When the tt(server) specifier doesn't include a TCP @@ -332,7 +332,7 @@ startdit() itemization( it() Syntax: tt(port) em(number) tt(;) it() There is no default. The port must be defined either in - the tt(server) setting or using the tt(port) specifier.) + the tt(server) setting or using the tt(port) specifier.) dit(Verbosity:) Similar to tt(service) specifications, a tt(backend) can have its own verbosity (tt(on) or tt(off)). When @@ -340,7 +340,7 @@ startdit() itemization( it() Syntax: tt(verbosity) em(setting) tt(;) - it() Or: tt(verbose) em(setting) tt(;) + it() Or: tt(verbose) em(setting) tt(;) it() where em(setting) is tt(true), tt(yes) or tt(on) to turn verbosity on; or tt(false), tt(no), tt(off) to turn it off. it() Default: tt(off).) @@ -356,10 +356,10 @@ startdit() overloaded. itemization( - it() Syntax: tt(maxconnections) em(number) tt(;) - it() where em(number) is the maximum number of concurrent - client connections. - it() Default: 0, meaning that there is no limit.) + it() Syntax: tt(maxconnections) em(number) tt(;) + it() where em(number) is the maximum number of concurrent + client connections. + it() Default: 0, meaning that there is no limit.) dit(Weight:) To influence how backends are selected by size or by duration, a backend can specify its 'weight' in the process. The @@ -375,7 +375,7 @@ startdit() itemization( it() Syntax: tt(weight) em(number) tt(;) - it() Default: 1) + it() Default: 1) dit(Decay:) To make sure that a 'spike' of activity doesn't influence the perceived load of a back end forever, you may @@ -397,8 +397,8 @@ startdit() data when other back ends are hit it() Default: 0, meaning that no decay is applied to usage statistics.) - dit(HTTP cookie handling:) When the service type is tt(stickyhttp), - then backends should define HTTP cookies to identify sessions. There + dit(HTTP cookie handling:) When the service type is tt(http), + then backends may define HTTP cookies to identify sessions. There are two directives to accomplish this: tt(stickycookie) and tt(insertcookie). @@ -406,8 +406,8 @@ startdit() HTTP stream when crossroads assigns a back end to a new session. itemization( - it() Syntax: tt(insertcookie) em(cookiestring) tt(;) - it() Default: none) + it() Syntax: tt(insertcookie) em(cookiestring) tt(;) + it() Default: none) The directive tt(stickycookie) is used by crossroads to determine whether a HTTP request is part of an established session. If so, the @@ -415,8 +415,8 @@ startdit() considerations. itemization( - it() Syntax: tt(stickycookie) em(cookiestring) tt(;) - it() Default: none) + it() Syntax: tt(stickycookie) em(cookiestring) tt(;) + it() Default: none) It should be obvious that the em(cookiestrings) in the two above directives should match: the cookie that's initially inserted by @@ -427,7 +427,7 @@ startdit() verb(\ service www { port 80; - type stickyhttp; + type http; backend one { server 10.0.0.1:80; stickycookie BalancerID=a; @@ -442,7 +442,22 @@ service www { Note also the quoting of the cookiestring in the tt(insertcookie) directive. Without it, the semicolon in the string would break the - directive. + directive. + + dit(Passing of real IP address:) When the service type is + tt(http), the directive tt(insertrealip) can be used to force + Crossroads to insert the client's real IP address as an HTTP + header. The back end may then inspect the header. + + itemization( + it() Syntax: tt(insertrealip) em(setting) tt(;) + it() where em(setting) is tt(true), tt(yes) or tt(on) to turn + on this feature; or tt(false), tt(no) or tt(off) to turn it + off. + it() Default: tt(off)) + + When turned on, the client's IP address is stored in an HTTP + header named tt(XR-Real-IP). dit(Event triggers:) As special 'hooks' for actions, two triggers are available: tt(onfailure) and tt(onsuccess). The argument to @@ -491,7 +506,7 @@ B 0050 78 74 2f 68 74 6d 6c 3b 20 63 68 61 72 73 65 74 xt/html; charset itemization( it() Syntax: tt(trafficlog) em(filename) tt(;) it() There is no default. Without this directive, traffic is - not logged.) + not logged.) Besides tt(trafficlog), there is also a directive tt(throughputlog). This directive also takes one argument, a @@ -499,13 +514,13 @@ B 0050 78 74 2f 68 74 6d 6c 3b 20 63 68 61 72 73 65 74 xt/html; charset logged: itemization( - it() The process ID of the crossroads image that serves the - TCP connection; - it() The time of the request, in seconds and microseconds - since start of the run; - it() A bf(C) when the request originated at the client, or - bf(B) when the request originated at the back end; - it() The first 100 bytes of the request.) + it() The process ID of the crossroads image that serves the + TCP connection; + it() The time of the request, in seconds and microseconds + since start of the run; + it() A bf(C) when the request originated at the client, or + bf(B) when the request originated at the back end; + it() The first 100 bytes of the request.) As an example, consider the following (the lines are shortened for brevity and prefixed by line numbers for clarity): @@ -523,25 +538,25 @@ B 0050 78 74 2f 68 74 6d 6c 3b 20 63 68 61 72 73 65 74 xt/html; charset This tells us that: itemization( - it() Line 1: PID 594 served a request that originated at - the client. The corresponding time is (almost) 0 seconds, - so this is really the start of the run. - it() Line 2: A back end replied 0.17 seconds later, and - 0.28 seconds later, it was still replying (this is the - third line, again a bf(B)-type transmission). - it() Line 4: PID 595 served a request that originated - at the client. Again, the corresponding time is (almost) - 0 seconds, since this is the first conversation part of - this connection. - it() Lines 5 to 7: This is the continuation of line 2. Line 7 - is the last line of the bf(B) series (not visible from - the example, but trust me, it is), so that we may - conclude that it took the back end 0.96 seconds to serve - the file tt(index.html) requested in line 1. - it() Line 8: This is the answer to the client's request of - line 4 (you can tell by the process ID number). - So the back end took 0.68 seconds to confirm that - the stylesheet requested in line 4 wasn't modified.) + it() Line 1: PID 594 served a request that originated at + the client. The corresponding time is (almost) 0 seconds, + so this is really the start of the run. + it() Line 2: A back end replied 0.17 seconds later, and + 0.28 seconds later, it was still replying (this is the + third line, again a bf(B)-type transmission). + it() Line 4: PID 595 served a request that originated + at the client. Again, the corresponding time is (almost) + 0 seconds, since this is the first conversation part of + this connection. + it() Lines 5 to 7: This is the continuation of line 2. Line 7 + is the last line of the bf(B) series (not visible from + the example, but trust me, it is), so that we may + conclude that it took the back end 0.96 seconds to serve + the file tt(index.html) requested in line 1. + it() Line 8: This is the answer to the client's request of + line 4 (you can tell by the process ID number). + So the back end took 0.68 seconds to confirm that + the stylesheet requested in line 4 wasn't modified.) It is also worth while remembering that the start time of a bf(C) request is the time that crossroads sees the activity. Any latency @@ -573,9 +588,9 @@ client ----<----<----<---< crossroads ====<====<====< are not (yet) included in Crossroads. itemization( - it() Syntax: tt(throughputlog) em(filename) tt(;) - it() There is no default. Without this directive, the - throughput is not logged.) + it() Syntax: tt(throughputlog) em(filename) tt(;) + it() There is no default. Without this directive, the + throughput is not logged.) enddit() diff --git a/doc/crossroads.html b/doc/crossroads.html @@ -1,26 +1,26 @@ <a name="defs.yo"></a><html><head> -<title>Crossroads 1.05</title> +<title>Crossroads 1.07</title> <link rel="stylesheet" type="text/css" href="http://www.e-tunity.com/css/yodl.css"> <link rel="stylesheet" type="text/css" href="http://www.e-tunity.com/css/yodl.css"> <link rev="made" href="mailto:info@e-tunity.com"> </head> <body> <hr> -<h1>Crossroads 1.05</h1> +<h1>Crossroads 1.07</h1> <h2>Karel Kubat</h2> <h2>e-tunity</h2><h2>2005, 2006, ff.</h2> <blockquote><em>Crossroads is a load balance and fail over utility for TCP - based services. It is a daemon program running in user - space, and features extensive configurability, polling of - back ends using 'wakeup calls', detailed status reporting, - 'hooks' for special actions when backend calls fail, and much - more. Crossroads is service-independent: it is usable for - HTTP/HTTPS, SSH, SMTP, DNS, etc. In the case of HTTP - balancing, Crossroads can provide 'session stickiness' for - back-end processes that need sessions, but aren't - session-aware of other back-ends.</em></blockquote> + based services. It is a daemon program running in user + space, and features extensive configurability, polling of + back ends using 'wakeup calls', detailed status reporting, + 'hooks' for special actions when backend calls fail, and much + more. Crossroads is service-independent: it is usable for + HTTP/HTTPS, SSH, SMTP, DNS, etc. In the case of HTTP + balancing, Crossroads can provide 'session stickiness' for + back-end processes that need sessions, but aren't + session-aware of other back-ends.</em></blockquote> <h1>Table of Contents</h1> <dl> @@ -62,35 +62,40 @@ <dt><a href="#l23">5.2.1: Don't use stickiness!</a></dt> <dt><a href="#l24">5.2.2: But if you must..</a></dt> </dl> -<dt><a href="#l25">5.3: Configuration examples</a></dt> +<dt><a href="#l25">5.3: Passing the client's IP address</a></dt> <dl> -<dt><a href="#l26">5.3.1: A load balancer for three webserver back ends</a></dt> -<dt><a href="#l27">5.3.2: An HTTP forwarder when travelling</a></dt> -<dt><a href="#l28">5.3.3: SSH login with enforced idle logout</a></dt> +<dt><a href="#l26">5.3.1: Sample Crossroads configuration</a></dt> +<dt><a href="#l27">5.3.2: Sample Apache configuration</a></dt> +</dl> +<dt><a href="#l28">5.4: Configuration examples</a></dt> +<dl> +<dt><a href="#l29">5.4.1: A load balancer for three webserver back ends</a></dt> +<dt><a href="#l30">5.4.2: An HTTP forwarder when travelling</a></dt> +<dt><a href="#l31">5.4.3: SSH login with enforced idle logout</a></dt> </dl> </dl> -<dt><h3><a href="#l29">6: Benchmarking</a></h3></dt> +<dt><h3><a href="#l32">6: Benchmarking</a></h3></dt> <dl> -<dt><a href="#l30">6.1: Benchmark 1: Accessing a proxy via crossroads or directly</a></dt> +<dt><a href="#l33">6.1: Benchmark 1: Accessing a proxy via crossroads or directly</a></dt> <dl> -<dt><a href="#l31">6.1.1: Results</a></dt> -<dt><a href="#l32">6.1.2: Discussion</a></dt> +<dt><a href="#l34">6.1.1: Results</a></dt> +<dt><a href="#l35">6.1.2: Discussion</a></dt> </dl> -<dt><a href="#l33">6.2: Benchmark 2: Crossroads versus Linux Virtual Server (LVS)</a></dt> +<dt><a href="#l36">6.2: Benchmark 2: Crossroads versus Linux Virtual Server (LVS)</a></dt> <dl> -<dt><a href="#l34">6.2.1: Environment</a></dt> -<dt><a href="#l35">6.2.2: Tests and results</a></dt> +<dt><a href="#l37">6.2.1: Environment</a></dt> +<dt><a href="#l38">6.2.2: Tests and results</a></dt> </dl> </dl> -<dt><h3><a href="#l36">7: Compiling and Installing</a></h3></dt> +<dt><h3><a href="#l39">7: Compiling and Installing</a></h3></dt> <dl> -<dt><a href="#l37">7.1: Prerequisites</a></dt> -<dt><a href="#l38">7.2: Compiling and installing</a></dt> -<dt><a href="#l39">7.3: Configuring crossroads</a></dt> -<dt><a href="#l40">7.4: A boot script</a></dt> +<dt><a href="#l40">7.1: Prerequisites</a></dt> +<dt><a href="#l41">7.2: Compiling and installing</a></dt> +<dt><a href="#l42">7.3: Configuring crossroads</a></dt> +<dt><a href="#l43">7.4: A boot script</a></dt> <dl> -<dt><a href="#l41">7.4.1: SysV Style Startup</a></dt> -<dt><a href="#l42">7.4.2: BSD Style Startup</a></dt> +<dt><a href="#l44">7.4.1: SysV Style Startup</a></dt> +<dt><a href="#l45">7.4.2: BSD Style Startup</a></dt> </dl> <p><hr><p> @@ -130,17 +135,17 @@ also available in <a href="crossroads.pdf">PDF</a> format. As quick reference, here are some important URL's for Crossroads: <p> <ul> - <li> <a href="http:/crossroads.e-tunity.com">http:/crossroads.e-tunity.com</a> is the site that serves - Crossroads. You can browse this at leisure - for documentation, sources, and so on. + <li> <a href="http:/crossroads.e-tunity.com">http:/crossroads.e-tunity.com</a> is the site that serves + Crossroads. You can browse this at leisure + for documentation, sources, and so on. <p> <li> <a href="http://freshmeat.net/projects/crossr">http://freshmeat.net/projects/crossr</a> is the - Freshmeat announcement page. + Freshmeat announcement page. <p> <li> <a href="svn://svn.e-tunity.com/crossroads">svn://svn.e-tunity.com/crossroads</a> is the SVN - repository; anonymous reading (fetching) is allowed. In order - to commit changes, <a href="mailto:karel@e-tunity.com">mail me</a> for - credentials.</ul> + repository; anonymous reading (fetching) is allowed. In order + to commit changes, <a href="mailto:karel@e-tunity.com">mail me</a> for + credentials.</ul> <p> <a name="l3"></a> <h3>1.2: Copyright and Disclaimer</h3> @@ -171,84 +176,84 @@ more meanings of the terms will exist -- yes, I am aware of that. I'm using the terms here in a very strict sense.) <p> <dl> - <p><dt><strong>A client</strong><dd> is a process that initiates a network connection - to get contact with some service. - <p><dt><strong>A service</strong><dd> or <strong>server process</strong> or <strong>listener</strong> - is a central application - that accepts network connections from clients and sevices - them. - <p><dt><strong>Back ends</strong><dd> are locations where crossroads looks in - order to service its clients. Crossroads sits 'in between' - and does its tricks. Therefore, as far as the back ends - are concerned, crossroads behaves like a client. As far as - the true client is concerned, crossroads behaves like the - service. The communication is however transparent: neither - client nor back end are aware of the middle position of - crossroads. - <p><dt><strong>A connection</strong><dd> is a network conversation between client and service, - where data are transferred to and fro. As - far as crossroads is concerned, success means that a - connection can be established without errors on - the network level. Crossroads isn't aware of service - pecularities. E.g., when a webserver answers <code>HTTP/1.0 - 500 Server Error</code> then crossroads will see this as a - succesful connection, though the user behind a browser may - think otherwise. - <p><dt><strong>Back end selection algorithms</strong><dd> are methods by which - crossroads determines which back end it will talk to - next. Crossroads has a number of built-in algorithms, - which may be configured per service. + <p><dt><strong>A client</strong><dd> is a process that initiates a network connection + to get contact with some service. + <p><dt><strong>A service</strong><dd> or <strong>server process</strong> or <strong>listener</strong> + is a central application + that accepts network connections from clients and sevices + them. + <p><dt><strong>Back ends</strong><dd> are locations where crossroads looks in + order to service its clients. Crossroads sits 'in between' + and does its tricks. Therefore, as far as the back ends + are concerned, crossroads behaves like a client. As far as + the true client is concerned, crossroads behaves like the + service. The communication is however transparent: neither + client nor back end are aware of the middle position of + crossroads. + <p><dt><strong>A connection</strong><dd> is a network conversation between client and service, + where data are transferred to and fro. As + far as crossroads is concerned, success means that a + connection can be established without errors on + the network level. Crossroads isn't aware of service + pecularities. E.g., when a webserver answers <code>HTTP/1.0 + 500 Server Error</code> then crossroads will see this as a + succesful connection, though the user behind a browser may + think otherwise. + <p><dt><strong>Back end selection algorithms</strong><dd> are methods by which + crossroads determines which back end it will talk to + next. Crossroads has a number of built-in algorithms, + which may be configured per service. <p><dt><strong>Back end states</strong><dd> are the statusses of each back end that - is known to crossroads. A back end may be available, - (temporarily) unavailble or truly down. When a back end is - temporarily unavailable, then crossroads will periodically - check whether the back end has come to life yet (that is, - if configured so). - <p><dt><strong>A spike</strong><dd> is a sudden increase in activity, leading to - extra load on a given service. When crossroads is in - effect and when the spike occurs in one connection, - then obviously the spike will also appear at one - of the back ends. However, crossroads will see the spike - and will make sure that a subsequent request goes to an - other back end. In contrast, when several connections - arrive simultaneously and cause a spike, then crossroads - will be able to distribute the connections over several - back ends, thereby 'flattening out' the increase. - <p><dt><strong>Load balancing</strong><dd> means that incoming client requests are - distributed over more than just one back end (which wouldn't be the - case if you wouldn't be running crossroads). Enabling load - balancing is nothing more than duplicating services over - more than one back end, and having something (in this - case: crossroads) distribute the requests, so that per - back end the load doesn't get too high. - <p><dt><strong>An HTTP session</strong><dd> is a series of separate network connections - that originate from one browser. E.g., to fill the display - with text and images, the browser hits a website several times. - An HTTP session may even span several - screens. E.g., a website registration dialog may involve 3 - screens that when called from the same browser, - form a logical group of some sort. - <p><dt><strong>Session stickiness</strong><dd> means that when a browser starts an - HTTP dialog, the balancer makes sure that it 'sticks' to - the same back end (i.e., subsequent requests from the - browser are forced to go to the same back end, instead of - being balanced to other ones). - <p><dt><strong>Back end usage</strong><dd> is measured by crossroads in order to be - able to determine back end selection. Crossroads stores - information about the number of active connections, the - transferred bytes and - about the connection duration. These numbers can be used to - estimate which back end is the least used -- and - therefore, presumably, the best candidate for a new - request. - <p><dt><strong>Fail over</strong><dd> is almost always used when load balancing is in - effect. The distributor of client requests (crossroads of - course) can also monitor back ends, so that incase a back - end is 'down', it is no longer accessed. - <p><dt><strong>Service downtime</strong><dd> normally occurs when a service is - switched off. Downtime is obviously avoided when fail over - is in effect: a back end can be taken out of service in a - controlled manner, without any client noticing it. + is known to crossroads. A back end may be available, + (temporarily) unavailble or truly down. When a back end is + temporarily unavailable, then crossroads will periodically + check whether the back end has come to life yet (that is, + if configured so). + <p><dt><strong>A spike</strong><dd> is a sudden increase in activity, leading to + extra load on a given service. When crossroads is in + effect and when the spike occurs in one connection, + then obviously the spike will also appear at one + of the back ends. However, crossroads will see the spike + and will make sure that a subsequent request goes to an + other back end. In contrast, when several connections + arrive simultaneously and cause a spike, then crossroads + will be able to distribute the connections over several + back ends, thereby 'flattening out' the increase. + <p><dt><strong>Load balancing</strong><dd> means that incoming client requests are + distributed over more than just one back end (which wouldn't be the + case if you wouldn't be running crossroads). Enabling load + balancing is nothing more than duplicating services over + more than one back end, and having something (in this + case: crossroads) distribute the requests, so that per + back end the load doesn't get too high. + <p><dt><strong>An HTTP session</strong><dd> is a series of separate network connections + that originate from one browser. E.g., to fill the display + with text and images, the browser hits a website several times. + An HTTP session may even span several + screens. E.g., a website registration dialog may involve 3 + screens that when called from the same browser, + form a logical group of some sort. + <p><dt><strong>Session stickiness</strong><dd> means that when a browser starts an + HTTP dialog, the balancer makes sure that it 'sticks' to + the same back end (i.e., subsequent requests from the + browser are forced to go to the same back end, instead of + being balanced to other ones). + <p><dt><strong>Back end usage</strong><dd> is measured by crossroads in order to be + able to determine back end selection. Crossroads stores + information about the number of active connections, the + transferred bytes and + about the connection duration. These numbers can be used to + estimate which back end is the least used -- and + therefore, presumably, the best candidate for a new + request. + <p><dt><strong>Fail over</strong><dd> is almost always used when load balancing is in + effect. The distributor of client requests (crossroads of + course) can also monitor back ends, so that incase a back + end is 'down', it is no longer accessed. + <p><dt><strong>Service downtime</strong><dd> normally occurs when a service is + switched off. Downtime is obviously avoided when fail over + is in effect: a back end can be taken out of service in a + controlled manner, without any client noticing it. </dl> <p> <a name="l5"></a> @@ -258,10 +263,10 @@ As of version 0.26 the syntax of the configuration file has changed. In particular: <p> <ul> - <li> The keyword <code>maxconnections</code> is now used instead of - <code>maxclients</code>; - <li> The keyword <code>connectiontimeout</code> is now used instead of - <code>sessiontimeout</code>.</ul> + <li> The keyword <code>maxconnections</code> is now used instead of + <code>maxclients</code>; + <li> The keyword <code>connectiontimeout</code> is now used instead of + <code>sessiontimeout</code>.</ul> <p> Therefore when converting configuration files to the new syntax, the above keywords must be changed. (The reason for these changes @@ -281,37 +286,37 @@ for getting crossroads up and running: <li> If you don't have SVN or don't want to use it: <p> <ul> - <li> Obtain the crossroads source archive at - <a href="http://crossroads.e-tunity.com">http://crossroads.e-tunity.com</a>. + <li> Obtain the crossroads source archive at + <a href="http://crossroads.e-tunity.com">http://crossroads.e-tunity.com</a>. <p> <li> Change-dir to a 'sources' directory on your system and - unpack the archive. + unpack the archive. <p> <li> Change-dir into the create directory <code>crossroads/</code>.</ul> <p> <li> If you have SVN and want to go for the newest snapshot: <p> <ul> - <li> Get the latest sources and snapshots using SVN from <br> + <li> Get the latest sources and snapshots using SVN from <br> <code>svn://svn.e-tunity.com/crossroads</code>. <p> <li> You'll find the newest alpha version under - <code>crossroads/trunk</code> and the stable versions under - <code>crossroads/tags</code>, - e.g. <code>crossroads/tags/release-1.00</code>. + <code>crossroads/trunk</code> and the stable versions under + <code>crossroads/tags</code>, + e.g. <code>crossroads/tags/release-1.00</code>. <p> <li> Choose which you want to use: the latest stable - release, or the bleeding edge alpha? In the former case, - change-dir to <code>crossroads/tags/release-</code><em>X.YY</em>, where - <em>X.YY</em> is a release ID. In the latter case, change-dir to - <code>crossroads/trunk</code>.</ul> + release, or the bleeding edge alpha? In the former case, + change-dir to <code>crossroads/tags/release-</code><em>X.YY</em>, where + <em>X.YY</em> is a release ID. In the latter case, change-dir to + <code>crossroads/trunk</code>.</ul> <p> <li> Type <code>make install</code>. This installs the crossroads - binary into <code>/usr/local/bin/</code>. If the compilation doesn't - work on your system, check <code>etc/Makefile.def</code> for hints. + binary into <code>/usr/local/bin/</code>. If the compilation doesn't + work on your system, check <code>etc/Makefile.def</code> for hints. <p> <li> Create a file <code>/etc/crossroads.conf</code>. In it state - something like: + something like: <p> <pre> service www { @@ -328,16 +333,16 @@ service www { <p> That's off course assuming that you want to balance HTTP on - port 80 to two back ends at 10.1.1.100 and 10.1.1.101. + port 80 to two back ends at 10.1.1.100 and 10.1.1.101. <p> <li> Type <code>crossroads start</code>. <p> <li> Surf to the machine where crossroads is running. You will - see the pages served by the back ends 10.1.1.100 or - 10.1.1.101. + see the pages served by the back ends 10.1.1.100 or + 10.1.1.101. <p> <li> To monitor the status of crossroads, type <code>crossroads - status</code>. + status</code>. </ul> <p> <a name="l7"></a> @@ -353,33 +358,33 @@ This section shows the most basic usage. As said above, start <code>crossroads</code> without arguments to view the full listing of options. <p> <ul> - <li> <code>crossroads start</code> and <code>crossroads stop</code> are typical - actions that are run from system startup scripts. The - meaning is self-explanatory. - <li> <code>crossroads restart</code> is a combination of the former - two. Beware that a restart may cause discontinuity in - service; it is just a shorthand for typing the 'stop' and - 'start' actions after one another. - <li> <code>crossroad status</code> reports on each running - service. Per service, the state of each back end is - reported. - <li> <code>crossroads tell</code> <em>service backend state</em> is a - command line way of telling crossroads that a given back - end, of a given service, is in a given state. Normally - crossroads maintains state information itself, but by - using <code>crossroads tell</code>, a back end can be e.g. taken - 'off line' for servicing. - <li> <code>crossroads configtest</code> tells you whether the - configuration is syntactially correct. - <li> <code>crossroads services</code> reports on the configured - services. In contrast to <code>crossroads status</code>, this - option only shows what's configured -- not what's up and - running. Therefore, <code>crossroads services</code> doesn't - report on back end states. - <li> <code>crossroads sampleconf</code> shows a sample configuration on - screen. A good way of quicky viewing the configuration - file syntax, or of getting a start for your own - configuration <code>/etc/crossroads.conf</code>. + <li> <code>crossroads start</code> and <code>crossroads stop</code> are typical + actions that are run from system startup scripts. The + meaning is self-explanatory. + <li> <code>crossroads restart</code> is a combination of the former + two. Beware that a restart may cause discontinuity in + service; it is just a shorthand for typing the 'stop' and + 'start' actions after one another. + <li> <code>crossroad status</code> reports on each running + service. Per service, the state of each back end is + reported. + <li> <code>crossroads tell</code> <em>service backend state</em> is a + command line way of telling crossroads that a given back + end, of a given service, is in a given state. Normally + crossroads maintains state information itself, but by + using <code>crossroads tell</code>, a back end can be e.g. taken + 'off line' for servicing. + <li> <code>crossroads configtest</code> tells you whether the + configuration is syntactially correct. + <li> <code>crossroads services</code> reports on the configured + services. In contrast to <code>crossroads status</code>, this + option only shows what's configured -- not what's up and + running. Therefore, <code>crossroads services</code> doesn't + report on back end states. + <li> <code>crossroads sampleconf</code> shows a sample configuration on + screen. A good way of quicky viewing the configuration + file syntax, or of getting a start for your own + configuration <code>/etc/crossroads.conf</code>. </ul> <p> <a name="l8"></a> @@ -481,12 +486,12 @@ character <code>;</code>. If a string must contain a semicolon, then it must be enclosed in single or double quotes: <p> <ul> - <li> <code>This is a string;</code> is a string that starts at <code>T</code> - and ends with <code>g</code> - <li> <code>"This is a string";</code> is the same, the double quotes - are not necessary - <li> <code>"This is ; a string";</code> has double quotes to protect - the inner ;</ul> + <li> <code>This is a string;</code> is a string that starts at <code>T</code> + and ends with <code>g</code> + <li> <code>"This is a string";</code> is the same, the double quotes + are not necessary + <li> <code>"This is ; a string";</code> has double quotes to protect + the inner ;</ul> <p> Finally, an argument can be a 'boolean' value. Crossroads knows <code>true</code>, <code>false</code>, <code>yes</code>, <code>no</code>, <code>on</code>, <code>off</code>. The keywords @@ -521,20 +526,20 @@ statements. Each statement must end with a semicolon, except for the <p> <p><dt><strong>The type statement</strong><dd> defines how crossroads handles the stated service. There are currently two types: <code>any</code> and -<code>stickyhttp</code>. The type <code>any</code> means that crossroads doesn't +<code>http</code>. The type <code>any</code> means that crossroads doesn't interpret the contents of a TCP stream, but only distributes streams -over back ends. The type <code>stickyhttp</code> means that crossroads has to -analyze what's in the messages, does magical HTTP cookie tricks, and -so on -- to ensure that multiple connections are treated as one -session. +over back ends. The type <code>http</code> means that crossroads has to +analyze what's in the messages, does magical HTTP header tricks, and +so on -- all to ensure that multiple connections are treated as one +session, or that the back end is notified of the client's IP address. <p> -Unless you really need sticky HTTP sessions, use the type <code>any</code> (the +Unless you really need such special features, use the type <code>any</code> (the default), even for HTTP protocols. <p> <ul> - <li> Syntax: <code>type</code> <em>typespecifier</em> <code>;</code> - <li> Where <em>typespecifier</em> is <code>any</code> or <code>stickyhttp</code> - <li> Default: <code>any</code></ul> + <li> Syntax: <code>type</code> <em>typespecifier</em> <code>;</code> + <li> Where <em>typespecifier</em> is <code>any</code> or <code>http</code> + <li> Default: <code>any</code></ul> <p> <p><dt><strong>The port statement</strong><dd> defines to which TCP port a service 'listens'. E.g. <code>port 8000</code> says that this service will accept @@ -552,10 +557,10 @@ serviced. By default, crossroads binds a service to all presently active IP addresses at the invoking host. <p> <ul> - <li> Syntax: <code>bindto</code> <em>ip-address</em> <code>;</code> - <li> where <em>ip-address</em> is a numeric IP address, such as - <code>192.168.1.45</code>, or the keyword <code>any</code> - <li> Default: <code>any</code></ul> + <li> Syntax: <code>bindto</code> <em>ip-address</em> <code>;</code> + <li> where <em>ip-address</em> is a numeric IP address, such as + <code>192.168.1.45</code>, or the keyword <code>any</code> + <li> Default: <code>any</code></ul> <p> <p><dt><strong>Verbosity statements</strong><dd> come in two forms: <code>verbosity on</code> or <code>verbosity off</code>. When 'on', log messages to <code>/var/log/messages</code> @@ -568,7 +573,7 @@ priority <code>LOG_INFO</code>. In most (Linux) cases this will mean: output to <p> <ul> <li> Syntax: <code>verbosity</code> <em>setting</em> <code>;</code> - <li> Or: <code>verbose</code> <em>setting</em> <code>;</code> + <li> Or: <code>verbose</code> <em>setting</em> <code>;</code> <li> where <em>setting</em> is <code>true</code>, <code>yes</code> or <code>on</code> to turn verbosity on; or <code>false</code>, <code>no</code>, <code>off</code> to turn it off. <li> Default: <code>off</code>.</ul> @@ -589,7 +594,7 @@ Roundrobin dispatching is the default method, when no <p> <li> <code>dispatchmode random</code>: Random selection. Probably only for stress testing, though when used with weights (see below) - it is a good distributor of new connections too. + it is a good distributor of new connections too. <p> <li> <code>dispatchmode bysize [ over</code> <em>connections</em> <code>]</code>: The next back end is the one @@ -610,9 +615,9 @@ The modifier <code>over</code> <em>connections</em> is optional. (The square assumes that the longer the connection, the heavier the load. <p> <li> <code>dispatchmode byconnections</code>: The next back end is the one - with the least active connections. This mechanism assumes that - each connection to a back end represents load. It is usable - for e.g. database connections. + with the least active connections. This mechanism assumes that + each connection to a back end represents load. It is usable + for e.g. database connections. <p> <li> <code>dispatchmode byorder</code>: The first back end is selected every time, unless it's unavailable. In that case the second @@ -622,12 +627,12 @@ The selection algorithm is only used when clients are serviced that aren't part of a sticky HTTP session. This is the case during: <p> <ul> - <li> all client requests of a service type <code>any</code>; - <li> new sessions of a service type <code>stickyhttp</code>.</ul> + <li> all client requests of a service type <code>any</code>; + <li> new sessions of a service type <code>http</code>.</ul> <p> -When <code>stickyhttp</code> is in effect and a session is underway, then the +When type <code>http</code> is in effect and a session is underway, then the previously used back end is always selected -- regardless of -dispatching mode. +dispatching mode. <p> Your 'right' dispatch mode will depend on the type of service. Given the fact that crossroads doesn't know (and doesn't care) how to @@ -641,7 +646,7 @@ happen when: <p> <ul> <li> The back end cannot be reached (network connection - fails); + fails); <li> The network connection to the back end suddenly dies.</ul> <p> An example of the definition is <code>revivinginterval 10</code>. When this @@ -755,7 +760,7 @@ The statements in the backend definition blocks are: <p> <ul> <li> Syntax: <code>server</code> <em>servername</em> <code>;</code> - <li> Or: <code>server</code> <em>servername</em><code>:</code><em>port</em> <code>;</code> + <li> Or: <code>server</code> <em>servername</em><code>:</code><em>port</em> <code>;</code> <li> There is no default. This is a required setting.</ul> <p> <p><dt><strong>Port:</strong><dd> When the <code>server</code> specifier doesn't include a TCP @@ -766,7 +771,7 @@ The statements in the backend definition blocks are: <ul> <li> Syntax: <code>port</code> <em>number</em> <code>;</code> <li> There is no default. The port must be defined either in - the <code>server</code> setting or using the <code>port</code> specifier.</ul> + the <code>server</code> setting or using the <code>port</code> specifier.</ul> <p> <p><dt><strong>Verbosity:</strong><dd> Similar to <code>service</code> specifications, a <code>backend</code> can have its own verbosity (<code>on</code> or <code>off</code>). When @@ -774,7 +779,7 @@ The statements in the backend definition blocks are: <p> <ul> <li> Syntax: <code>verbosity</code> <em>setting</em> <code>;</code> - <li> Or: <code>verbose</code> <em>setting</em> <code>;</code> + <li> Or: <code>verbose</code> <em>setting</em> <code>;</code> <li> where <em>setting</em> is <code>true</code>, <code>yes</code> or <code>on</code> to turn verbosity on; or <code>false</code>, <code>no</code>, <code>off</code> to turn it off. <li> Default: <code>off</code>.</ul> @@ -790,10 +795,10 @@ The difference is that a <code>maxconnections</code> statement at the level of overloaded. <p> <ul> - <li> Syntax: <code>maxconnections</code> <em>number</em> <code>;</code> - <li> where <em>number</em> is the maximum number of concurrent - client connections. - <li> Default: 0, meaning that there is no limit.</ul> + <li> Syntax: <code>maxconnections</code> <em>number</em> <code>;</code> + <li> where <em>number</em> is the maximum number of concurrent + client connections. + <li> Default: 0, meaning that there is no limit.</ul> <p> <p><dt><strong>Weight:</strong><dd> To influence how backends are selected by size or by duration, a backend can specify its 'weight' in the process. The @@ -809,7 +814,7 @@ The weighing mechanism only applies to the dispatch modes <p> <ul> <li> Syntax: <code>weight</code> <em>number</em> <code>;</code> - <li> Default: 1</ul> + <li> Default: 1</ul> <p> <p><dt><strong>Decay:</strong><dd> To make sure that a 'spike' of activity doesn't influence the perceived load of a back end forever, you may @@ -831,8 +836,8 @@ This means that when a given back end is hit, then its usage data data when other back ends are hit <li> Default: 0, meaning that no decay is applied to usage statistics.</ul> <p> -<p><dt><strong>HTTP cookie handling:</strong><dd> When the service type is <code>stickyhttp</code>, - then backends should define HTTP cookies to identify sessions. There +<p><dt><strong>HTTP cookie handling:</strong><dd> When the service type is <code>http</code>, + then backends may define HTTP cookies to identify sessions. There are two directives to accomplish this: <code>stickycookie</code> and <code>insertcookie</code>. <p> @@ -840,8 +845,8 @@ The directive <code>insertcookie</code> is used to insert a cookie into the HTTP stream when crossroads assigns a back end to a new session. <p> <ul> - <li> Syntax: <code>insertcookie</code> <em>cookiestring</em> <code>;</code> - <li> Default: none</ul> + <li> Syntax: <code>insertcookie</code> <em>cookiestring</em> <code>;</code> + <li> Default: none</ul> <p> The directive <code>stickycookie</code> is used by crossroads to determine whether a HTTP request is part of an established session. If so, the @@ -849,8 +854,8 @@ The directive <code>stickycookie</code> is used by crossroads to determine considerations. <p> <ul> - <li> Syntax: <code>stickycookie</code> <em>cookiestring</em> <code>;</code> - <li> Default: none</ul> + <li> Syntax: <code>stickycookie</code> <em>cookiestring</em> <code>;</code> + <li> Default: none</ul> <p> It should be obvious that the <em>cookiestrings</em> in the two above directives should match: the cookie that's initially inserted by @@ -861,7 +866,7 @@ It should be obvious that the <em>cookiestrings</em> in the two above <pre> service www { port 80; - type stickyhttp; + type http; backend one { server 10.0.0.1:80; stickycookie BalancerID=a; @@ -878,7 +883,22 @@ service www { <p> Note also the quoting of the cookiestring in the <code>insertcookie</code> directive. Without it, the semicolon in the string would break the - directive. + directive. +<p> +<p><dt><strong>Passing of real IP address:</strong><dd> When the service type is + <code>http</code>, the directive <code>insertrealip</code> can be used to force + Crossroads to insert the client's real IP address as an HTTP + header. The back end may then inspect the header. +<p> +<ul> + <li> Syntax: <code>insertrealip</code> <em>setting</em> <code>;</code> + <li> where <em>setting</em> is <code>true</code>, <code>yes</code> or <code>on</code> to turn + on this feature; or <code>false</code>, <code>no</code> or <code>off</code> to turn it + off. + <li> Default: <code>off</code></ul> +<p> +When turned on, the client's IP address is stored in an HTTP + header named <code>XR-Real-IP</code>. <p> <p><dt><strong>Event triggers:</strong><dd> As special 'hooks' for actions, two triggers are available: <code>onfailure</code> and <code>onsuccess</code>. The argument to @@ -929,7 +949,7 @@ Turning on traffic dumps will <em>significantly</em> <ul> <li> Syntax: <code>trafficlog</code> <em>filename</em> <code>;</code> <li> There is no default. Without this directive, traffic is - not logged.</ul> + not logged.</ul> <p> Besides <code>trafficlog</code>, there is also a directive <code>throughputlog</code>. This directive also takes one argument, a @@ -937,13 +957,13 @@ Besides <code>trafficlog</code>, there is also a directive logged: <p> <ul> - <li> The process ID of the crossroads image that serves the - TCP connection; - <li> The time of the request, in seconds and microseconds - since start of the run; - <li> A <strong>C</strong> when the request originated at the client, or - <strong>B</strong> when the request originated at the back end; - <li> The first 100 bytes of the request.</ul> + <li> The process ID of the crossroads image that serves the + TCP connection; + <li> The time of the request, in seconds and microseconds + since start of the run; + <li> A <strong>C</strong> when the request originated at the client, or + <strong>B</strong> when the request originated at the back end; + <li> The first 100 bytes of the request.</ul> <p> As an example, consider the following (the lines are shortened for brevity and prefixed by line numbers for clarity): @@ -964,25 +984,25 @@ As an example, consider the following (the lines are shortened for This tells us that: <p> <ul> - <li> Line 1: PID 594 served a request that originated at - the client. The corresponding time is (almost) 0 seconds, - so this is really the start of the run. - <li> Line 2: A back end replied 0.17 seconds later, and - 0.28 seconds later, it was still replying (this is the - third line, again a <strong>B</strong>-type transmission). - <li> Line 4: PID 595 served a request that originated - at the client. Again, the corresponding time is (almost) - 0 seconds, since this is the first conversation part of - this connection. - <li> Lines 5 to 7: This is the continuation of line 2. Line 7 - is the last line of the <strong>B</strong> series (not visible from - the example, but trust me, it is), so that we may - conclude that it took the back end 0.96 seconds to serve - the file <code>index.html</code> requested in line 1. - <li> Line 8: This is the answer to the client's request of - line 4 (you can tell by the process ID number). - So the back end took 0.68 seconds to confirm that - the stylesheet requested in line 4 wasn't modified.</ul> + <li> Line 1: PID 594 served a request that originated at + the client. The corresponding time is (almost) 0 seconds, + so this is really the start of the run. + <li> Line 2: A back end replied 0.17 seconds later, and + 0.28 seconds later, it was still replying (this is the + third line, again a <strong>B</strong>-type transmission). + <li> Line 4: PID 595 served a request that originated + at the client. Again, the corresponding time is (almost) + 0 seconds, since this is the first conversation part of + this connection. + <li> Lines 5 to 7: This is the continuation of line 2. Line 7 + is the last line of the <strong>B</strong> series (not visible from + the example, but trust me, it is), so that we may + conclude that it took the back end 0.96 seconds to serve + the file <code>index.html</code> requested in line 1. + <li> Line 8: This is the answer to the client's request of + line 4 (you can tell by the process ID number). + So the back end took 0.68 seconds to confirm that + the stylesheet requested in line 4 wasn't modified.</ul> <p> It is also worth while remembering that the start time of a <strong>C</strong> request is the time that crossroads sees the activity. Any latency @@ -1017,9 +1037,9 @@ Summarizing, the throughput times of a client-back end connection are not (yet) included in Crossroads. <p> <ul> - <li> Syntax: <code>throughputlog</code> <em>filename</em> <code>;</code> - <li> There is no default. Without this directive, the - throughput is not logged.</ul> + <li> Syntax: <code>throughputlog</code> <em>filename</em> <code>;</code> + <li> There is no default. Without this directive, the + throughput is not logged.</ul> <p> </dl> <p> @@ -1068,23 +1088,23 @@ must therefore give the right hints: calls and other HTTP requests). <p> <li> As a last remark, the dispatching mode <code>byconnections</code> can - be used if you don't have other clues for load - estimations. + be used if you don't have other clues for load + estimations. <p> E.g., consider a database connection. What's - heavier on the back end, time-consuming connections, or connections - where loads of bytes are transferred? Well, that depends. A - tough <code>select</code> query that joins multiple tables can be very - heavy on the back end, though the response set can be quite - small - and hence the number of - transferred bytes. That would suggest - dispatching by duration. However, <code>byduration</code> - balancing doesn't respresent the true world, when interactive - connections can occur where users have an idle TCP connection to - the database: - this consumes time, but no bytes (see the SSH login example - above). In this case, the dispatch mode <code>byconnections</code> may be - your best bet. + heavier on the back end, time-consuming connections, or connections + where loads of bytes are transferred? Well, that depends. A + tough <code>select</code> query that joins multiple tables can be very + heavy on the back end, though the response set can be quite + small - and hence the number of + transferred bytes. That would suggest + dispatching by duration. However, <code>byduration</code> + balancing doesn't respresent the true world, when interactive + connections can occur where users have an idle TCP connection to + the database: + this consumes time, but no bytes (see the SSH login example + above). In this case, the dispatch mode <code>byconnections</code> may be + your best bet. <p> </ul> <p> @@ -1226,18 +1246,18 @@ good idea to protect your service by specifying a maximum number of concurrent connections. This protection can be specified on two levels: <p> <dl> - <p><dt><strong>On the service level</strong><dd> a statement like <code>maxconnections - 100;</code> states that the service as a whole will never - service more than 100 concurrent connections. This means that - all your back ends and the crossroads balancer itself - will be protected from being overloaded. - <p><dt><strong>On the back end level</strong><dd> a statement like <code>maxconnections 10;</code> - states that this particular back end will never have more - than 10 concurrent connections; regardless of the overall - setting on the service level. This means that this - particular back end will be protected from being - overloaded (regardless of what other back ends may - experience).</dl> + <p><dt><strong>On the service level</strong><dd> a statement like <code>maxconnections + 100;</code> states that the service as a whole will never + service more than 100 concurrent connections. This means that + all your back ends and the crossroads balancer itself + will be protected from being overloaded. + <p><dt><strong>On the back end level</strong><dd> a statement like <code>maxconnections 10;</code> + states that this particular back end will never have more + than 10 concurrent connections; regardless of the overall + setting on the service level. This means that this + particular back end will be protected from being + overloaded (regardless of what other back ends may + experience).</dl> <p> The <code>maxconnections</code> statement, combined with a back end selection algorithm, allows very fine granularity. The <code>maxconnections</code> statement @@ -1266,16 +1286,16 @@ session stickiness hampers failover, balancing and performance: <ul> <li> Failover is hampered because during the session, the balancer has to assign new connections to the same back - end that was selected at the start of a session. If the back - end suddenly goes 'down', then the session will crash. + end that was selected at the start of a session. If the back + end suddenly goes 'down', then the session will crash. <li> Balancing is hampered because at the start of the session, the balancer has selected the next-best back end. But during - the session, that back end may well become overloaded. The - balancer however must continue to send the requests there. + the session, that back end may well become overloaded. The + balancer however must continue to send the requests there. <li> Performance is hampered because crossroads needs to 'unpack' messages as they are passed to and fro. That's because - crossroads needs to check the HTTP headers in the messages - for persistence cookies.</ul> + crossroads needs to check the HTTP headers in the messages + for persistence cookies.</ul> <p> There is a number of measures that you can take to avoid using session stickiness. E.g., session data can be 'shared' between web back @@ -1292,7 +1312,7 @@ follows: <p> <ul> <li> At the level of a <code>service</code> description, set the type to - <code>stickyhttp</code>. + <code>http</code>. <li> At the level of each back end description, configure the <code>stickycookie</code> and a <code>insertcookie</code> directives.</ul> <p> @@ -1302,14 +1322,14 @@ shuttles between client and back end: <ul> <li> If there is no persistence cookie in the HTTP headers of a client's request, then the message must be the first one and - a new session should be established. - Crossroads selects an appropriate back - end, sends the message to that back end, catches the reply, - and inserts a <code>Set-Cookie</code> directive. + a new session should be established. + Crossroads selects an appropriate back + end, sends the message to that back end, catches the reply, + and inserts a <code>Set-Cookie</code> directive. <li> If there is a persistence cookie in the HTTP headers of a client's request, then the request is part of an already - established session. Crossroads analyzes the cookie and - forwards the request to the appropriate back end.</ul> + established session. Crossroads analyzes the cookie and + forwards the request to the appropriate back end.</ul> <p> Below is a short example of a configuration. <p> @@ -1322,14 +1342,14 @@ service www { backend one { server 10.1.1.100:80; - stickycookie XRID=100; - insertcookie "XRID=100; Path=/"; + stickycookie XRID=100; + insertcookie "XRID=100; Path=/"; } backend two { - server 10.1.1.101:80; - stickycookie XRID=101; - insertcookie "XRID=101; Path=/"; + server 10.1.1.101:80; + stickycookie XRID=101; + insertcookie "XRID=101; Path=/"; } } </pre> @@ -1337,17 +1357,101 @@ service www { <p> Note how the cookie names and values in the directives <code>stickycookie</code> and <code>insertcookie</code> match. That is obviously a -prerequisite for stickiness. +prerequisite for stickiness. <p> <a name="l25"></a> -<h3>5.3: Configuration examples</h3> +<h3>5.3: Passing the client's IP address</h3> +<p> +Since Crossroads just shuttles bytes to and fro, meta-information of +network connections is lost. As far as the back ends are concerned, +their connections originate at the Crossroads junction. +For example, standard Apache access logs will show the IP address of +Crossroads. +<p> +In order to compensate for this, Crossroads can insert a special +header in HTTP connections, to inform the back end of the original +client's IP address. In order to enable this, the Crossroads +configuration must state the following: +<p> +<ul> + <li> The service type must be <code>http</code>, and not <code>any</code>; + <li> In the back end definition, a statement <code>insertrealip on;</code> + must occur.</ul> +<p> +After this, HTTP traffic that arrives at the back ends has a new +header: <code>XR-Real-IP</code>, holding the client's IP address. +<strong>Note that</strong> once the type is set to <code>http</code>, Crossroads' +performance will be hampered -- all passing messages will have to be +unpacked and analyzed. +<p> +<a name="l26"></a> +<strong>5.3.1: Sample Crossroads configuration</strong> +<p> +The below sample configuration shows two HTTP back ends that receive +the client's IP address: +<p> +<pre> + +service www { + port 80; + type http; + revivinginterval 5; + dispatchmode roundrobin; + + backend one { + server 10.1.1.100:80; + insertrealip on; + } + + backend two { + server 10.1.1.200:80; + insertrealip on; + } +} +</pre> + +<p> +<a name="l27"></a> +<strong>5.3.2: Sample Apache configuration</strong> +<p> +The method by which each back end analyzes the header <code>XR-Real-IP</code> +will obviously be different per server implementations. However, a +common method with the Apache webserver is to log the client's IP +address into the access log. +<p> +Often this is accomplished using the log format <code>custom</code>, defined as +follows: +<p> +<pre> +LogFormat "%h %l %u %t %D \"%r\" %&gt;s %b" common +CustomLog logs/access_log common +</pre> + +<p> +The first line defines the format <code>common</code>, with the remote host +specified by <code>%h</code>. The second line sends access information to a log +file <code>logs/access_log</code>, using the previously defined format +<code>common</code>. +<p> +Furtunately, Apache's <code>LogFormat</code> allows one to log contents of +headers. By replacing the <code>%h</code> with <code>%{XR-Real-IP}i</code>, the desired +information is sent to the log. Therefore, normally you can simply +redefine the <code>common</code> format to +<p> +<pre> +LogFormat "%{XR-Real-IP}i %l %u %t %D \"%r\" %&gt;s %b" common +</pre> + +<p> +<a name="l28"></a> +<h3>5.4: Configuration examples</h3> <p> As a general hint, use <code>crossroads sampleconf</code> to view the most up-to-date examples of configurations. The description below shows a few examples too. <p> -<a name="l26"></a> -<strong>5.3.1: A load balancer for three webserver back ends</strong> +<a name="l29"></a> +<strong>5.4.1: A load balancer for three webserver back ends</strong> <p> The following configuration example binds crossroads to port 80 of the current server, and distributes the load over three back ends. This @@ -1475,8 +1579,8 @@ service www { </pre> <p> -<a name="l27"></a> -<strong>5.3.2: An HTTP forwarder when travelling</strong> +<a name="l30"></a> +<strong>5.4.2: An HTTP forwarder when travelling</strong> <p> As another example, here's my <code>crossroads.conf</code> that I use on my Unix laptop. The problem that I face is that I need many HTTP proxy @@ -1501,7 +1605,7 @@ Here's how it used to be before crossroads: <p> <li> And in yet other instances, I use a HTTP diagnostic tool <a href="http://www.xk72.com/charles">Charles</a> - that sits between browser and website and shows me + that sits between browser and website and shows me what's happening. I run charles on my own machine and it listens to port 8888, behaving like a proxy. The browser configuration for the proxy is then @@ -1564,8 +1668,8 @@ and <code>LocalSquid</code> are both active, then <code>crossroads tell httpprox sshtunnel down</code> will 'take down' the back end <code>SshTunnel</code> -- and will automatically cause crossroads to switch to <code>LocalSquid</code>. <p> -<a name="l28"></a> -<strong>5.3.3: SSH login with enforced idle logout</strong> +<a name="l31"></a> +<strong>5.4.3: SSH login with enforced idle logout</strong> <p> The following example shows how crossroads 'throttles' SSH logins. Connections are accepted on port @@ -1590,24 +1694,24 @@ service Ssh { </pre> <p> -<a name="l29"></a> +<a name="l32"></a> <h2>6: Benchmarking</h2> <a name="benchmarking"></a>This section shows how crossroads affects the transmitting of HTML data when used as an intermediate 'station' through which all data travels. <p> -<a name="l30"></a> +<a name="l33"></a> <h3>6.1: Benchmark 1: Accessing a proxy via crossroads or directly</h3> <p> The benchmark was run on a system where the following was varied: <p> <ol> - <li> A website was recursively spidered through a local squid - proxy. The spidering was repeated 10 times, the total was recorded. + <li> A website was recursively spidered through a local squid + proxy. The spidering was repeated 10 times, the total was recorded. <p> <li> Crossroads was placed in front of the squid proxy, and - the website was again recursively spidered. Again, the - spidering was repeated 10 times and the total was recorded.</ol> + the website was again recursively spidered. Again, the + spidering was repeated 10 times and the total was recorded.</ol> <p> The crossroads configuration of the second alternative is shown below: <p> @@ -1624,7 +1728,7 @@ service HttpProxy { </pre> <p> -<a name="l31"></a> +<a name="l34"></a> <strong>6.1.1: Results</strong> <p> The results of this test are that crossroads causes a negligible @@ -1647,7 +1751,7 @@ sys 0m0.230s </pre> <p> -<a name="l32"></a> +<a name="l35"></a> <strong>6.1.2: Discussion</strong> <p> The above shown results are quite favorable to crossroads. However, @@ -1668,18 +1772,18 @@ This worst case scenario will however (fortunately) occur only very seldom in the real world: <p> <ul> - <li> Normally network issues, such as the above mentioned host - name lookups or throughput restrictions, will add - significantly to the duration of a request. The 'twice as - many' read/writes caused by crossroads are then relatively - irrelevant. + <li> Normally network issues, such as the above mentioned host + name lookups or throughput restrictions, will add + significantly to the duration of a request. The 'twice as + many' read/writes caused by crossroads are then relatively + irrelevant. <p> <li> Normally a significant amount of time will be spent in a - back end, due to processing (e.g., when calling a servlet on a - back end). Again, this processing time will weigh much heavier - than the multiple read/writes.</ul> + back end, due to processing (e.g., when calling a servlet on a + back end). Again, this processing time will weigh much heavier + than the multiple read/writes.</ul> <p> -<a name="l33"></a> +<a name="l36"></a> <h3>6.2: Benchmark 2: Crossroads versus Linux Virtual Server (LVS)</h3> <p> LVS is a kernel-based balancer that acts like a masquerading @@ -1693,7 +1797,7 @@ LVS isn't aware of downtime of back ends (unless one implements an external heartbeat). Also, crossroads offers more complex balancing than LVS. <p> -<a name="l34"></a> +<a name="l37"></a> <strong>6.2.1: Environment</strong> <p> On the balancer, LVS was run on port 80, its forwarding set up for two @@ -1724,7 +1828,7 @@ service http { </pre> <p> -<a name="l35"></a> +<a name="l38"></a> <strong>6.2.2: Tests and results</strong> <p> In the first test, ports 80 and 81 on the balancer were 'bombed' with @@ -1732,16 +1836,16 @@ In the first test, ports 80 and 81 on the balancer were 'bombed' with following timings where measured: <p> <ul> - <li> How long it takes to establish a connection; - <li> How long it takes to retrieve the page.</ul> + <li> How long it takes to establish a connection; + <li> How long it takes to retrieve the page.</ul> <p> The results of this test were: <p> <ul> - <li> On average, each client took 0.12 seconds to connect - to LVS, and each page was retrieved in 0.14 seconds; - <li> On average, each client took 0.11 seconds to connect to - crossroads, and each page was retrieved in 0.13 seconds.</ul> + <li> On average, each client took 0.12 seconds to connect + to LVS, and each page was retrieved in 0.14 seconds; + <li> On average, each client took 0.11 seconds to connect to + crossroads, and each page was retrieved in 0.13 seconds.</ul> <p> In this setup there seems to be no difference between the performance of LVS and crossroads! @@ -1803,16 +1907,16 @@ are shown in the below table: Again, the results show that crossroads performs just as effectively as LVS, even with large data chunks! <p> -<a name="l36"></a> +<a name="l39"></a> <h2>7: Compiling and Installing</h2> -<a name="compiling"></a><a name="l37"></a> +<a name="compiling"></a><a name="l40"></a> <h3>7.1: Prerequisites</h3> <p> The creation of crossroads requires: <p> <ul> - <li> Standard Unix tools, such as <code>sed</code>, <code>awk</code>, <code>Perl</code> - (5.00 or better); + <li> Standard Unix tools, such as <code>sed</code>, <code>awk</code>, <code>Perl</code> + (5.00 or better); <p> <li> A POSIX-compliant C compiler; <p> @@ -1825,65 +1929,65 @@ Basically a Linux or Apple MacOSX box will do nicely once you make sure that <code>bison</code> and <code>flex</code> are installed. To compile and install crossroads, follow these steps. <p> -<a name="l38"></a> +<a name="l41"></a> <h3>7.2: Compiling and installing</h3> <p> <ul> - <li> Obtain the source distribution. It can be found on - <a href="http://crossroads.e-tunity.com">http://crossroads.e-tunity.com</a>. The distribution comes as an - archive <code>crossroads-</code><em>type</em><code>.tar.gz</code>, where <em>type</em> is - <code>stable</code> or <code>devel</code>. + <li> Obtain the source distribution. It can be found on + <a href="http://crossroads.e-tunity.com">http://crossroads.e-tunity.com</a>. The distribution comes as an + archive <code>crossroads-</code><em>type</em><code>.tar.gz</code>, where <em>type</em> is + <code>stable</code> or <code>devel</code>. <p> <li> Unpack the archive in a sources directory using <code>tar - xzf crossroads-</code><em>X.YY</em><code>.tar.gz</code>. The contents spill into a - subdirectory <code>crossroads-</code><em>X.YY/</em>. + xzf crossroads-</code><em>X.YY</em><code>.tar.gz</code>. The contents spill into a + subdirectory <code>crossroads-</code><em>X.YY/</em>. <p> <li> Change-dir into the directory. <p> <li> Next, edit <code>etc/Makefile.def</code> and verify that all - compilation settings are to your likings. The settings are - explained in the file. <strong>Note that</strong> the default distribution - of <code>Makefile.def</code> is suited for Linux or Apple MacOSX - systems. On other Unices, or on non-Unix systems, you must - particularly pay attention to <code>SET_PROC_TITLE_BY...</code>. When - in doubt, comment out all <code>SET_PROC_TITLE...</code> - settings. Crossroads will work nevertheless, but it won't show - nice titles in <code>ps</code> listings. Also there's a macro - <code>EXTRA_LIBS</code> to add linkage flags (an example for a Solaris - build is included). + compilation settings are to your likings. The settings are + explained in the file. <strong>Note that</strong> the default distribution + of <code>Makefile.def</code> is suited for Linux or Apple MacOSX + systems. On other Unices, or on non-Unix systems, you must + particularly pay attention to <code>SET_PROC_TITLE_BY...</code>. When + in doubt, comment out all <code>SET_PROC_TITLE...</code> + settings. Crossroads will work nevertheless, but it won't show + nice titles in <code>ps</code> listings. Also there's a macro + <code>EXTRA_LIBS</code> to add linkage flags (an example for a Solaris + build is included). <p> <li> Now crossroads is ready for compilation. Do a <code>make - local</code> followed by <code>make install</code>. The latter step may have - to be done by the user <code>root</code> if the <code>BINDIR</code> setting of - <code>etc/Makefile.def</code> points to a root-owned directory. + local</code> followed by <code>make install</code>. The latter step may have + to be done by the user <code>root</code> if the <code>BINDIR</code> setting of + <code>etc/Makefile.def</code> points to a root-owned directory. <p> <li> The documentation doesn't install in this process. If you - want to install the documentation, then proceed as follows: + want to install the documentation, then proceed as follows: <p> <ul> - <li> Optionally, <code>cp doc/crossroads.html</code> - <em>htmldirectory/</em>; where <em>htmldirectory</em> is the destination - directory for your HTML manuals; + <li> Optionally, <code>cp doc/crossroads.html</code> + <em>htmldirectory/</em>; where <em>htmldirectory</em> is the destination + directory for your HTML manuals; <p> <li> Optionally, <code>cp doc/crossroads.pdf</code> - <em>pdfdirectory/</em>; where <em>pdfdirectory</em> is the - destination directory for your PDF manuals; + <em>pdfdirectory/</em>; where <em>pdfdirectory</em> is the + destination directory for your PDF manuals; <p> <li> Optionally, <code>cp doc/crossroads.man</code> - <em>manualdirectory</em><code>/man1/crossroads.1</code>, where - <em>manualdirectory</em> is e.g. <code>/usr/man</code>, - <code>/usr/share</code>, <code>/usr/local/man</code>, - <code>/usr/local/share</code>. Any possibility is valid, as - long as <em>manualdirectory</em> has a subdirectory - <code>man1/</code>; + <em>manualdirectory</em><code>/man1/crossroads.1</code>, where + <em>manualdirectory</em> is e.g. <code>/usr/man</code>, + <code>/usr/share</code>, <code>/usr/local/man</code>, + <code>/usr/local/share</code>. Any possibility is valid, as + long as <em>manualdirectory</em> has a subdirectory + <code>man1/</code>; <p> <li> If your manual page system supports compressed - manual pages, then you can save some space with - <code>gzip</code> <em>manualdirectory</em><code>/man1/crossroads.1</code>.</ul> + manual pages, then you can save some space with + <code>gzip</code> <em>manualdirectory</em><code>/man1/crossroads.1</code>.</ul> <p> </ul> <p> -<a name="l39"></a> +<a name="l42"></a> <h3>7.3: Configuring crossroads</h3> <p> Now that the binary is available on your system, you need to create a @@ -1895,8 +1999,8 @@ Once you have the configuration ready, start crossroads with ends. Monitor how crossroads is doing with: <p> <ul> - <li> In one terminal, run the script: - <pre> + <li> In one terminal, run the script: + <pre> while [ 1 ] ; do tput clear crossroads status @@ -1906,10 +2010,10 @@ done <p> <strong>Note</strong> that depending on your system you might need - <code>sleep 3s</code>, i.e., with an <code>s</code> appended. + <code>sleep 3s</code>, i.e., with an <code>s</code> appended. <p> <li> In another terminal, run: - <pre> + <pre> while [ 1 ] ; do tput clear ps ax | grep crossroads | grep -v grep @@ -1919,11 +2023,11 @@ done <p> <strong>Note</strong> that depending on your system you might need - <code>ps -ef</code> instead of <code>ps ax</code>. + <code>ps -ef</code> instead of <code>ps ax</code>. <p> <li> In yet another terminal, run <code>tail -f - /var/log/messages</code> (supply the appropriate system log file if - <code>/var/log/messages</code> doesn't work for you).</ul> + /var/log/messages</code> (supply the appropriate system log file if + <code>/var/log/messages</code> doesn't work for you).</ul> <p> Now thoroughly test the availability of your back ends through crossroads. The status display will show an updated view of which back @@ -1932,13 +2036,13 @@ which crossroads daemons are running. Finally, the tailing of <code>/var/log/messages</code> shows what's going on -- especially if you have <code>verbosity true</code> statements in the configuration. <p> -<a name="l40"></a> +<a name="l43"></a> <h3>7.4: A boot script</h3> <p> Finally, you may want to create a boot-time startup script. The exact procedure depends on the used Unix flavor. <p> -<a name="l41"></a> +<a name="l44"></a> <strong>7.4.1: SysV Style Startup</strong> <p> On SysV style systems, there's a startup script directory @@ -1948,8 +2052,8 @@ inserting scripts into the boot sequence, but otherwise the steps will resemble the following. <p> <ul> - <li> Create a script <code>crossroads</code> in <code>/etc/init.d</code> similar to the - following: + <li> Create a script <code>crossroads</code> in <code>/etc/init.d</code> similar to the + following: <p> <pre> #!/bin/sh @@ -1958,13 +2062,13 @@ otherwise the steps will resemble the following. <p> The stated directory <code>/usr/local/bin</code> must correspond with - the installation path. The flag <code>-v</code> causes the startup to - be more 'verbose'. However, once daemonized, the verbosity is - controlled by the appropriate statements in the configuration. + the installation path. The flag <code>-v</code> causes the startup to + be more 'verbose'. However, once daemonized, the verbosity is + controlled by the appropriate statements in the configuration. <p> <li> Determine your 'runlevel': usually 3 when your system is - running in text-mode only, or 5 when you are a graphical - interface. If your runlevel is 3, then: + running in text-mode only, or 5 when you are using a graphical + interface. If your runlevel is 3, then: <p> <pre> root&gt; cd /etc/rc.d/rc3.d @@ -1974,13 +2078,13 @@ root&gt; ln -s /etc/init.d/crossroads K99crossroads <p> This creates startup (<code>S*</code>) and stop (<code>K*</code>) links that - will be run when the system enters or leaves a given runlevel. + will be run when the system enters or leaves a given runlevel. <p> If your runlevel is 5, then the right <code>cd</code> command is to - <code>/etc/rc.d/rc5.d</code>. Alternatively, you can create the - symlinks in both runlevel directories.</ul> + <code>/etc/rc.d/rc5.d</code>. Alternatively, you can create the + symlinks in both runlevel directories.</ul> <p> -<a name="l42"></a> +<a name="l45"></a> <strong>7.4.2: BSD Style Startup</strong> <p> On BSD style systems, daemons are booted directly from <code>/etc/rc</code> and diff --git a/doc/crossroads.man b/doc/crossroads.man @@ -1,6 +1,6 @@ -.TH "Crossroads 1\&.05" "2005, 2006, ff\&." +.TH "Crossroads 1\&.07" "2005, 2006, ff\&." .PP -.SH "Crossroads 1\&.05" +.SH "Crossroads 1\&.07" .SH "Karel Kubat" .SH "e-tunity" .SH "2005, 2006, ff\&." @@ -477,20 +477,20 @@ statements\&. Each statement must end with a semicolon, except for the .IP "The type statement" defines how crossroads handles the stated service\&. There are currently two types: \f(CWany\fP and -\f(CWstickyhttp\fP\&. The type \f(CWany\fP means that crossroads doesn\&'t +\f(CWhttp\fP\&. The type \f(CWany\fP means that crossroads doesn\&'t interpret the contents of a TCP stream, but only distributes streams -over back ends\&. The type \f(CWstickyhttp\fP means that crossroads has to -analyze what\&'s in the messages, does magical HTTP cookie tricks, and -so on -- to ensure that multiple connections are treated as one -session\&. +over back ends\&. The type \f(CWhttp\fP means that crossroads has to +analyze what\&'s in the messages, does magical HTTP header tricks, and +so on -- all to ensure that multiple connections are treated as one +session, or that the back end is notified of the client\&'s IP address\&. .IP -Unless you really need sticky HTTP sessions, use the type \f(CWany\fP (the +Unless you really need such special features, use the type \f(CWany\fP (the default), even for HTTP protocols\&. .IP .IP o Syntax: \f(CWtype\fP \fItypespecifier\fP \f(CW;\fP .IP o -Where \fItypespecifier\fP is \f(CWany\fP or \f(CWstickyhttp\fP +Where \fItypespecifier\fP is \f(CWany\fP or \f(CWhttp\fP .IP o Default: \f(CWany\fP .IP @@ -597,11 +597,11 @@ aren\&'t part of a sticky HTTP session\&. This is the case during: .IP o all client requests of a service type \f(CWany\fP; .IP o -new sessions of a service type \f(CWstickyhttp\fP\&. +new sessions of a service type \f(CWhttp\fP\&. .IP -When \f(CWstickyhttp\fP is in effect and a session is underway, then the +When type \f(CWhttp\fP is in effect and a session is underway, then the previously used back end is always selected -- regardless of -dispatching mode\&. +dispatching mode\&. .IP Your \&'right\&' dispatch mode will depend on the type of service\&. Given the fact that crossroads doesn\&'t know (and doesn\&'t care) how to @@ -831,8 +831,8 @@ data when other back ends are hit Default: 0, meaning that no decay is applied to usage statistics\&. .IP .IP "HTTP cookie handling:" -When the service type is \f(CWstickyhttp\fP, -then backends should define HTTP cookies to identify sessions\&. There +When the service type is \f(CWhttp\fP, +then backends may define HTTP cookies to identify sessions\&. There are two directives to accomplish this: \f(CWstickycookie\fP and \f(CWinsertcookie\fP\&. .IP @@ -863,7 +863,7 @@ example: .nf service www { port 80; - type stickyhttp; + type http; backend one { server 10\&.0\&.0\&.1:80; stickycookie BalancerID=a; @@ -880,7 +880,25 @@ service www { .IP Note also the quoting of the cookiestring in the \f(CWinsertcookie\fP directive\&. Without it, the semicolon in the string would break the -directive\&. +directive\&. +.IP +.IP "Passing of real IP address:" +When the service type is +\f(CWhttp\fP, the directive \f(CWinsertrealip\fP can be used to force +Crossroads to insert the client\&'s real IP address as an HTTP +header\&. The back end may then inspect the header\&. +.IP +.IP o +Syntax: \f(CWinsertrealip\fP \fIsetting\fP \f(CW;\fP +.IP o +where \fIsetting\fP is \f(CWtrue\fP, \f(CWyes\fP or \f(CWon\fP to turn +on this feature; or \f(CWfalse\fP, \f(CWno\fP or \f(CWoff\fP to turn it +off\&. +.IP o +Default: \f(CWoff\fP +.IP +When turned on, the client\&'s IP address is stored in an HTTP +header named \f(CWXR-Real-IP\fP\&. .IP .IP "Event triggers:" As special \&'hooks\&' for actions, two triggers @@ -1319,7 +1337,7 @@ follows: .PP .IP o At the level of a \f(CWservice\fP description, set the type to -\f(CWstickyhttp\fP\&. +\f(CWhttp\fP\&. .IP o At the level of each back end description, configure the \f(CWstickycookie\fP and a \f(CWinsertcookie\fP directives\&. @@ -1351,14 +1369,14 @@ service www { backend one { server 10\&.1\&.1\&.100:80; - stickycookie XRID=100; - insertcookie "XRID=100; Path=/"; + stickycookie XRID=100; + insertcookie "XRID=100; Path=/"; } backend two { - server 10\&.1\&.1\&.101:80; - stickycookie XRID=101; - insertcookie "XRID=101; Path=/"; + server 10\&.1\&.1\&.101:80; + stickycookie XRID=101; + insertcookie "XRID=101; Path=/"; } } .fi @@ -1366,10 +1384,98 @@ service www { .PP Note how the cookie names and values in the directives \f(CWstickycookie\fP and \f(CWinsertcookie\fP match\&. That is obviously a -prerequisite for stickiness\&. +prerequisite for stickiness\&. +.PP + +.SH "5\&.3: Passing the client\&'s IP address" + +.PP +Since Crossroads just shuttles bytes to and fro, meta-information of +network connections is lost\&. As far as the back ends are concerned, +their connections originate at the Crossroads junction\&. +For example, standard Apache access logs will show the IP address of +Crossroads\&. +.PP +In order to compensate for this, Crossroads can insert a special +header in HTTP connections, to inform the back end of the original +client\&'s IP address\&. In order to enable this, the Crossroads +configuration must state the following: +.PP +.IP o +The service type must be \f(CWhttp\fP, and not \f(CWany\fP; +.IP o +In the back end definition, a statement \f(CWinsertrealip on;\fP +must occur\&. +.PP +After this, HTTP traffic that arrives at the back ends has a new +header: \f(CWXR-Real-IP\fP, holding the client\&'s IP address\&. +\fBNote that\fP once the type is set to \f(CWhttp\fP, Crossroads\&' +performance will be hampered -- all passing messages will have to be +unpacked and analyzed\&. +.PP + +.SH "5\&.3\&.1: Sample Crossroads configuration" + +.PP +The below sample configuration shows two HTTP back ends that receive +the client\&'s IP address: +.PP +.nf + +service www { + port 80; + type http; + revivinginterval 5; + dispatchmode roundrobin; + + backend one { + server 10\&.1\&.1\&.100:80; + insertrealip on; + } + + backend two { + server 10\&.1\&.1\&.200:80; + insertrealip on; + } +} +.fi + +.PP + +.SH "5\&.3\&.2: Sample Apache configuration" + +.PP +The method by which each back end analyzes the header \f(CWXR-Real-IP\fP +will obviously be different per server implementations\&. However, a +common method with the Apache webserver is to log the client\&'s IP +address into the access log\&. +.PP +Often this is accomplished using the log format \f(CWcustom\fP, defined as +follows: +.PP +.nf +LogFormat "%h %l %u %t %D \e"%r\e" %>s %b" common +CustomLog logs/access_log common +.fi + +.PP +The first line defines the format \f(CWcommon\fP, with the remote host +specified by \f(CW%h\fP\&. The second line sends access information to a log +file \f(CWlogs/access_log\fP, using the previously defined format +\f(CWcommon\fP\&. +.PP +Furtunately, Apache\&'s \f(CWLogFormat\fP allows one to log contents of +headers\&. By replacing the \f(CW%h\fP with \f(CW%{XR-Real-IP}i\fP, the desired +information is sent to the log\&. Therefore, normally you can simply +redefine the \f(CWcommon\fP format to +.PP +.nf +LogFormat "%{XR-Real-IP}i %l %u %t %D \e"%r\e" %>s %b" common +.fi + .PP -.SH "5\&.3: Configuration examples" +.SH "5\&.4: Configuration examples" .PP As a general hint, use \f(CWcrossroads sampleconf\fP to view the most @@ -1377,7 +1483,7 @@ up-to-date examples of configurations\&. The description below shows a few examples too\&. .PP -.SH "5\&.3\&.1: A load balancer for three webserver back ends" +.SH "5\&.4\&.1: A load balancer for three webserver back ends" .PP The following configuration example binds crossroads to port 80 of the @@ -1507,7 +1613,7 @@ service www { .PP -.SH "5\&.3\&.2: An HTTP forwarder when travelling" +.SH "5\&.4\&.2: An HTTP forwarder when travelling" .PP As another example, here\&'s my \f(CWcrossroads\&.conf\fP that I use on my @@ -1602,7 +1708,7 @@ sshtunnel down\fP will \&'take down\&' the back end \f(CWSshTunnel\fP -- and will automatically cause crossroads to switch to \f(CWLocalSquid\fP\&. .PP -.SH "5\&.3\&.3: SSH login with enforced idle logout" +.SH "5\&.4\&.3: SSH login with enforced idle logout" .PP The following example shows how crossroads \&'throttles\&' SSH @@ -2016,7 +2122,7 @@ controlled by the appropriate statements in the configuration\&. .IP .IP o Determine your \&'runlevel\&': usually 3 when your system is -running in text-mode only, or 5 when you are a graphical +running in text-mode only, or 5 when you are using a graphical interface\&. If your runlevel is 3, then: .IP .nf diff --git a/doc/crossroads.pdf b/doc/crossroads.pdf Binary files differ. diff --git a/doc/crossroads.yo b/doc/crossroads.yo @@ -2,15 +2,15 @@ tocclearpage() titleclearpage() includefile(defs.yo) abstract(Crossroads is a load balance and fail over utility for TCP - based services. It is a daemon program running in user - space, and features extensive configurability, polling of - back ends using 'wakeup calls', detailed status reporting, - 'hooks' for special actions when backend calls fail, and much - more. Crossroads is service-independent: it is usable for - HTTP/HTTPS, SSH, SMTP, DNS, etc. In the case of HTTP - balancing, Crossroads can provide 'session stickiness' for - back-end processes that need sessions, but aren't - session-aware of other back-ends.) + based services. It is a daemon program running in user + space, and features extensive configurability, polling of + back ends using 'wakeup calls', detailed status reporting, + 'hooks' for special actions when backend calls fail, and much + more. Crossroads is service-independent: it is usable for + HTTP/HTTPS, SSH, SMTP, DNS, etc. In the case of HTTP + balancing, Crossroads can provide 'session stickiness' for + back-end processes that need sessions, but aren't + session-aware of other back-ends.) article(Crossroads VER()) (Karel Kubat) (2005, 2006, ff.) diff --git a/doc/defs.yo b/doc/defs.yo @@ -1,7 +1,7 @@ COMMENT( - Local Yodl Macros - ================= + Local Yodl Macros + ================= Here's a sample "local config" file that gets installed into /usr/e/share/yodl. You can use this as an example to cook your @@ -42,9 +42,9 @@ setfigureext(.png) sethtmlfigureext(.png) COMMENT(pngfig (basename-without-extension) (caption) (label) - A-la figure() but includes the true png. We can do that - because we use pdflatex.) - + A-la figure() but includes the true png. We can do that + because we use pdflatex.) + redef(pngfig)(3)(\ whenhtml(figure(ARG1)(ARG2)(ARG3))\ whenlatex(\ @@ -57,14 +57,14 @@ redef(pngfig)(3)(\ latexcommand(\label{)ARG3+latexcommand(})\ latexcommand(\end{figure})) whenman(verb( - ===================================================== - Insert figure ARG1 about here - ARG2 - =====================================================)\ - label(ARG3))) + ===================================================== + Insert figure ARG1 about here + ARG2 + =====================================================)\ + label(ARG3))) COMMENT(listing(text) - A-la verb() but nicely formatted in LaTeX.) + A-la verb() but nicely formatted in LaTeX.) redef(listing)(1)(\ whenhtml(verb(ARG1))\ diff --git a/doc/impatient.yo b/doc/impatient.yo @@ -4,42 +4,42 @@ for getting crossroads up and running: itemization( - it() If you don't have SVN or don't want to use it: + it() If you don't have SVN or don't want to use it: - itemization( - it() Obtain the crossroads source archive at - lurl(http://crossroads.e-tunity.com). - - it() Change-dir to a 'sources' directory on your system and - unpack the archive. + itemization( + it() Obtain the crossroads source archive at + lurl(http://crossroads.e-tunity.com). + + it() Change-dir to a 'sources' directory on your system and + unpack the archive. - it() Change-dir into the create directory tt(crossroads/).) + it() Change-dir into the create directory tt(crossroads/).) - it() If you have SVN and want to go for the newest snapshot: + it() If you have SVN and want to go for the newest snapshot: - itemization( - it() Get the latest sources and snapshots using SVN from nl() + itemization( + it() Get the latest sources and snapshots using SVN from nl() tt(svn://svn.e-tunity.com/crossroads). - it() You'll find the newest alpha version under - tt(crossroads/trunk) and the stable versions under - tt(crossroads/tags), - e.g. tt(crossroads/tags/release-1.00). + it() You'll find the newest alpha version under + tt(crossroads/trunk) and the stable versions under + tt(crossroads/tags), + e.g. tt(crossroads/tags/release-1.00). - it() Choose which you want to use: the latest stable - release, or the bleeding edge alpha? In the former case, - change-dir to tt(crossroads/tags/release-)em(X.YY), where - em(X.YY) is a release ID. In the latter case, change-dir to - tt(crossroads/trunk).) + it() Choose which you want to use: the latest stable + release, or the bleeding edge alpha? In the former case, + change-dir to tt(crossroads/tags/release-)em(X.YY), where + em(X.YY) is a release ID. In the latter case, change-dir to + tt(crossroads/trunk).) - it() Type tt(make install). This installs the crossroads - binary into tt(/usr/local/bin/). If the compilation doesn't - work on your system, check tt(etc/Makefile.def) for hints. + it() Type tt(make install). This installs the crossroads + binary into tt(/usr/local/bin/). If the compilation doesn't + work on your system, check tt(etc/Makefile.def) for hints. - it() Create a file tt(/etc/crossroads.conf). In it state - something like: + it() Create a file tt(/etc/crossroads.conf). In it state + something like: - verb(\ + verb(\ service www { port 80; revivinginterval 15; @@ -51,15 +51,15 @@ service www { } }) - That's off course assuming that you want to balance HTTP on - port 80 to two back ends at 10.1.1.100 and 10.1.1.101. + That's off course assuming that you want to balance HTTP on + port 80 to two back ends at 10.1.1.100 and 10.1.1.101. - it() Type tt(crossroads start). + it() Type tt(crossroads start). - it() Surf to the machine where crossroads is running. You will - see the pages served by the back ends 10.1.1.100 or - 10.1.1.101. + it() Surf to the machine where crossroads is running. You will + see the pages served by the back ends 10.1.1.100 or + 10.1.1.101. - it() To monitor the status of crossroads, type tt(crossroads - status). + it() To monitor the status of crossroads, type tt(crossroads + status). ) diff --git a/doc/intro.yo b/doc/intro.yo @@ -30,17 +30,17 @@ subsect(Obtaining Crossroads) As quick reference, here are some important URL's for Crossroads: itemization( - it() lurl(http:/crossroads.e-tunity.com) is the site that serves - Crossroads. You can browse this at leisure - for documentation, sources, and so on. + it() lurl(http:/crossroads.e-tunity.com) is the site that serves + Crossroads. You can browse this at leisure + for documentation, sources, and so on. - it() lurl(http://freshmeat.net/projects/crossr) is the - Freshmeat announcement page. + it() lurl(http://freshmeat.net/projects/crossr) is the + Freshmeat announcement page. - it() lurl(svn://svn.e-tunity.com/crossroads) is the SVN - repository; anonymous reading (fetching) is allowed. In order - to commit changes, url(mail me)(mailto:karel@e-tunity.com) for - credentials.) + it() lurl(svn://svn.e-tunity.com/crossroads) is the SVN + repository; anonymous reading (fetching) is allowed. In order + to commit changes, url(mail me)(mailto:karel@e-tunity.com) for + credentials.) subsect(Copyright and Disclaimer) @@ -70,84 +70,84 @@ more meanings of the terms will exist -- yes, I am aware of that. I'm using the terms here in a very strict sense.) description( - dit(A client) is a process that initiates a network connection - to get contact with some service. - dit(A service) or bf(server process) or bf(listener) - is a central application - that accepts network connections from clients and sevices - them. - dit(Back ends) are locations where crossroads looks in - order to service its clients. Crossroads sits 'in between' - and does its tricks. Therefore, as far as the back ends - are concerned, crossroads behaves like a client. As far as - the true client is concerned, crossroads behaves like the - service. The communication is however transparent: neither - client nor back end are aware of the middle position of - crossroads. - dit(A connection) is a network conversation between client and service, - where data are transferred to and fro. As - far as crossroads is concerned, success means that a - connection can be established without errors on - the network level. Crossroads isn't aware of service - pecularities. E.g., when a webserver answers tt(HTTP/1.0 - 500 Server Error) then crossroads will see this as a - succesful connection, though the user behind a browser may - think otherwise. - dit(Back end selection algorithms) are methods by which - crossroads determines which back end it will talk to - next. Crossroads has a number of built-in algorithms, - which may be configured per service. + dit(A client) is a process that initiates a network connection + to get contact with some service. + dit(A service) or bf(server process) or bf(listener) + is a central application + that accepts network connections from clients and sevices + them. + dit(Back ends) are locations where crossroads looks in + order to service its clients. Crossroads sits 'in between' + and does its tricks. Therefore, as far as the back ends + are concerned, crossroads behaves like a client. As far as + the true client is concerned, crossroads behaves like the + service. The communication is however transparent: neither + client nor back end are aware of the middle position of + crossroads. + dit(A connection) is a network conversation between client and service, + where data are transferred to and fro. As + far as crossroads is concerned, success means that a + connection can be established without errors on + the network level. Crossroads isn't aware of service + pecularities. E.g., when a webserver answers tt(HTTP/1.0 + 500 Server Error) then crossroads will see this as a + succesful connection, though the user behind a browser may + think otherwise. + dit(Back end selection algorithms) are methods by which + crossroads determines which back end it will talk to + next. Crossroads has a number of built-in algorithms, + which may be configured per service. dit(Back end states) are the statusses of each back end that - is known to crossroads. A back end may be available, - (temporarily) unavailble or truly down. When a back end is - temporarily unavailable, then crossroads will periodically - check whether the back end has come to life yet (that is, - if configured so). - dit(A spike) is a sudden increase in activity, leading to - extra load on a given service. When crossroads is in - effect and when the spike occurs in one connection, - then obviously the spike will also appear at one - of the back ends. However, crossroads will see the spike - and will make sure that a subsequent request goes to an - other back end. In contrast, when several connections - arrive simultaneously and cause a spike, then crossroads - will be able to distribute the connections over several - back ends, thereby 'flattening out' the increase. - dit(Load balancing) means that incoming client requests are - distributed over more than just one back end (which wouldn't be the - case if you wouldn't be running crossroads). Enabling load - balancing is nothing more than duplicating services over - more than one back end, and having something (in this - case: crossroads) distribute the requests, so that per - back end the load doesn't get too high. - dit(An HTTP session) is a series of separate network connections - that originate from one browser. E.g., to fill the display - with text and images, the browser hits a website several times. - An HTTP session may even span several - screens. E.g., a website registration dialog may involve 3 - screens that when called from the same browser, - form a logical group of some sort. - dit(Session stickiness) means that when a browser starts an - HTTP dialog, the balancer makes sure that it 'sticks' to - the same back end (i.e., subsequent requests from the - browser are forced to go to the same back end, instead of - being balanced to other ones). - dit(Back end usage) is measured by crossroads in order to be - able to determine back end selection. Crossroads stores - information about the number of active connections, the - transferred bytes and - about the connection duration. These numbers can be used to - estimate which back end is the least used -- and - therefore, presumably, the best candidate for a new - request. - dit(Fail over) is almost always used when load balancing is in - effect. The distributor of client requests (crossroads of - course) can also monitor back ends, so that incase a back - end is 'down', it is no longer accessed. - dit(Service downtime) normally occurs when a service is - switched off. Downtime is obviously avoided when fail over - is in effect: a back end can be taken out of service in a - controlled manner, without any client noticing it. + is known to crossroads. A back end may be available, + (temporarily) unavailble or truly down. When a back end is + temporarily unavailable, then crossroads will periodically + check whether the back end has come to life yet (that is, + if configured so). + dit(A spike) is a sudden increase in activity, leading to + extra load on a given service. When crossroads is in + effect and when the spike occurs in one connection, + then obviously the spike will also appear at one + of the back ends. However, crossroads will see the spike + and will make sure that a subsequent request goes to an + other back end. In contrast, when several connections + arrive simultaneously and cause a spike, then crossroads + will be able to distribute the connections over several + back ends, thereby 'flattening out' the increase. + dit(Load balancing) means that incoming client requests are + distributed over more than just one back end (which wouldn't be the + case if you wouldn't be running crossroads). Enabling load + balancing is nothing more than duplicating services over + more than one back end, and having something (in this + case: crossroads) distribute the requests, so that per + back end the load doesn't get too high. + dit(An HTTP session) is a series of separate network connections + that originate from one browser. E.g., to fill the display + with text and images, the browser hits a website several times. + An HTTP session may even span several + screens. E.g., a website registration dialog may involve 3 + screens that when called from the same browser, + form a logical group of some sort. + dit(Session stickiness) means that when a browser starts an + HTTP dialog, the balancer makes sure that it 'sticks' to + the same back end (i.e., subsequent requests from the + browser are forced to go to the same back end, instead of + being balanced to other ones). + dit(Back end usage) is measured by crossroads in order to be + able to determine back end selection. Crossroads stores + information about the number of active connections, the + transferred bytes and + about the connection duration. These numbers can be used to + estimate which back end is the least used -- and + therefore, presumably, the best candidate for a new + request. + dit(Fail over) is almost always used when load balancing is in + effect. The distributor of client requests (crossroads of + course) can also monitor back ends, so that incase a back + end is 'down', it is no longer accessed. + dit(Service downtime) normally occurs when a service is + switched off. Downtime is obviously avoided when fail over + is in effect: a back end can be taken out of service in a + controlled manner, without any client noticing it. ) subsect(Porting issues for pre-0.26 installations) @@ -156,10 +156,10 @@ subsect(Porting issues for pre-0.26 installations) changed. In particular: itemization( - it() The keyword tt(maxconnections) is now used instead of - tt(maxclients); - it() The keyword tt(connectiontimeout) is now used instead of - tt(sessiontimeout).) + it() The keyword tt(maxconnections) is now used instead of + tt(maxclients); + it() The keyword tt(connectiontimeout) is now used instead of + tt(sessiontimeout).) Therefore when converting configuration files to the new syntax, the above keywords must be changed. (The reason for these changes diff --git a/doc/tips.yo b/doc/tips.yo @@ -39,26 +39,26 @@ itemization( processes that don't wait for user interaction (e.g., SOAP calls and other HTTP requests). - it() As a last remark, the dispatching mode tt(byconnections) can - be used if you don't have other clues for load - estimations. - - E.g., consider a database connection. What's - heavier on the back end, time-consuming connections, or connections - where loads of bytes are transferred? Well, that depends. A - tough tt(select) query that joins multiple tables can be very - heavy on the back end, though the response set can be quite - small - and hence the number of - transferred bytes. That would suggest - dispatching by duration. However, tt(byduration) - balancing doesn't respresent the true world, when interactive - connections can occur where users have an idle TCP connection to - the database: - this consumes time, but no bytes (see the SSH login example - above). In this case, the dispatch mode tt(byconnections) may be - your best bet. - - ) + it() As a last remark, the dispatching mode tt(byconnections) can + be used if you don't have other clues for load + estimations. + + E.g., consider a database connection. What's + heavier on the back end, time-consuming connections, or connections + where loads of bytes are transferred? Well, that depends. A + tough tt(select) query that joins multiple tables can be very + heavy on the back end, though the response set can be quite + small - and hence the number of + transferred bytes. That would suggest + dispatching by duration. However, tt(byduration) + balancing doesn't respresent the true world, when interactive + connections can occur where users have an idle TCP connection to + the database: + this consumes time, but no bytes (see the SSH login example + above). In this case, the dispatch mode tt(byconnections) may be + your best bet. + + ) subsubsect(Averaging size and duration) @@ -193,18 +193,18 @@ good idea to protect your service by specifying a maximum number of concurrent connections. This protection can be specified on two levels: description( - dit(On the service level) a statement like tt(maxconnections - 100;) states that the service as a whole will never - service more than 100 concurrent connections. This means that - all your back ends and the crossroads balancer itself - will be protected from being overloaded. - dit(On the back end level) a statement like tt(maxconnections 10;) - states that this particular back end will never have more - than 10 concurrent connections; regardless of the overall - setting on the service level. This means that this - particular back end will be protected from being - overloaded (regardless of what other back ends may - experience).) + dit(On the service level) a statement like tt(maxconnections + 100;) states that the service as a whole will never + service more than 100 concurrent connections. This means that + all your back ends and the crossroads balancer itself + will be protected from being overloaded. + dit(On the back end level) a statement like tt(maxconnections 10;) + states that this particular back end will never have more + than 10 concurrent connections; regardless of the overall + setting on the service level. This means that this + particular back end will be protected from being + overloaded (regardless of what other back ends may + experience).) The tt(maxconnections) statement, combined with a back end selection algorithm, allows very fine granularity. The tt(maxconnections) statement @@ -232,16 +232,16 @@ session stickiness hampers failover, balancing and performance: itemization( it() Failover is hampered because during the session, the balancer has to assign new connections to the same back - end that was selected at the start of a session. If the back - end suddenly goes 'down', then the session will crash. + end that was selected at the start of a session. If the back + end suddenly goes 'down', then the session will crash. it() Balancing is hampered because at the start of the session, the balancer has selected the next-best back end. But during - the session, that back end may well become overloaded. The - balancer however must continue to send the requests there. + the session, that back end may well become overloaded. The + balancer however must continue to send the requests there. it() Performance is hampered because crossroads needs to 'unpack' messages as they are passed to and fro. That's because - crossroads needs to check the HTTP headers in the messages - for persistence cookies.) + crossroads needs to check the HTTP headers in the messages + for persistence cookies.) There is a number of measures that you can take to avoid using session stickiness. E.g., session data can be 'shared' between web back @@ -257,7 +257,7 @@ follows: itemization( it() At the level of a tt(service) description, set the type to - tt(stickyhttp). + tt(http). it() At the level of each back end description, configure the tt(stickycookie) and a tt(insertcookie) directives.) @@ -267,14 +267,14 @@ shuttles between client and back end: itemization( it() If there is no persistence cookie in the HTTP headers of a client's request, then the message must be the first one and - a new session should be established. - Crossroads selects an appropriate back - end, sends the message to that back end, catches the reply, - and inserts a tt(Set-Cookie) directive. + a new session should be established. + Crossroads selects an appropriate back + end, sends the message to that back end, catches the reply, + and inserts a tt(Set-Cookie) directive. it() If there is a persistence cookie in the HTTP headers of a client's request, then the request is part of an already - established session. Crossroads analyzes the cookie and - forwards the request to the appropriate back end.) + established session. Crossroads analyzes the cookie and + forwards the request to the appropriate back end.) Below is a short example of a configuration. @@ -287,21 +287,97 @@ service www { backend one { server 10.1.1.100:80; - stickycookie XRID=100; - insertcookie "XRID=100; Path=/"; + stickycookie XRID=100; + insertcookie "XRID=100; Path=/"; } backend two { - server 10.1.1.101:80; - stickycookie XRID=101; - insertcookie "XRID=101; Path=/"; + server 10.1.1.101:80; + stickycookie XRID=101; + insertcookie "XRID=101; Path=/"; } }) Note how the cookie names and values in the directives tt(stickycookie) and tt(insertcookie) match. That is obviously a -prerequisite for stickiness. - +prerequisite for stickiness. + + +subsect(Passing the client's IP address) + +Since Crossroads just shuttles bytes to and fro, meta-information of +network connections is lost. As far as the back ends are concerned, +their connections originate at the Crossroads junction. +For example, standard Apache access logs will show the IP address of +Crossroads. + +In order to compensate for this, Crossroads can insert a special +header in HTTP connections, to inform the back end of the original +client's IP address. In order to enable this, the Crossroads +configuration must state the following: + +itemization( + it() The service type must be tt(http), and not tt(any); + it() In the back end definition, a statement tt(insertrealip on;) + must occur.) + +After this, HTTP traffic that arrives at the back ends has a new +header: tt(XR-Real-IP), holding the client's IP address. +bf(Note that) once the type is set to tt(http), Crossroads' +performance will be hampered -- all passing messages will have to be +unpacked and analyzed. + +subsubsect(Sample Crossroads configuration) + +The below sample configuration shows two HTTP back ends that receive +the client's IP address: + +verb( +service www { + port 80; + type http; + revivinginterval 5; + dispatchmode roundrobin; + + backend one { + server 10.1.1.100:80; + insertrealip on; + } + + backend two { + server 10.1.1.200:80; + insertrealip on; + } +}) + + +subsubsect(Sample Apache configuration) + +The method by which each back end analyzes the header tt(XR-Real-IP) +will obviously be different per server implementations. However, a +common method with the Apache webserver is to log the client's IP +address into the access log. + +Often this is accomplished using the log format tt(custom), defined as +follows: + +verb(\ +LogFormat "%h %l %u %t %D \"%r\" %>s %b" common +CustomLog logs/access_log common) + +The first line defines the format tt(common), with the remote host +specified by tt(%h). The second line sends access information to a log +file tt(logs/access_log), using the previously defined format +tt(common). + +Furtunately, Apache's tt(LogFormat) allows one to log contents of +headers. By replacing the tt(%h) with tt(%{XR-Real-IP}i), the desired +information is sent to the log. Therefore, normally you can simply +redefine the tt(common) format to + +verb(\ +LogFormat "%{XR-Real-IP}i %l %u %t %D \"%r\" %>s %b" common) + subsect(Configuration examples) @@ -461,7 +537,7 @@ itemization( it() And in yet other instances, I use a HTTP diagnostic tool url(Charles)(http://www.xk72.com/charles) - that sits between browser and website and shows me + that sits between browser and website and shows me what's happening. I run charles on my own machine and it listens to port 8888, behaving like a proxy. The browser configuration for the proxy is then diff --git a/doc/using.yo b/doc/using.yo @@ -9,33 +9,33 @@ This section shows the most basic usage. As said above, start tt(crossroads) without arguments to view the full listing of options. itemization( - it() tt(crossroads start) and tt(crossroads stop) are typical - actions that are run from system startup scripts. The - meaning is self-explanatory. - it() tt(crossroads restart) is a combination of the former - two. Beware that a restart may cause discontinuity in - service; it is just a shorthand for typing the 'stop' and - 'start' actions after one another. - it() tt(crossroad status) reports on each running - service. Per service, the state of each back end is - reported. - it() tt(crossroads tell) em(service backend state) is a - command line way of telling crossroads that a given back - end, of a given service, is in a given state. Normally - crossroads maintains state information itself, but by - using tt(crossroads tell), a back end can be e.g. taken - 'off line' for servicing. - it() tt(crossroads configtest) tells you whether the - configuration is syntactially correct. - it() tt(crossroads services) reports on the configured - services. In contrast to tt(crossroads status), this - option only shows what's configured -- not what's up and - running. Therefore, tt(crossroads services) doesn't - report on back end states. - it() tt(crossroads sampleconf) shows a sample configuration on - screen. A good way of quicky viewing the configuration - file syntax, or of getting a start for your own - configuration tt(/etc/crossroads.conf). + it() tt(crossroads start) and tt(crossroads stop) are typical + actions that are run from system startup scripts. The + meaning is self-explanatory. + it() tt(crossroads restart) is a combination of the former + two. Beware that a restart may cause discontinuity in + service; it is just a shorthand for typing the 'stop' and + 'start' actions after one another. + it() tt(crossroad status) reports on each running + service. Per service, the state of each back end is + reported. + it() tt(crossroads tell) em(service backend state) is a + command line way of telling crossroads that a given back + end, of a given service, is in a given state. Normally + crossroads maintains state information itself, but by + using tt(crossroads tell), a back end can be e.g. taken + 'off line' for servicing. + it() tt(crossroads configtest) tells you whether the + configuration is syntactially correct. + it() tt(crossroads services) reports on the configured + services. In contrast to tt(crossroads status), this + option only shows what's configured -- not what's up and + running. Therefore, tt(crossroads services) doesn't + report on back end states. + it() tt(crossroads sampleconf) shows a sample configuration on + screen. A good way of quicky viewing the configuration + file syntax, or of getting a start for your own + configuration tt(/etc/crossroads.conf). ) subsect(Logging-related options) diff --git a/etc/Makefile.def b/etc/Makefile.def @@ -3,7 +3,7 @@ # Versioning. This defines the overall version ID and must match the topmost # entry in the ChangeLog. -VER = 1.06 +VER = 1.07 # Default config DEFAULT_CONF = /etc/crossroads.conf diff --git a/src/crossroads.h b/src/crossroads.h @@ -73,6 +73,7 @@ typedef enum { /* Config parsing related */ cf_bindspec, cf_stickycookiespec, cf_insertcookiespec, + cf_insertrealipspec, } Conftype; typedef union { /* Integer of string value */ @@ -118,6 +119,7 @@ typedef struct { /* Backend description */ int maxconnections; /* .. max # of allowed connections */ char *stickycookie; /* .. cookie selector */ char *insertcookie; /* .. cookie ID to insert */ + int insertrealip; /* .. insert real IP (yes/no) */ } Backend; typedef struct { /* Service description */ @@ -217,6 +219,7 @@ extern void configtest (int ac, char **av); extern void dealloc_reporter (Service *s); extern void error (char const *fmt, ...); extern int fork_tcp_servicer (int to_backend); +extern int http_09_received (unsigned char const *buf); extern unsigned char const *http_beyondheaders (unsigned char const *buf, int buflen); extern int http_complete (unsigned char const *buf, int buflen); @@ -224,12 +227,15 @@ extern void http_error (int clientsock); extern int http_findcookie (unsigned char const *buf, char const *cookie); extern char *http_headerval (unsigned char const *buf, char const *label); extern int http_headers_done (unsigned char const *buf); -extern int http_09_received (unsigned char const *buf); +extern void http_insert_header (unsigned char **bufp, int *buflenp, + char const *header); extern void http_read (int sock, int isclient, unsigned char **bufp, int *buflen); extern void http_serve (int clientsock, struct sockaddr_in *client); extern int http_serversocket (unsigned char const *buf, int *is_continuation); +extern void http_set_realip_header (unsigned char **buf, int *buflen, + struct sockaddr_in *client); extern void http_set_sticky_cookie (unsigned char **buf, int *buflen); extern int http_write (int sock, unsigned char const *buf, int buflen); extern int init_sockaddr (struct sockaddr_in *name, diff --git a/src/httpinsertheader.c b/src/httpinsertheader.c @@ -0,0 +1,50 @@ +#include "crossroads.h" + +void http_insert_header (unsigned char **bufp, int *buflen, + char const *header) { + unsigned char const *buf, *cp; + unsigned char *newbuf; + char *instruction; + int crseen = 0, atend = 0; + + cp = buf = *bufp; + msg ("Pasting header '%s' in '%s'", header, buf); + while (1) { + if (*cp == '\n') + cp++; + if (! (cp = (unsigned char const *) + strchr ( (char const *) cp, '\n')) ) { + msg ("Failed to find end of headers while inserting header"); + return; + } + cp++; + + if (*cp == '\r') { + crseen++; + if (*(cp + 1) == '\n') + atend++; + } else if (*cp == '\n') + atend++; + + if (atend) { + instruction = str_printf ("%s%s", + header, + crseen ? "\r\n" : "\n"); + + newbuf = xmalloc (*buflen + strlen(instruction) + 1); + + memcpy (newbuf, buf, cp - buf); + memcpy (newbuf + (cp - buf), instruction, strlen(instruction)); + memcpy (newbuf + (cp - buf) + strlen(instruction), + cp, *buflen - (cp - buf)); + + msg ("Modified buffer: '%s'", newbuf); + + free (*bufp); + *bufp = newbuf; + *buflen += strlen(instruction); + + return; + } + } +} diff --git a/src/httpserve.c b/src/httpserve.c @@ -2,7 +2,7 @@ void http_serve (int clientsock, struct sockaddr_in *client) { unsigned char *firstbuf, *secondbuf; - int serversock, buflen, is_continuation; + int serversock, buflen, is_continuation, is_ready; /* Fork off a servicer if we're in daemon mode. */ if (fork_tcp_servicer(-1)) @@ -11,13 +11,19 @@ void http_serve (int clientsock, struct sockaddr_in *client) { /* Get the initial client request, update logs. */ http_read (clientsock, 1, &firstbuf, &buflen); - /* Determine the back end and send the initial client request. */ + /* Determine the back, maybe a fresh one, or a session continuation. */ if ( (serversock = http_serversocket (firstbuf, &is_continuation)) < 1 ) http_error (clientsock); + /* Stick in a real-IP header if required. */ + msg ("pasting - current_backend: %d, insertrealip: %d", + current_backend, + activeservice->backend[current_backend].insertrealip); + if (activeservice->backend[current_backend].insertrealip) + http_set_realip_header (&firstbuf, &buflen, client); + /* Log what's going on. We only log the continuation here, - * because copysockets below will handle the start/end. - */ + * because copysockets below will handle the start/end. */ if (is_continuation) log_activity_continuation (client); @@ -29,23 +35,26 @@ void http_serve (int clientsock, struct sockaddr_in *client) { free (firstbuf); /* If we have a sticky cookie to insert: - * Get the server response, stick in the cookie, send it to the + * Get the server response, stick in a session cookie, send it to the * client. */ + is_ready = 0; if (activeservice->backend[current_backend].insertcookie) { msg ("Getting server response"); http_read (serversock, 0, &secondbuf, &buflen); http_set_sticky_cookie (&secondbuf, &buflen); if (http_write (clientsock, secondbuf, buflen)) error ("Failed to send data to client"); + if (http_complete (secondbuf, buflen)) { + mark_activity (0, 0, st_available); + is_ready = 1; + } + free (secondbuf); } /* Now we might have to piggyback to and fro -- tho only if the * HTTP chat wasn't done yet. */ - if (http_complete (secondbuf, buflen)) - mark_activity (0, 0, st_available); - else + if (!is_ready) copysockets (clientsock, serversock, client); - free (secondbuf); /* Normal shutdown: TCP traffic done. */ msg ("Normal HTTP stop."); diff --git a/src/httpsetrealipheader.c b/src/httpsetrealipheader.c @@ -0,0 +1,10 @@ +#include "crossroads.h" + +void http_set_realip_header (unsigned char **bufp, int *buflenp, + struct sockaddr_in *client) { + char *header; + + header = str_printf ("XR-Real-IP: %s", inet_ntoa (client->sin_addr)); + http_insert_header (bufp, buflenp, header); + free (header); +} diff --git a/src/httpsetstickycookie.c b/src/httpsetstickycookie.c @@ -1,51 +1,10 @@ #include "crossroads.h" -void http_set_sticky_cookie (unsigned char **bufp, int *buflen) { - char - *cookieval = activeservice->backend[current_backend].insertcookie, - *instruction; - unsigned char const *buf, *cp; - unsigned char *newbuf; - int crseen = 0, atend = 0; +void http_set_sticky_cookie (unsigned char **bufp, int *buflenp) { + char *header; - cp = buf = *bufp; - msg ("Pasting cookie '%s' in '%s'", cookieval, buf); - while (1) { - if (*cp == '\n') - cp++; - if (! (cp = (unsigned char const *) - strchr ( (char const *) cp, '\n')) ) { - msg ("Failed to find end of headers while inserting cookie"); - return; - } - cp++; - - if (*cp == '\r') { - crseen++; - if (*(cp + 1) == '\n') - atend++; - } else if (*cp == '\n') - atend++; - - if (atend) { - instruction = str_printf ("Set-Cookie: %s%s", - cookieval, - crseen ? "\r\n" : "\n"); - - newbuf = xmalloc (*buflen + strlen(instruction) + 1); - - memcpy (newbuf, buf, cp - buf); - memcpy (newbuf + (cp - buf), instruction, strlen(instruction)); - memcpy (newbuf + (cp - buf) + strlen(instruction), - cp, *buflen - (cp - buf)); - - msg ("Modified buffer: '%s'", newbuf); - - free (*bufp); - *bufp = newbuf; - *buflen += strlen(instruction); - - return; - } - } + header = str_printf ("Set-Cookie: %s", + activeservice->backend[current_backend].insertcookie); + http_insert_header (bufp, buflenp, header); + free (header); } diff --git a/src/lexer.l b/src/lexer.l @@ -133,8 +133,15 @@ any { return (ANY); } stickyhttp { - lmsg ("sticky http"); - return (STICKYHTTP); + lmsg ("stickyhttp"); + warning ("The 'stickyhttp protocol " + "is obsolte.\n" + "You should change to 'http'."); + return (HTTP); + } +http { + lmsg ("http"); + return (HTTP); } throughputlog { lmsg ("throughputlog"); @@ -155,7 +162,7 @@ dumptraffic { warning ("The 'dumptraffic' statement " "is obsolete.\n" "You should change to " - "'trafficlog'.\n"); + "'trafficlog'."); BEGIN (stringstate); free (laststring); laststring = 0; @@ -174,20 +181,24 @@ onfailure { free (laststring); laststring = 0; return (ONFAILURE); - } + } stickycookie { lmsg ("stickycookie"); BEGIN (stringstate); free (laststring); laststring = 0; return (STICKYCOOKIE); - } + } insertcookie { lmsg ("insertcookie"); BEGIN (stringstate); free (laststring); laststring = 0; return (INSERTCOOKIE); + } +insertrealip { + lmsg ("insertrealip"); + return (INSERTREALIP); } on|true|yes { lmsg ("on"); @@ -227,7 +238,7 @@ off|false|no { <commentstate>\n { yylineno++; } -<commentstate>. ; +<commentstate>. ; <stringstate>\"[^\"]*\" { llmsg ("string part", yytext); diff --git a/src/main.c b/src/main.c @@ -44,7 +44,6 @@ int main (int argc, char **argv) { { "tell", 3, 1, tell_service }, { "configtest", 0, 1, configtest }, }; - struct passwd *passwd; /* Remember original ac/av/ep */ org_argc = argc; diff --git a/src/parser.y b/src/parser.y @@ -79,14 +79,14 @@ static Service cur_service; /* Storage for a handled service */ ON OFF DISPATCHMODE ROUNDROBIN REVIVINGINTERVAL SHMKEY WEIGHT ONSUCCESS ONFAILURE STRING BACKLOG RANDOM BYDURATION BYSIZE BYCONNECTIONS CONNECTIONTIMEOUT MAXCONNECTIONS BYORDER TRAFFICLOG - OVER DECAY BINDTO THROUGHPUTLOG TYPE ANY STICKYHTTP - STICKYCOOKIE INSERTCOOKIE + OVER DECAY BINDTO THROUGHPUTLOG TYPE ANY HTTP + STICKYCOOKIE INSERTCOOKIE INSERTREALIP %% /* Config file grammar rules */ input: - element + element input | element @@ -98,26 +98,26 @@ element: '{' servicestatements '}' { - /* Verify the service description, supply defaults - * and so on. - */ - if (!cur_service.port) - error ("Service %s lacks a port", - cur_service.name); - if (!cur_service.nbackend) - error ("Service %s lacks back ends", - cur_service.name); + /* Verify the service description, supply defaults + * and so on. + */ + if (!cur_service.port) + error ("Service %s lacks a port", + cur_service.name); + if (!cur_service.nbackend) + error ("Service %s lacks back ends", + cur_service.name); - if (!cur_service.shmkey) - cur_service.shmkey = cur_service.port | SHM_MASK; + if (!cur_service.shmkey) + cur_service.shmkey = cur_service.port | SHM_MASK; - if (!cur_service.bind) - cur_service.bind = "any"; + if (!cur_service.bind) + cur_service.bind = "any"; - /* Add to the list. */ - service = xrealloc (service, ++nservice * sizeof(Service)); - service[nservice - 1] = cur_service; - memset (&cur_service, 0, sizeof(Service)); + /* Add to the list. */ + service = xrealloc (service, ++nservice * sizeof(Service)); + service[nservice - 1] = cur_service; + memset (&cur_service, 0, sizeof(Service)); } ; @@ -129,8 +129,8 @@ service: servicename: servicename_expected IDENTIFIER { - psmsg ("service:", yytext); - cur_service.name = xstrdup(yytext); + psmsg ("service:", yytext); + cur_service.name = xstrdup(yytext); } ; @@ -148,150 +148,155 @@ servicestatement: servicebody: portstatement { - pimsg ("sevice port:", $1.set[0].v.ival); - cur_service.port = $1.set[0].v.ival; - free ($1.set); + pimsg ("sevice port:", $1.set[0].v.ival); + cur_service.port = $1.set[0].v.ival; + free ($1.set); } | bindstatement { - psmsg ("service binding:", $1.set[0].v.sval); - cur_service.bind = $1.set[0].v.sval; - free ($1.set); + psmsg ("service binding:", $1.set[0].v.sval); + cur_service.bind = $1.set[0].v.sval; + free ($1.set); } | verbositystatement { - pimsg ("service verbosity:", $1.set[0].v.ival); - cur_service.verbosity = $1.set[0].v.ival; - free ($1.set); + pimsg ("service verbosity:", $1.set[0].v.ival); + cur_service.verbosity = $1.set[0].v.ival; + free ($1.set); } | dispatchmodestatement { - pimsg ("service dispatch mode:", $1.set[0].v.ival); - pimsg ("service dispatch over:", lastovernr); - cur_service.dispatchtype = $1.set[0].v.ival; - cur_service.dispatchover = lastovernr; - free ($1.set); + pimsg ("service dispatch mode:", $1.set[0].v.ival); + pimsg ("service dispatch over:", lastovernr); + cur_service.dispatchtype = $1.set[0].v.ival; + cur_service.dispatchover = lastovernr; + free ($1.set); } | revivingintervalstatement { - pimsg ("service revival interval:", $1.set[0].v.ival); - cur_service.rev_interval = $1.set[0].v.ival; - free ($1.set); + pimsg ("service revival interval:", $1.set[0].v.ival); + cur_service.rev_interval = $1.set[0].v.ival; + free ($1.set); } | backlogstatement { - pimsg ("service backlog:", $1.set[0].v.ival); - cur_service.backlog = $1.set[0].v.ival; - free ($1.set); + pimsg ("service backlog:", $1.set[0].v.ival); + cur_service.backlog = $1.set[0].v.ival; + free ($1.set); } | shmkeystatement { - pimsg ("service shmkey:", $1.set[0].v.ival); - cur_service.shmkey = $1.set[0].v.ival; - free ($1.set); + pimsg ("service shmkey:", $1.set[0].v.ival); + cur_service.shmkey = $1.set[0].v.ival; + free ($1.set); } | connectiontimeoutstatement { - pimsg ("connection timout:", $1.set[0].v.ival); - cur_service.connectiontimeout = $1.set[0].v.ival; - free ($1.set); + pimsg ("connection timout:", $1.set[0].v.ival); + cur_service.connectiontimeout = $1.set[0].v.ival; + free ($1.set); } | maxconnectionsstatement { - pimsg ("max clients in service:", $1.set[0].v.ival); - cur_service.maxconnections = $1.set[0].v.ival; - free ($1.set); + pimsg ("max clients in service:", $1.set[0].v.ival); + cur_service.maxconnections = $1.set[0].v.ival; + free ($1.set); } | typestatement { - pimsg ("service type: ", $1.set[0].v.ival); - cur_service.type = $1.set[0].v.ival; - free ($1.set); + pimsg ("service type: ", $1.set[0].v.ival); + cur_service.type = $1.set[0].v.ival; + free ($1.set); } | backendblock { - pimsg ("converting backend statements, count is", $1.n); - for (i = 0; i < $1.n; i++) - switch ($1.set[i].cf) { - case cf_portspec: - pimsg ("backend block port:", $1.set[i].v.ival); - cur_backend.port = $1.set[i].v.ival; - break; - case cf_serverspec: - psmsg ("backend block server:", $1.set[i].v.sval); - cur_backend.server = serverpart ($1.set[i].v.sval); - cur_backend.port = portpart ($1.set[i].v.sval); - free ($1.set[i].v.sval); - break; - case cf_verbosityspec: - pimsg ("backend block verbosity:", $1.set[i].v.ival); - cur_backend.verbosity = $1.set[i].v.ival; - break; - case cf_onsuccspec: - psmsg ("backend block onsuccess:", $1.set[i].v.sval); - cur_backend.onsuccess = $1.set[i].v.sval; - break; - case cf_onfailspec: - psmsg ("backend block onfailure:", $1.set[i].v.sval); - cur_backend.onfailure = $1.set[i].v.sval; - break; - case cf_dumpspec: - psmsg ("backend trafficlog:", $1.set[i].v.sval); - cur_backend.dumpfile = $1.set[i].v.sval; - break; - case cf_thruspec: - psmsg ("backend throughputlog:", $1.set[i].v.sval); - cur_backend.thruputfile = $1.set[i].v.sval; - break; - case cf_weightspec: - pimsg ("backend weight:", $1.set[i].v.ival); - cur_backend.weight = $1.set[i].v.ival; - break; - case cf_decayspec: - pimsg ("backend decay:", $1.set[i].v.ival); - if ($1.set[i].v.ival >= 100) - error ("Decay specifier %d must be a percentage, " - "never more than 99", - $1.set[i].v.ival); - cur_backend.decay = $1.set[i].v.ival; - break; - case cf_maxconnectionsspec: - pimsg ("backend max clients: ", $1.set[i].v.ival); - cur_backend.maxconnections = $1.set[i].v.ival; - break; - case cf_insertcookiespec: - psmsg ("backend cookie to insert:", - $1.set[i].v.sval); - cur_backend.insertcookie = $1.set[i].v.sval; - break; - case cf_stickycookiespec: - psmsg ("backend sticky cookie:", - $1.set[i].v.sval); - cur_backend.stickycookie = $1.set[i].v.sval; - break; - default: - error ("Internal jam, unhandled type %d " - "in backend specification", - $1.set[i].cf); - } - free ($1.set); - /* Verify the backend block, supply defaults, - * And so on. - */ - if (!cur_service.port) - error ("Back end %s lacks port", - cur_service.port); - if (!cur_backend.weight) - cur_backend.weight = 1; - - /* Add to the list. */ - cur_service.backend = xrealloc (cur_service.backend, - ++cur_service.nbackend * - sizeof(Service)); - cur_service.backend[cur_service.nbackend - 1] = - cur_backend; - pimsg ("this was backend defintion", cur_service.nbackend); - memset (&cur_backend, 0, sizeof(cur_backend)); + pimsg ("converting backend statements, count is", $1.n); + for (i = 0; i < $1.n; i++) + switch ($1.set[i].cf) { + case cf_portspec: + pimsg ("backend block port:", $1.set[i].v.ival); + cur_backend.port = $1.set[i].v.ival; + break; + case cf_serverspec: + psmsg ("backend block server:", $1.set[i].v.sval); + cur_backend.server = serverpart ($1.set[i].v.sval); + cur_backend.port = portpart ($1.set[i].v.sval); + free ($1.set[i].v.sval); + break; + case cf_verbosityspec: + pimsg ("backend block verbosity:", $1.set[i].v.ival); + cur_backend.verbosity = $1.set[i].v.ival; + break; + case cf_onsuccspec: + psmsg ("backend block onsuccess:", $1.set[i].v.sval); + cur_backend.onsuccess = $1.set[i].v.sval; + break; + case cf_onfailspec: + psmsg ("backend block onfailure:", $1.set[i].v.sval); + cur_backend.onfailure = $1.set[i].v.sval; + break; + case cf_dumpspec: + psmsg ("backend trafficlog:", $1.set[i].v.sval); + cur_backend.dumpfile = $1.set[i].v.sval; + break; + case cf_thruspec: + psmsg ("backend throughputlog:", $1.set[i].v.sval); + cur_backend.thruputfile = $1.set[i].v.sval; + break; + case cf_weightspec: + pimsg ("backend weight:", $1.set[i].v.ival); + cur_backend.weight = $1.set[i].v.ival; + break; + case cf_decayspec: + pimsg ("backend decay:", $1.set[i].v.ival); + if ($1.set[i].v.ival >= 100) + error ("Decay specifier %d must be a percentage, " + "never more than 99", + $1.set[i].v.ival); + cur_backend.decay = $1.set[i].v.ival; + break; + case cf_maxconnectionsspec: + pimsg ("backend max clients: ", $1.set[i].v.ival); + cur_backend.maxconnections = $1.set[i].v.ival; + break; + case cf_insertrealipspec: + pimsg ("backend real IP to insert:", + $1.set[i].v.ival); + cur_backend.insertrealip = $1.set[i].v.ival; + break; + case cf_insertcookiespec: + psmsg ("backend cookie to insert:", + $1.set[i].v.sval); + cur_backend.insertcookie = $1.set[i].v.sval; + break; + case cf_stickycookiespec: + psmsg ("backend sticky cookie:", + $1.set[i].v.sval); + cur_backend.stickycookie = $1.set[i].v.sval; + break; + default: + error ("Internal jam, unhandled type %d " + "in backend specification", + $1.set[i].cf); + } + free ($1.set); + /* Verify the backend block, supply defaults, + * And so on. + */ + if (!cur_service.port) + error ("Back end %s lacks port", + cur_service.port); + if (!cur_backend.weight) + cur_backend.weight = 1; + + /* Add to the list. */ + cur_service.backend = xrealloc (cur_service.backend, + ++cur_service.nbackend * + sizeof(Service)); + cur_service.backend[cur_service.nbackend - 1] = + cur_backend; + pimsg ("this was backend defintion", cur_service.nbackend); + memset (&cur_backend, 0, sizeof(cur_backend)); } ; @@ -299,11 +304,11 @@ portstatement: PORT number semicol { - pimsg ("port statement:", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_portspec; - $$.set[0].v.ival = lastnr; + pimsg ("port statement:", lastnr); + $$.n = 1; + $$.set = xmalloc (sizeof(Confset)); + $$.set[0].cf = cf_portspec; + $$.set[0].v.ival = lastnr; } ; @@ -311,27 +316,27 @@ bindstatement: BINDTO ipaddress semicol { - psmsg ("bindto statement:", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_bindspec; - $$.set[0].v.sval = xstrdup(laststr); + psmsg ("bindto statement:", laststr); + $$.n = 1; + $$.set = xmalloc (sizeof(Confset)); + $$.set[0].cf = cf_bindspec; + $$.set[0].v.sval = xstrdup(laststr); } ; ipaddress: ipaddress_expected STRING { - setlaststr (laststring); - free (laststring); - laststring = 0; + setlaststr (laststring); + free (laststring); + laststring = 0; } ; number: number_expected NUMBER { - setlastnr (yytext); + setlastnr (yytext); } ; @@ -345,21 +350,21 @@ verbositystatement: onoff_expected onoff semicol { - pimsg ("verbosity statement:", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_verbosityspec; - $$.set[0].v.ival = lastnr; + pimsg ("verbosity statement:", lastnr); + $$.n = 1; + $$.set = xmalloc (sizeof(Confset)); + $$.set[0].cf = cf_verbosityspec; + $$.set[0].v.ival = lastnr; } ; onoff: ON { - lastnr = 1; + lastnr = 1; } | OFF { - lastnr = 0; + lastnr = 0; } ; @@ -368,17 +373,17 @@ dispatchmodestatement: dispatchmethod opt_over semicol { - pimsg ("dispatch mode statement:", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_dispatchspec; - $$.set[0].v.ival = lastnr; + pimsg ("dispatch mode statement:", lastnr); + $$.n = 1; + $$.set = xmalloc (sizeof(Confset)); + $$.set[0].cf = cf_dispatchspec; + $$.set[0].v.ival = lastnr; - if (lastovernr && - (lastnr != ds_bysize && lastnr != ds_byduration)) - error ("Service '%s': this dispatch mode " - "doesn't support 'over <connections>'", - cur_service.name); + if (lastovernr && + (lastnr != ds_bysize && lastnr != ds_byduration)) + error ("Service '%s': this dispatch mode " + "doesn't support 'over <connections>'", + cur_service.name); } ; @@ -387,14 +392,14 @@ opt_over: overnumber | /* empty */ { - lastovernr = 0; + lastovernr = 0; } ; overnumber: number_expected NUMBER { - setlastovernr (yytext); + setlastovernr (yytext); } ; @@ -405,27 +410,27 @@ dispatchmethod: dispatchmethodspec: ROUNDROBIN { - lastnr = ds_roundrobin; + lastnr = ds_roundrobin; } | RANDOM { - lastnr = ds_random; + lastnr = ds_random; } | BYDURATION { - lastnr = ds_byduration; + lastnr = ds_byduration; } | BYSIZE { - lastnr = ds_bysize; + lastnr = ds_bysize; } | BYORDER { - lastnr = ds_byorder; + lastnr = ds_byorder; } | BYCONNECTIONS { - lastnr = ds_byconnections; + lastnr = ds_byconnections; } ; @@ -433,11 +438,11 @@ revivingintervalstatement: REVIVINGINTERVAL number semicol { - pimsg ("reviving interval statement:", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_revivespec; - $$.set[0].v.ival = lastnr; + pimsg ("reviving interval statement:", lastnr); + $$.n = 1; + $$.set = xmalloc (sizeof(Confset)); + $$.set[0].cf = cf_revivespec; + $$.set[0].v.ival = lastnr; } ; @@ -445,11 +450,11 @@ backlogstatement: BACKLOG number semicol { - pimsg ("backlog statement:", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_revivespec; - $$.set[0].v.ival = lastnr; + pimsg ("backlog statement:", lastnr); + $$.n = 1; + $$.set = xmalloc (sizeof(Confset)); + $$.set[0].cf = cf_revivespec; + $$.set[0].v.ival = lastnr; } ; @@ -457,11 +462,11 @@ shmkeystatement: SHMKEY number semicol { - pimsg ("shmkey statement:", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_shmkeyspec; - $$.set[0].v.ival = lastnr; + pimsg ("shmkey statement:", lastnr); + $$.n = 1; + $$.set = xmalloc (sizeof(Confset)); + $$.set[0].cf = cf_shmkeyspec; + $$.set[0].v.ival = lastnr; } ; @@ -469,11 +474,11 @@ connectiontimeoutstatement: CONNECTIONTIMEOUT number semicol { - pimsg ("connection timeout statement:", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_connectiontimeoutspec; - $$.set[0].v.ival = lastnr; + pimsg ("connection timeout statement:", lastnr); + $$.n = 1; + $$.set = xmalloc (sizeof(Confset)); + $$.set[0].cf = cf_connectiontimeoutspec; + $$.set[0].v.ival = lastnr; } ; @@ -481,11 +486,11 @@ maxconnectionsstatement: MAXCONNECTIONS number semicol { - pimsg ("max clients statement (service):", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_maxconnectionsspec; - $$.set[0].v.ival = lastnr; + pimsg ("max clients statement (service):", lastnr); + $$.n = 1; + $$.set = xmalloc (sizeof(Confset)); + $$.set[0].cf = cf_maxconnectionsspec; + $$.set[0].v.ival = lastnr; } ; @@ -493,11 +498,11 @@ typestatement: TYPE typespec semicol { - pimsg ("service type:", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof(Confset)); - $$.set[0].cf = cf_typespec; - $$.set[0].v.ival = lastnr; + pimsg ("service type:", lastnr); + $$.n = 1; + $$.set = xmalloc (sizeof(Confset)); + $$.set[0].cf = cf_typespec; + $$.set[0].v.ival = lastnr; } ; @@ -508,11 +513,11 @@ typespec: typespecifier: ANY { - lastnr = type_any; + lastnr = type_any; } | - STICKYHTTP { - lastnr = type_http; + HTTP { + lastnr = type_http; } ; @@ -522,98 +527,103 @@ backendblock: '{' backenddefinitions '}' { - $$ = $4; + $$ = $4; } ; backendname: backendname_expected IDENTIFIER { - psmsg ("backend name:", laststr); - cur_backend.name = xstrdup (yytext); + psmsg ("backend name:", laststr); + cur_backend.name = xstrdup (yytext); } ; backenddefinitions: backenddefinitions backenddefinition { - $1.n++; - $1.set = xrealloc ($1.set, $1.n * sizeof(Confset)); - $1.set[$1.n - 1] = $2.set[0]; - $$ = $1; + $1.n++; + $1.set = xrealloc ($1.set, $1.n * sizeof(Confset)); + $1.set[$1.n - 1] = $2.set[0]; + $$ = $1; } | backenddefinition { - $$ = $1; + $$ = $1; } ; backenddefinition: backendstatement_expected backendstatement { - $$ = $2; + $$ = $2; } ; backendstatement: serverstatement { - psmsg ("backend server:", $1.set[0].v.sval); - $$ = $1; - } + psmsg ("backend server:", $1.set[0].v.sval); + $$ = $1; + } | portstatement { - pimsg ("backend port:", $1.set[0].v.ival); - $$ = $1; + pimsg ("backend port:", $1.set[0].v.ival); + $$ = $1; } | verbositystatement { - pimsg ("backend verbosity:", $1.set[0].v.ival); - $$ = $1; + pimsg ("backend verbosity:", $1.set[0].v.ival); + $$ = $1; } | onsuccessstatement { - psmsg ("backend onsuccess:", $1.set[0].v.sval); - $$ = $1; + psmsg ("backend onsuccess:", $1.set[0].v.sval); + $$ = $1; } | onfailurestatement { - psmsg ("backend onfailure:", $1.set[0].v.sval); - $$ = $1; + psmsg ("backend onfailure:", $1.set[0].v.sval); + $$ = $1; } | dumptrafficstatement { - psmsg ("backend trafficlog:", $1.set[0].v.sval); - $$ = $1; + psmsg ("backend trafficlog:", $1.set[0].v.sval); + $$ = $1; } | throughputstatement { - psmsg ("backend trafficlog:", $1.set[0].v.sval); - $$ = $1; + psmsg ("backend trafficlog:", $1.set[0].v.sval); + $$ = $1; } | weightstatement { - pimsg ("backend weight:", $1.set[0].v.ival); - $$ = $1; + pimsg ("backend weight:", $1.set[0].v.ival); + $$ = $1; } | decaystatement { - pimsg ("backend decay:", $1.set[0].v.ival); - $$ = $1; + pimsg ("backend decay:", $1.set[0].v.ival); + $$ = $1; } | maxconnectionsstatement { - pimsg ("backend maxconnections:", $1.set[0].v.ival); - $$ = $1; + pimsg ("backend maxconnections:", $1.set[0].v.ival); + $$ = $1; } | stickycookiestatement { - psmsg ("backend sticky cookie:", $1.set[0].v.sval); - $$ = $1; + psmsg ("backend sticky cookie:", $1.set[0].v.sval); + $$ = $1; } | insertcookiestatement { - psmsg ("backend cookie insertion:", $1.set[0].v.sval); - $$ = $1; + psmsg ("backend cookie insertion:", $1.set[0].v.sval); + $$ = $1; + } +| + insertrealipstatement { + pimsg ("insert real ip:", $1.set[0].v.ival); + $$ = $1; } ; @@ -622,11 +632,11 @@ serverstatement: serveraddress_expected serveraddress semicol { - psmsg ("server statement:", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_serverspec; - $$.set[0].v.sval = xstrdup (laststr); + psmsg ("server statement:", laststr); + $$.n = 1; + $$.set = xmalloc (sizeof (Confset)); + $$.set[0].cf = cf_serverspec; + $$.set[0].v.sval = xstrdup (laststr); } ; @@ -634,11 +644,11 @@ weightstatement: WEIGHT number semicol { - pimsg ("weight statement", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_weightspec; - $$.set[0].v.ival = lastnr; + pimsg ("weight statement", lastnr); + $$.n = 1; + $$.set = xmalloc (sizeof (Confset)); + $$.set[0].cf = cf_weightspec; + $$.set[0].v.ival = lastnr; } ; @@ -646,17 +656,17 @@ decaystatement: DECAY number semicol { - pimsg ("decay statement", lastnr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_decayspec; - $$.set[0].v.ival = lastnr; + pimsg ("decay statement", lastnr); + $$.n = 1; + $$.set = xmalloc (sizeof (Confset)); + $$.set[0].cf = cf_decayspec; + $$.set[0].v.ival = lastnr; } ; serveraddress: STRING { - setlaststr (laststring); + setlaststr (laststring); } ; @@ -664,11 +674,11 @@ onsuccessstatement: ONSUCCESS commandline semicol { - psmsg ("onsuccess statement:", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_onsuccspec; - $$.set[0].v.sval = xstrdup (laststr); + psmsg ("onsuccess statement:", laststr); + $$.n = 1; + $$.set = xmalloc (sizeof (Confset)); + $$.set[0].cf = cf_onsuccspec; + $$.set[0].v.sval = xstrdup (laststr); } ; @@ -676,11 +686,11 @@ onfailurestatement: ONFAILURE commandline semicol { - psmsg ("onfailure statement:", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_onfailspec; - $$.set[0].v.sval = xstrdup (laststr); + psmsg ("onfailure statement:", laststr); + $$.n = 1; + $$.set = xmalloc (sizeof (Confset)); + $$.set[0].cf = cf_onfailspec; + $$.set[0].v.sval = xstrdup (laststr); } ; @@ -688,11 +698,11 @@ dumptrafficstatement: TRAFFICLOG filename semicol { - psmsg ("trafficlog statement:", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_dumpspec; - $$.set[0].v.sval = xstrdup (laststr); + psmsg ("trafficlog statement:", laststr); + $$.n = 1; + $$.set = xmalloc (sizeof (Confset)); + $$.set[0].cf = cf_dumpspec; + $$.set[0].v.sval = xstrdup (laststr); } ; @@ -700,29 +710,29 @@ throughputstatement: THROUGHPUTLOG filename semicol { - psmsg ("throughputlog statement:", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_thruspec; - $$.set[0].v.sval = xstrdup (laststr); + psmsg ("throughputlog statement:", laststr); + $$.n = 1; + $$.set = xmalloc (sizeof (Confset)); + $$.set[0].cf = cf_thruspec; + $$.set[0].v.sval = xstrdup (laststr); } ; commandline: commandline_expected STRING { - setlaststr (laststring); - free (laststring); - laststring = 0; + setlaststr (laststring); + free (laststring); + laststring = 0; } ; filename: filename_expected STRING { - setlaststr (laststring); - free (laststring); - laststring = 0; + setlaststr (laststring); + free (laststring); + laststring = 0; } ; @@ -730,11 +740,11 @@ stickycookiestatement: STICKYCOOKIE cookiespecifier semicol { - psmsg ("insertcookie statement:", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_stickycookiespec; - $$.set[0].v.sval = xstrdup (laststr); + psmsg ("insertcookie statement:", laststr); + $$.n = 1; + $$.set = xmalloc (sizeof (Confset)); + $$.set[0].cf = cf_stickycookiespec; + $$.set[0].v.sval = xstrdup (laststr); } ; @@ -742,14 +752,28 @@ insertcookiestatement: INSERTCOOKIE cookiespecifier semicol { - psmsg ("insertcookie statement:", laststr); - $$.n = 1; - $$.set = xmalloc (sizeof (Confset)); - $$.set[0].cf = cf_insertcookiespec; - $$.set[0].v.sval = xstrdup (laststr); + psmsg ("insertcookie statement:", laststr); + $$.n = 1; + $$.set = xmalloc (sizeof (Confset)); + $$.set[0].cf = cf_insertcookiespec; + $$.set[0].v.sval = xstrdup (laststr); + } +; + +insertrealipstatement: + INSERTREALIP + onoff_expected + onoff + semicol { + pimsg ("insert real IP statement: ", lastnr); + $$.n = 1; + $$.set = xmalloc (sizeof(Confset)); + $$.set[0].cf = cf_insertrealipspec; + $$.set[0].v.ival = lastnr; } ; + cookiespecifier: cookie_expected STRING { @@ -760,76 +784,76 @@ cookiespecifier: ; cookie_expected: { - yyerrmsg = "cookie specifier expected"; - } + yyerrmsg = "cookie specifier expected"; +} ; number_expected: { - yyerrmsg = "number expected"; - } + yyerrmsg = "number expected"; +} ; serveraddress_expected: { - yyerrmsg = "hostname or IP address expected"; - } + yyerrmsg = "hostname or IP address expected"; +} ; service_expected: { - yyerrmsg = "'service' expected"; - } + yyerrmsg = "'service' expected"; +} ; backendstatement_expected: { - yyerrmsg = "backend definition statement expected"; - } + yyerrmsg = "backend definition statement expected"; +} ; servicebody_expected: { - yyerrmsg = "service body statement expected"; - } + yyerrmsg = "service body statement expected"; +} ; semicol_expected: { - yyerrmsg = "semicolon (;) expected"; - } + yyerrmsg = "semicolon (;) expected"; +} ; onoff_expected: { - yyerrmsg = "'on' or 'off' expetcted"; - } + yyerrmsg = "'on' or 'off' expetcted"; +} ; dispatchmethod_expected: { - yyerrmsg = "dispatch method expected"; - } + yyerrmsg = "dispatch method expected"; +} ; commandline_expected: { - yyerrmsg = "command line expected"; - } + yyerrmsg = "command line expected"; +} ; filename_expected: { - yyerrmsg = "file name expected"; - } + yyerrmsg = "file name expected"; +} ; servicename_expected: { - yyerrmsg = "service name (identifier) expected"; - } + yyerrmsg = "service name (identifier) expected"; +} ; backendname_expected: { - yyerrmsg = "backend name (identifier) expected"; - } + yyerrmsg = "backend name (identifier) expected"; +} ; ipaddress_expected: { - yyerrmsg = "IP address or 'any' expected"; - } + yyerrmsg = "IP address or 'any' expected"; +} ; type_expected: { - yyerrmsg = "Service type expected ('any', 'stickyhttp', ...)"; - } + yyerrmsg = "Service type expected ('any', 'stickyhttp', ...)"; +} ; diff --git a/test/client b/test/client @@ -14,7 +14,7 @@ ENDUSAGE my ($host, $port) = @ARGV; print ("Starting client loop to query $host:$port.. press ^C to stop\n"); -sleep (1); +select (undef, undef, undef, 0.2); my %hits = (a => 0, b => 0, c => 0, unknown => 0, errors => 0); @@ -25,7 +25,7 @@ while (1) { $err = 1; while (my $msg = <$if>) { chomp ($msg); - # print ("Server says: [$msg]\n"); + # print ("Server says: [$msg]\n"); if ($msg =~ /HTTP.*200 OK/ or $msg =~ /Hello World/) { $err = 0; } elsif ($msg =~ /Backend_A/) { @@ -51,6 +51,5 @@ while (1) { } print ("\n"); - # sleep (1); select (undef, undef, undef, 0.2); }; diff --git a/test/server b/test/server @@ -34,14 +34,19 @@ sub handleconnection () { } # Child branch - select Client; - $| = 1; + while (defined (my $line = <Client>)) { + chomp ($line); + $line =~ s/\r//; + last if ($line eq ''); + msg ("Client says: [$line]\n"); + } + msg ("Sending response\n"); my $msg = "Hello World!\n"; - print ("HTTP/1.0 200 OK\r\n", - "Content-length: ", length($msg), "\r\n", - "\r\n", - $msg); + print Client ("HTTP/1.0 200 OK\r\n", + "Content-length: ", length($msg), "\r\n", + "\r\n", + $msg); exit (0); } diff --git a/test/t1.conf b/test/t1.conf @@ -1,6 +1,6 @@ service test { port 10001; - type stickyhttp; + type http; bindto any; revivinginterval 5; dispatchmode bysize; diff --git a/test/t2.conf b/test/t2.conf @@ -1,6 +1,6 @@ service test { port 10001; - type stickyhttp; + type http; bindto any; revivinginterval 5; dispatchmode random; diff --git a/test/t4.conf b/test/t4.conf @@ -0,0 +1,35 @@ +service test { + port 10001; + type http; + revivinginterval 5; + dispatchmode roundrobin; + connectiontimeout 600; + verbosity on; + + backend a { + server localhost; + port 10000; + verbosity on; + stickycookie BalancerID=Backend_A; + insertcookie "BalancerID=Backend_A; Path=/"; + insertrealip on; + } + + backend b { + server localhost; + port 10000; + verbosity on; + stickycookie BalancerID=Backend_B; + insertcookie "BalancerID=Backend_B; Path=/"; + insertrealip on; + } + + backend c { + server localhost; + port 10000; + verbosity on; + stickycookie BalancerID=Backend_C; + insertcookie "BalancerID=Backend_C; Path=/"; + insertrealip on; + } +} diff --git a/tools/untab b/tools/untab @@ -0,0 +1,31 @@ +#!/usr/bin/perl + +use strict; + +die ("Usage: untab file [file...]\n") if ($#ARGV == -1); + +for my $in (@ARGV) { + my $out = "$in.new"; + open (my $fin, $in) or die ("Can't read $in: $!\n"); + open (my $fout, ">$out") or die ("Can't write $out: $!\n"); + + my $changed = 0; + while (defined (my $line = <$fin>)) { + while ($line =~ /^\t/) { + $line =~ s/^\t/ /; + $changed++; + } + print $fout ($line); + } + + close ($fin); + close ($fout); + + if (!$changed) { + unlink ($out) or die ("Can't unlink $out: $!\n"); + } else { + print ("Untabbed $in ($changed times)\n"); + unlink ($in) or die ("Can't unlink $in: $!\n"); + rename ($out, $in) or die ("Can't rename $out to $in: $!\n"); + } +}