* released 1.2.2 (1.1.29)
* fixed a bug where a TCP connection would be logged twice if the 'logasap' option was enabled without the 'tcplog' option. * encode_string() would use hdr_encode_map instead of the map argument. * the logged request is now encoded with '#XX' for unprintable characters * new keywords 'capture request header' and 'capture response header' enable logging of arbitrary HTTP headers in requests and responses * removed "-DSOLARIS" after replacing the last inet_aton() with inet_pton()
This commit is contained in:
parent
982249e9e7
commit
4302f49525
12
CHANGELOG
12
CHANGELOG
@ -1,5 +1,17 @@
|
||||
ChangeLog :
|
||||
===========
|
||||
|
||||
2004/10/18 : 1.2.2 (1.1.29)
|
||||
- fixed a bug where a TCP connection would be logged twice if the 'logasap'
|
||||
option was enabled without the 'tcplog' option.
|
||||
- encode_string() would use hdr_encode_map instead of the map argument.
|
||||
|
||||
2004/08/10 : (1.1.29-pre2)
|
||||
- the logged request is now encoded with '#XX' for unprintable characters
|
||||
- new keywords 'capture request header' and 'capture response header' enable
|
||||
logging of arbitrary HTTP headers in requests and responses
|
||||
- removed "-DSOLARIS" after replacing the last inet_aton() with inet_pton()
|
||||
|
||||
2004/06/06 : 1.2.1 (1.1.28)
|
||||
- added the '-V' command line option to verbosely report errors even though
|
||||
the -q or 'quiet' options are specified. This is useful with '-c'.
|
||||
|
3
TODO
3
TODO
@ -136,6 +136,7 @@ Todo for 1.2
|
||||
* listen [ip4.ip4.ip4.ip4]:port[-port]
|
||||
* listen [ip6::...ip6]/port[-port]
|
||||
- server xxx ipv4 | ipv4: | ipv4:port[-port] | ipv6/ | ipv6/port[-port]
|
||||
|
||||
- appcookie
|
||||
- weighted round robin
|
||||
- option to shutdown(listen_sock) when max connections reached
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
|
||||
H A - P r o x y
|
||||
---------------
|
||||
version 1.2.1
|
||||
version 1.2.2
|
||||
willy tarreau
|
||||
2004/06/06
|
||||
2004/10/18
|
||||
|
||||
============
|
||||
| Abstract |
|
||||
@ -227,7 +227,9 @@ eventhough the load needed to saturate a recent processor is far above common
|
||||
usage. Anyway, for very specific needs, the proxy can start several processes
|
||||
between which the operating system will spread the incoming connections. The
|
||||
number of processes is controlled by the 'nbproc' parameter in the 'global'
|
||||
section. It defaults to 1, and obviously works only in 'daemon' mode.
|
||||
section. It defaults to 1, and obviously works only in 'daemon' mode. One
|
||||
typical usage of this parameter has been to workaround the default per-process
|
||||
file-descriptor limit that Solaris imposes to user processes.
|
||||
|
||||
Example :
|
||||
---------
|
||||
@ -1154,6 +1156,67 @@ client. Each of these field is replaced with '-' when no cookie was seen.
|
||||
connection to the server failed ('SC--') after 4 attemps of 2 seconds
|
||||
(config says 'retries 3'), then a 503 error code was sent to the client.
|
||||
|
||||
4.2.6) Non-printable characters
|
||||
-------------------------------
|
||||
As of version 1.1.29, non-printable characters are not sent as-is into log
|
||||
files, but are converted to their two-digits hexadecimal representation,
|
||||
prefixed by the character '#'. The only characters that can now be logged
|
||||
without being escaped are between 32 and 126 (inclusive). Obviously, the
|
||||
escape character '#' is also encoded to avoid any ambiguity. It is the same for
|
||||
the character '"', as well as '{', '|' and '}' when logging headers.
|
||||
|
||||
4.2.7) Logging HTTP headers
|
||||
---------------------------
|
||||
As of version 1.1.29, it is now possible to log HTTP headers extracts. It is
|
||||
both possible to include request headers and response headers. It is
|
||||
particularly useful to know what virtual server the client requested, to know
|
||||
the content length during a POST request, or a unique request ID set on a
|
||||
previous proxy. In the response, one can search for information about the
|
||||
response length, how the server asked the cache to behave, or an object location
|
||||
during a redirection. The syntax is :
|
||||
|
||||
capture request header <name> len <max length>
|
||||
capture response header <name> len <max length>
|
||||
|
||||
Note: Header names are not case-sensitive.
|
||||
|
||||
Examples:
|
||||
---------
|
||||
# keep the name of the virtual server
|
||||
capture request header Host len 20
|
||||
# keep the amount of data uploaded during a POST
|
||||
capture request header Content-Length len 10
|
||||
|
||||
# note the expected cache behaviour on the response
|
||||
capture response header Cache-Control len 8
|
||||
# note the URL location during a redirection
|
||||
capture response header Location len 20
|
||||
|
||||
Non-existant headers are logged as empty strings, and if one header appears more
|
||||
than once, only its last occurence will be kept. Request headers are grouped
|
||||
within braces '{' and '}' in the same order as they were declared, and delimited
|
||||
with a vertical bar '|' without any space. Response headers follow the same
|
||||
representation, but are displayed after a space following the request headers
|
||||
block. These blocks are displayed just before the HTTP request in the logs.
|
||||
Example :
|
||||
|
||||
Config:
|
||||
|
||||
capture request header Host len 20
|
||||
capture request header Content-Length len 10
|
||||
capture request header Referer len 20
|
||||
capture response header Server len 20
|
||||
capture response header Content-Length len 10
|
||||
capture response header Cache-Control len 8
|
||||
capture response header Via len 20
|
||||
capture response header Location len 20
|
||||
|
||||
Log :
|
||||
|
||||
Aug 9 20:26:09 localhost haproxy[2022]: 127.0.0.1:34014 [09/Aug/2004:20:26:09] relais-http netcache 0/0/162/+162 200 +350 - - ---- {fr.adserver.yahoo.co||http://fr.f416.mail.} {|864|private||} "GET http://fr.adserver.yahoo.com/"
|
||||
Aug 9 20:30:46 localhost haproxy[2022]: 127.0.0.1:34020 [09/Aug/2004:20:30:46] relais-http netcache 0/0/182/+182 200 +279 - - ---- {w.ods.org||} {Formilux/0.1.8|3495|||} "GET http://w.ods.org/sytadin.html HTTP/1.1"
|
||||
Aug 9 20:30:46 localhost haproxy[2022]: 127.0.0.1:34028 [09/Aug/2004:20:30:46] relais-http netcache 0/2/126/+128 200 +223 - - ---- {www.infotrafic.com||http://w.ods.org/syt} {Apache/2.0.40 (Red H|9068|||} "GET http://www.infotrafic.com/images/live/cartesidf/grandes/idf_ne.png HTTP/1.1"
|
||||
|
||||
4.3) HTTP header manipulation
|
||||
-----------------------------
|
||||
In HTTP mode, it is possible to rewrite, add or delete some of the request and
|
||||
|
@ -1,9 +1,9 @@
|
||||
|
||||
H A - P r o x y
|
||||
---------------
|
||||
version 1.2.1
|
||||
version 1.2.2
|
||||
willy tarreau
|
||||
2004/06/06
|
||||
2004/10/18
|
||||
|
||||
================
|
||||
| Introduction |
|
||||
@ -240,7 +240,9 @@ grandeur couramment rencontr
|
||||
programme sait démarrer plusieurs processus se répartissant la charge de
|
||||
travail. Ce nombre de processus est spécifié par le paramètre 'nbproc' de la
|
||||
section 'global'. Sa valeur par défaut est naturellement 1. Ceci ne fonctionne
|
||||
qu'en mode 'daemon'.
|
||||
qu'en mode 'daemon'. Un usage déjà rencontré pour ce paramètre fut de dépasser
|
||||
la limite de nombre de descripteurs de fichiers allouée par processus sous
|
||||
Solaris.
|
||||
|
||||
Exemple :
|
||||
---------
|
||||
@ -1192,6 +1194,69 @@ client ou le serveur.
|
||||
connexion ('SC--') vers le serveur échoue au bout de 4 tentatives de 2
|
||||
secondes (retries 3 dans la conf), puis un code 503 est retourné au client.
|
||||
|
||||
4.2.6) Caractères non-imprimables
|
||||
---------------------------------
|
||||
Depuis la version 1.1.29, les caractères non-imprimables ne sont plus envoyés
|
||||
tels quels dans les lignes de logs, mais inscrits sous la forme de deux chiffres
|
||||
hexadécimaux, préfixés du caractère d'échappement '#'. Les seuls caractères
|
||||
dorénavant logués tels quels sont compris entre 32 et 126. Bien évidemment, le
|
||||
caractère d'échappement '#' est lui-même encodé afin de lever l'ambiguité. Il en
|
||||
est de même pour le caractère '"', ainsi que les caractères '{', '|' et '}' pour
|
||||
les en-têtes.
|
||||
|
||||
4.2.7) Journalisation d'en-têtes
|
||||
--------------------------------
|
||||
Depuis la version 1.1.29, il est désormais possible de conserver des extraits
|
||||
d'en-têtes HTTP dans les logs. On peut aussi bien extraire des en-têtes de la
|
||||
requête que de la réponse. C'est particulièrement pratique pour savoir à quel
|
||||
serveur virtuel une requête s'adressait, pour connaitre la longueur des données
|
||||
émises lors d'un POST, ou encore loguer un identifiant unique de requête
|
||||
positionné en amont. Dans la réponse, on peut chercher également à conserver des
|
||||
informations relatives à la taille annoncée de la réponse, le fonctionnement
|
||||
attendu du cache, ou encore la localisation d'un objet en cas de redirection.
|
||||
La syntaxe est la suivante :
|
||||
|
||||
capture request header <nom> len <longueur max>
|
||||
capture response header <nom> len <longueur max>
|
||||
|
||||
Note: Les noms d'en-têtes ne sont pas sensibles à la casse.
|
||||
|
||||
Exemples:
|
||||
---------
|
||||
# conserver le nom du serveur virtuel accédé par le client
|
||||
capture request header Host len 20
|
||||
# noter la longueur des données envoyées dans un POST
|
||||
capture request header Content-Length len 10
|
||||
|
||||
# noter le fonctionnement attendu du cache par le serveur
|
||||
capture response header Cache-Control len 8
|
||||
# noter l'URL de redirection
|
||||
capture response header Location len 20
|
||||
|
||||
Les en-têtes non trouvés sont logués à vide, et si un en-tête apparait plusieurs
|
||||
fois, seule la dernière occurence sera conservée. Les en-têtes de requête sont
|
||||
regroupés entre deux accolades '{' et '}' dans l'ordre de leur déclaration, et
|
||||
chacun séparés par une barre verticale '|', sans aucun espace. Les en-têtes de
|
||||
réponse sont présentés de la même manière, mais après un espace suivant le bloc
|
||||
d'en-tête de requête. Le tout précède la requête HTTP. Exemple :
|
||||
|
||||
Config:
|
||||
|
||||
capture request header Host len 20
|
||||
capture request header Content-Length len 10
|
||||
capture request header Referer len 20
|
||||
capture response header Server len 20
|
||||
capture response header Content-Length len 10
|
||||
capture response header Cache-Control len 8
|
||||
capture response header Via len 20
|
||||
capture response header Location len 20
|
||||
|
||||
Log :
|
||||
|
||||
Aug 9 20:26:09 localhost haproxy[2022]: 127.0.0.1:34014 [09/Aug/2004:20:26:09] relais-http netcache 0/0/162/+162 200 +350 - - ---- {fr.adserver.yahoo.co||http://fr.f416.mail.} {|864|private||} "GET http://fr.adserver.yahoo.com/"
|
||||
Aug 9 20:30:46 localhost haproxy[2022]: 127.0.0.1:34020 [09/Aug/2004:20:30:46] relais-http netcache 0/0/182/+182 200 +279 - - ---- {w.ods.org||} {Formilux/0.1.8|3495|||} "GET http://w.ods.org/sytadin.html HTTP/1.1"
|
||||
Aug 9 20:30:46 localhost haproxy[2022]: 127.0.0.1:34028 [09/Aug/2004:20:30:46] relais-http netcache 0/2/126/+128 200 +223 - - ---- {www.infotrafic.com||http://w.ods.org/syt} {Apache/2.0.40 (Red H|9068|||} "GET http://www.infotrafic.com/images/live/cartesidf/grandes/idf_ne.png HTTP/1.1"
|
||||
|
||||
4.3) Modification des entêtes HTTP
|
||||
----------------------------------
|
||||
En mode HTTP uniquement, il est possible de remplacer certains en-têtes dans la
|
||||
|
@ -1,6 +1,6 @@
|
||||
Summary: HA-Proxy is a TCP/HTTP reverse proxy for high availability environments
|
||||
Name: haproxy
|
||||
Version: 1.2.1
|
||||
Version: 1.2.3
|
||||
Release: 1
|
||||
License: GPL
|
||||
Group: System Environment/Daemons
|
||||
@ -9,8 +9,6 @@ Packager: Simon Matter <simon.matter@invoca.ch>
|
||||
Vendor: Invoca Systems
|
||||
Distribution: Invoca Linux Server
|
||||
Source0: http://w.ods.org/tools/%{name}/%{name}-%{version}.tar.gz
|
||||
Source1: %{name}.cfg
|
||||
Source2: %{name}.init
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-root
|
||||
BuildRequires: pcre-devel
|
||||
Prereq: /sbin/chkconfig
|
||||
@ -35,20 +33,18 @@ risking the system's stability.
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
%{__make} REGEX=pcre DEBUG=""
|
||||
%{__make} REGEX=pcre DEBUG="" LIBS.pcre="-L\$(PCREDIR)/lib -Wl,-Bstatic -lpcreposix -lpcre -Wl,-Bdynamic"
|
||||
|
||||
%install
|
||||
[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot}
|
||||
|
||||
%{__install} -d %{buildroot}%{_sbindir}
|
||||
%{__install} -d %{buildroot}%{_sysconfdir}/rc.d/init.d
|
||||
%{__install} -d %{buildroot}%{_sysconfdir}/logrotate.d
|
||||
%{__install} -d %{buildroot}%{_sysconfdir}/%{name}
|
||||
%{__install} -d %{buildroot}%{_datadir}/%{name}
|
||||
|
||||
%{__install} -s %{name} %{buildroot}%{_sbindir}/
|
||||
%{__install} -c -m 644 %{SOURCE1} %{buildroot}%{_sysconfdir}/%{name}/
|
||||
%{__install} -c -m 755 %{SOURCE2} %{buildroot}%{_sysconfdir}/rc.d/init.d/%{name}
|
||||
%{__install} -c -m 644 examples/%{name}.cfg %{buildroot}%{_sysconfdir}/%{name}/
|
||||
%{__install} -c -m 755 examples/%{name}.init %{buildroot}%{_sysconfdir}/rc.d/init.d/%{name}
|
||||
|
||||
%clean
|
||||
[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot}
|
||||
@ -69,14 +65,21 @@ fi
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%doc CHANGELOG TODO examples
|
||||
%doc CHANGELOG TODO examples doc/haproxy-en.txt doc/haproxy-fr.txt doc/architecture.txt
|
||||
%attr(0755,root,root) %{_sbindir}/%{name}
|
||||
%dir %{_sysconfdir}/%{name}
|
||||
%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/%{name}/%{name}.cfg
|
||||
%attr(0755,root,root) %config %{_sysconfdir}/rc.d/init.d/%{name}
|
||||
%dir %{_datadir}/%{name}
|
||||
|
||||
%changelog
|
||||
* Sat Jan 22 2005 Willy Tarreau <willy@w.ods.org>
|
||||
- updated to 1.2.3 (1.1.30)
|
||||
|
||||
* Sun Nov 14 2004 Willy Tarreau <w@w.ods.org>
|
||||
- updated to 1.1.29
|
||||
- fixed path to config and init files
|
||||
- statically linked PCRE to increase portability to non-pcre systems
|
||||
|
||||
* Sun Jun 6 2004 Willy Tarreau <willy@w.ods.org>
|
||||
- updated to 1.1.28
|
||||
- added config check support to the init script
|
||||
|
413
haproxy.c
413
haproxy.c
@ -29,6 +29,7 @@
|
||||
* - fix client/server state transition when server is in connect or headers state
|
||||
* and client suddenly disconnects. The server *should* switch to SHUT_WR, but
|
||||
* still handle HTTP headers.
|
||||
* - remove MAX_NEWHDR
|
||||
*
|
||||
*/
|
||||
|
||||
@ -56,8 +57,8 @@
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#endif
|
||||
|
||||
#define HAPROXY_VERSION "1.2.1"
|
||||
#define HAPROXY_DATE "2004/06/06"
|
||||
#define HAPROXY_VERSION "1.2.2"
|
||||
#define HAPROXY_DATE "2004/10/18"
|
||||
|
||||
/* this is for libc5 for example */
|
||||
#ifndef TCP_NODELAY
|
||||
@ -145,6 +146,35 @@ int strlcpy2(char *dst, const char *src, int size) {
|
||||
return dst - orig;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a pointer to an area of <__len> bytes taken from the pool <pool> or
|
||||
* dynamically allocated. In the first case, <__pool> is updated to point to
|
||||
* the next element in the list.
|
||||
*/
|
||||
#define pool_alloc_from(__pool, __len) ({ \
|
||||
void *__p; \
|
||||
if ((__p = (__pool)) == NULL) \
|
||||
__p = malloc(((__len) >= sizeof (void *)) ? (__len) : sizeof(void *)); \
|
||||
else { \
|
||||
__pool = *(void **)(__pool); \
|
||||
} \
|
||||
__p; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Puts a memory area back to the corresponding pool.
|
||||
* Items are chained directly through a pointer that
|
||||
* is written in the beginning of the memory area, so
|
||||
* there's no need for any carrier cell. This implies
|
||||
* that each memory area is at least as big as one
|
||||
* pointer.
|
||||
*/
|
||||
#define pool_free_to(__pool, __ptr) ({ \
|
||||
*(void **)(__ptr) = (void *)(__pool); \
|
||||
__pool = (void *)(__ptr); \
|
||||
})
|
||||
|
||||
|
||||
#define MEM_OPTIM
|
||||
#ifdef MEM_OPTIM
|
||||
/*
|
||||
@ -154,13 +184,13 @@ int strlcpy2(char *dst, const char *src, int size) {
|
||||
* next element in the list.
|
||||
*/
|
||||
#define pool_alloc(type) ({ \
|
||||
void *p; \
|
||||
if ((p = pool_##type) == NULL) \
|
||||
p = malloc(sizeof_##type); \
|
||||
void *__p; \
|
||||
if ((__p = pool_##type) == NULL) \
|
||||
__p = malloc(sizeof_##type); \
|
||||
else { \
|
||||
pool_##type = *(void **)pool_##type; \
|
||||
} \
|
||||
p; \
|
||||
__p; \
|
||||
})
|
||||
|
||||
/*
|
||||
@ -332,6 +362,9 @@ int strlcpy2(char *dst, const char *src, int size) {
|
||||
#define LW_PXIP 64 /* proxy IP */
|
||||
#define LW_PXID 128 /* proxy ID */
|
||||
#define LW_BYTES 256 /* bytes read from server */
|
||||
#define LW_COOKIE 512 /* captured cookie */
|
||||
#define LW_REQHDR 1024 /* request header(s) */
|
||||
#define LW_RSPHDR 2048 /* response header(s) */
|
||||
|
||||
/*********************************************************************/
|
||||
|
||||
@ -339,6 +372,15 @@ int strlcpy2(char *dst, const char *src, int size) {
|
||||
|
||||
/*********************************************************************/
|
||||
|
||||
struct cap_hdr {
|
||||
struct cap_hdr *next;
|
||||
char *name; /* header name, case insensitive */
|
||||
int namelen; /* length of the header name, to speed-up lookups */
|
||||
int len; /* capture length, not including terminal zero */
|
||||
int index; /* index in the output array */
|
||||
void *pool; /* pool of pre-allocated memory area of (len+1) bytes */
|
||||
};
|
||||
|
||||
struct hdr_exp {
|
||||
struct hdr_exp *next;
|
||||
regex_t *preg; /* expression to look for */
|
||||
@ -403,6 +445,8 @@ struct session {
|
||||
struct sockaddr_storage cli_addr; /* the client address */
|
||||
struct sockaddr_in srv_addr; /* the address to connect to */
|
||||
struct server *srv; /* the server being used */
|
||||
char **req_cap; /* array of captured request headers (may be NULL) */
|
||||
char **rsp_cap; /* array of captured response headers (may be NULL) */
|
||||
struct {
|
||||
int logwait; /* log fields waiting to be collected : LW_* */
|
||||
struct timeval tv_accept; /* date of the accept() (beginning of the session) */
|
||||
@ -456,6 +500,10 @@ struct proxy {
|
||||
int nb_reqadd, nb_rspadd;
|
||||
struct hdr_exp *req_exp; /* regular expressions for request headers */
|
||||
struct hdr_exp *rsp_exp; /* regular expressions for response headers */
|
||||
int nb_req_cap, nb_rsp_cap; /* # of headers to be captured */
|
||||
struct cap_hdr *req_cap; /* chained list of request headers to be captured */
|
||||
struct cap_hdr *rsp_cap; /* chained list of response headers to be captured */
|
||||
void *req_cap_pool, *rsp_cap_pool; /* pools of pre-allocated char ** used to build the sessions */
|
||||
char *req_add[MAX_NEWHDR], *rsp_add[MAX_NEWHDR]; /* headers to be added */
|
||||
int grace; /* grace time after stop request */
|
||||
char *check_req; /* HTTP request to use if PR_O_HTTP_CHK is set, else NULL */
|
||||
@ -825,6 +873,7 @@ struct listener *str2listener(char *str, struct listener *tail) {
|
||||
int port, end;
|
||||
|
||||
next = dupstr = strdup(str);
|
||||
|
||||
while (next && *next) {
|
||||
struct sockaddr_storage ss;
|
||||
|
||||
@ -896,6 +945,56 @@ struct listener *str2listener(char *str, struct listener *tail) {
|
||||
}
|
||||
|
||||
|
||||
#define FD_SETS_ARE_BITFIELDS
|
||||
#ifdef FD_SETS_ARE_BITFIELDS
|
||||
/*
|
||||
* This map is used with all the FD_* macros to check whether a particular bit
|
||||
* is set or not. Each bit represents an ACSII code. FD_SET() sets those bytes
|
||||
* which should be encoded. When FD_ISSET() returns non-zero, it means that the
|
||||
* byte should be encoded. Be careful to always pass bytes from 0 to 255
|
||||
* exclusively to the macros.
|
||||
*/
|
||||
fd_set hdr_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
|
||||
fd_set url_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
|
||||
|
||||
#else
|
||||
#error "Check if your OS uses bitfields for fd_sets"
|
||||
#endif
|
||||
|
||||
/* will try to encode the string <string> replacing all characters tagged in
|
||||
* <map> with the hexadecimal representation of their ASCII-code (2 digits)
|
||||
* prefixed by <escape>, and will store the result between <start> (included
|
||||
*) and <stop> (excluded), and will always terminate the string with a '\0'
|
||||
* before <stop>. The position of the '\0' is returned if the conversion
|
||||
* completes. If bytes are missing between <start> and <stop>, then the
|
||||
* conversion will be incomplete and truncated. If <stop> <= <start>, the '\0'
|
||||
* cannot even be stored so we return <start> without writing the 0.
|
||||
* The input string must also be zero-terminated.
|
||||
*/
|
||||
char hextab[16] = "0123456789ABCDEF";
|
||||
char *encode_string(char *start, char *stop,
|
||||
const char escape, const fd_set *map,
|
||||
const char *string)
|
||||
{
|
||||
if (start < stop) {
|
||||
stop--; /* reserve one byte for the final '\0' */
|
||||
while (start < stop && *string != 0) {
|
||||
if (!FD_ISSET((unsigned char)(*string), map))
|
||||
*start++ = *string;
|
||||
else {
|
||||
if (start + 3 >= stop)
|
||||
break;
|
||||
*start++ = escape;
|
||||
*start++ = hextab[(*string >> 4) & 15];
|
||||
*start++ = hextab[*string & 15];
|
||||
}
|
||||
string++;
|
||||
}
|
||||
*start = '\0';
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function sends a syslog message to both log servers of a proxy,
|
||||
* or to global log servers if the proxy is NULL.
|
||||
@ -1422,6 +1521,24 @@ static inline void session_free(struct session *s) {
|
||||
pool_free(buffer, s->req);
|
||||
if (s->rep)
|
||||
pool_free(buffer, s->rep);
|
||||
|
||||
if (s->rsp_cap != NULL) {
|
||||
struct cap_hdr *h;
|
||||
for (h = s->proxy->rsp_cap; h; h = h->next) {
|
||||
if (s->rsp_cap[h->index] != NULL)
|
||||
pool_free_to(h->pool, s->rsp_cap[h->index]);
|
||||
}
|
||||
pool_free_to(s->proxy->rsp_cap_pool, s->rsp_cap);
|
||||
}
|
||||
if (s->req_cap != NULL) {
|
||||
struct cap_hdr *h;
|
||||
for (h = s->proxy->req_cap; h; h = h->next) {
|
||||
if (s->req_cap[h->index] != NULL)
|
||||
pool_free_to(h->pool, s->req_cap[h->index]);
|
||||
}
|
||||
pool_free_to(s->proxy->req_cap_pool, s->req_cap);
|
||||
}
|
||||
|
||||
if (s->logs.uri)
|
||||
pool_free(requri, s->logs.uri);
|
||||
if (s->logs.cli_cookie)
|
||||
@ -1997,7 +2114,43 @@ void sess_log(struct session *s) {
|
||||
|
||||
tm = localtime(&s->logs.tv_accept.tv_sec);
|
||||
if (p->to_log & LW_REQ) {
|
||||
send_log(p, LOG_INFO, "%s:%d [%02d/%s/%04d:%02d:%02d:%02d] %s %s %d/%d/%d/%s%d %d %s%lld %s %s %c%c%c%c \"%s\"\n",
|
||||
char tmpline[MAX_SYSLOG_LEN], *h;
|
||||
int hdr;
|
||||
|
||||
h = tmpline;
|
||||
if (p->to_log & LW_REQHDR && (h < tmpline + sizeof(tmpline) - 10)) {
|
||||
*(h++) = ' ';
|
||||
*(h++) = '{';
|
||||
for (hdr = 0; hdr < p->nb_req_cap; hdr++) {
|
||||
if (hdr)
|
||||
*(h++) = '|';
|
||||
if (s->req_cap[hdr] != NULL)
|
||||
h = encode_string(h, tmpline + sizeof(tmpline) - 7, '#', hdr_encode_map, s->req_cap[hdr]);
|
||||
}
|
||||
*(h++) = '}';
|
||||
}
|
||||
|
||||
if (p->to_log & LW_RSPHDR && (h < tmpline + sizeof(tmpline) - 7)) {
|
||||
*(h++) = ' ';
|
||||
*(h++) = '{';
|
||||
for (hdr = 0; hdr < p->nb_rsp_cap; hdr++) {
|
||||
if (hdr)
|
||||
*(h++) = '|';
|
||||
if (s->rsp_cap[hdr] != NULL)
|
||||
h = encode_string(h, tmpline + sizeof(tmpline) - 4, '#', hdr_encode_map, s->rsp_cap[hdr]);
|
||||
}
|
||||
*(h++) = '}';
|
||||
}
|
||||
|
||||
if (h < tmpline + sizeof(tmpline) - 4) {
|
||||
*(h++) = ' ';
|
||||
*(h++) = '"';
|
||||
h = encode_string(h, tmpline + sizeof(tmpline) - 1, '#', url_encode_map, uri);
|
||||
*(h++) = '"';
|
||||
}
|
||||
*h = '\0';
|
||||
|
||||
send_log(p, LOG_INFO, "%s:%d [%02d/%s/%04d:%02d:%02d:%02d] %s %s %d/%d/%d/%s%d %d %s%lld %s %s %c%c%c%c%s\n",
|
||||
pn,
|
||||
(s->cli_addr.ss_family == AF_INET) ?
|
||||
ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port) :
|
||||
@ -2017,7 +2170,7 @@ void sess_log(struct session *s) {
|
||||
sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT],
|
||||
(p->options & PR_O_COOK_ANY) ? sess_cookie[(s->flags & SN_CK_MASK) >> SN_CK_SHIFT] : '-',
|
||||
(p->options & PR_O_COOK_ANY) ? sess_set_cookie[(s->flags & SN_SCK_MASK) >> SN_SCK_SHIFT] : '-',
|
||||
uri);
|
||||
tmpline);
|
||||
}
|
||||
else {
|
||||
send_log(p, LOG_INFO, "%s:%d [%02d/%s/%04d:%02d:%02d:%02d] %s %s %d/%s%d %s%lld %c%c\n",
|
||||
@ -2125,6 +2278,36 @@ int event_accept(int fd) {
|
||||
|
||||
s->uniq_id = totalconn;
|
||||
|
||||
if (p->nb_req_cap > 0) {
|
||||
if ((s->req_cap =
|
||||
pool_alloc_from(p->req_cap_pool, p->nb_req_cap*sizeof(char *)))
|
||||
== NULL) { /* no memory */
|
||||
close(cfd); /* nothing can be done for this fd without memory */
|
||||
pool_free(task, t);
|
||||
pool_free(session, s);
|
||||
return 0;
|
||||
}
|
||||
memset(s->req_cap, 0, p->nb_req_cap*sizeof(char *));
|
||||
}
|
||||
else
|
||||
s->req_cap = NULL;
|
||||
|
||||
if (p->nb_rsp_cap > 0) {
|
||||
if ((s->rsp_cap =
|
||||
pool_alloc_from(p->rsp_cap_pool, p->nb_rsp_cap*sizeof(char *)))
|
||||
== NULL) { /* no memory */
|
||||
if (s->req_cap != NULL)
|
||||
pool_free_to(p->req_cap_pool, s->req_cap);
|
||||
close(cfd); /* nothing can be done for this fd without memory */
|
||||
pool_free(task, t);
|
||||
pool_free(session, s);
|
||||
return 0;
|
||||
}
|
||||
memset(s->rsp_cap, 0, p->nb_rsp_cap*sizeof(char *));
|
||||
}
|
||||
else
|
||||
s->rsp_cap = NULL;
|
||||
|
||||
if ((p->mode == PR_MODE_TCP || p->mode == PR_MODE_HTTP)
|
||||
&& (p->logfac1 >= 0 || p->logfac2 >= 0)) {
|
||||
struct sockaddr_storage sockname;
|
||||
@ -2201,11 +2384,16 @@ int event_accept(int fd) {
|
||||
}
|
||||
|
||||
if ((s->req = pool_alloc(buffer)) == NULL) { /* no memory */
|
||||
if (s->rsp_cap != NULL)
|
||||
pool_free_to(p->rsp_cap_pool, s->rsp_cap);
|
||||
if (s->req_cap != NULL)
|
||||
pool_free_to(p->req_cap_pool, s->req_cap);
|
||||
close(cfd); /* nothing can be done for this fd without memory */
|
||||
pool_free(task, t);
|
||||
pool_free(session, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s->req->l = 0;
|
||||
s->req->total = 0;
|
||||
s->req->h = s->req->r = s->req->lr = s->req->w = s->req->data; /* r and w will be reset further */
|
||||
@ -2215,6 +2403,10 @@ int event_accept(int fd) {
|
||||
|
||||
if ((s->rep = pool_alloc(buffer)) == NULL) { /* no memory */
|
||||
pool_free(buffer, s->req);
|
||||
if (s->rsp_cap != NULL)
|
||||
pool_free_to(p->rsp_cap_pool, s->rsp_cap);
|
||||
if (s->req_cap != NULL)
|
||||
pool_free_to(p->req_cap_pool, s->req_cap);
|
||||
close(cfd); /* nothing can be done for this fd without memory */
|
||||
pool_free(task, t);
|
||||
pool_free(session, s);
|
||||
@ -2609,6 +2801,27 @@ int process_cli(struct session *t) {
|
||||
if (!(t->logs.logwait &= ~LW_REQ))
|
||||
sess_log(t);
|
||||
}
|
||||
else if (t->logs.logwait & LW_REQHDR) {
|
||||
struct cap_hdr *h;
|
||||
int len;
|
||||
for (h = t->proxy->req_cap; h; h = h->next) {
|
||||
if ((h->namelen + 2 <= ptr - req->h) &&
|
||||
(req->h[h->namelen] == ':') &&
|
||||
(strncasecmp(req->h, h->name, h->namelen) == 0)) {
|
||||
|
||||
if (t->req_cap[h->index] == NULL)
|
||||
t->req_cap[h->index] = pool_alloc_from(h->pool, h->len + 1);
|
||||
|
||||
len = ptr - (req->h + h->namelen + 2);
|
||||
if (len > h->len)
|
||||
len = h->len;
|
||||
|
||||
memcpy(t->req_cap[h->index], req->h + h->namelen + 2, len);
|
||||
t->req_cap[h->index][len]=0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
delete_header = 0;
|
||||
|
||||
@ -3234,7 +3447,7 @@ int process_srv(struct session *t) {
|
||||
|
||||
/* if the user wants to log as soon as possible, without counting
|
||||
bytes from the server, then this is the right moment. */
|
||||
if (!(t->logs.logwait & LW_BYTES)) {
|
||||
if (t->proxy->to_log && !(t->logs.logwait & LW_BYTES)) {
|
||||
t->logs.t_close = t->logs.t_connect; /* to get a valid end date */
|
||||
sess_log(t);
|
||||
}
|
||||
@ -3349,7 +3562,7 @@ int process_srv(struct session *t) {
|
||||
|
||||
/* if the user wants to log as soon as possible, without counting
|
||||
bytes from the server, then this is the right moment. */
|
||||
if (!(t->logs.logwait & LW_BYTES)) {
|
||||
if (t->proxy->to_log && !(t->logs.logwait & LW_BYTES)) {
|
||||
t->logs.t_close = t->logs.t_data; /* to get a valid end date */
|
||||
t->logs.bytes = rep->h - rep->data;
|
||||
sess_log(t);
|
||||
@ -3413,6 +3626,27 @@ int process_srv(struct session *t) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (t->logs.logwait & LW_RSPHDR) {
|
||||
struct cap_hdr *h;
|
||||
int len;
|
||||
for (h = t->proxy->rsp_cap; h; h = h->next) {
|
||||
if ((h->namelen + 2 <= ptr - rep->h) &&
|
||||
(rep->h[h->namelen] == ':') &&
|
||||
(strncasecmp(rep->h, h->name, h->namelen) == 0)) {
|
||||
|
||||
if (t->rsp_cap[h->index] == NULL)
|
||||
t->rsp_cap[h->index] = pool_alloc_from(h->pool, h->len + 1);
|
||||
|
||||
len = ptr - (rep->h + h->namelen + 2);
|
||||
if (len > h->len)
|
||||
len = h->len;
|
||||
|
||||
memcpy(t->rsp_cap[h->index], rep->h + h->namelen + 2, len);
|
||||
t->rsp_cap[h->index][len]=0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
delete_header = 0;
|
||||
|
||||
@ -4730,7 +4964,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
|
||||
curproxy->logfac2 = defproxy.logfac2;
|
||||
curproxy->logsrv2 = defproxy.logsrv2;
|
||||
curproxy->loglev2 = defproxy.loglev2;
|
||||
curproxy->to_log = defproxy.to_log;
|
||||
curproxy->to_log = defproxy.to_log & ~LW_COOKIE & ~LW_REQHDR & ~ LW_RSPHDR;
|
||||
curproxy->grace = defproxy.grace;
|
||||
curproxy->source_addr = defproxy.source_addr;
|
||||
return 0;
|
||||
@ -4838,32 +5072,85 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(args[0], "capture")) { /* name of a cookie to capture */
|
||||
// if (curproxy == &defproxy) {
|
||||
// Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
|
||||
// return -1;
|
||||
// }
|
||||
else if (!strcmp(args[0], "capture")) {
|
||||
if (!strcmp(args[1], "cookie")) { /* name of a cookie to capture */
|
||||
// if (curproxy == &defproxy) {
|
||||
// Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
if (curproxy->capture_name != NULL) {
|
||||
// Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n",
|
||||
// file, linenum, args[0]);
|
||||
// return 0;
|
||||
free(curproxy->capture_name);
|
||||
}
|
||||
if (curproxy->capture_name != NULL) {
|
||||
// Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n",
|
||||
// file, linenum, args[0]);
|
||||
// return 0;
|
||||
free(curproxy->capture_name);
|
||||
}
|
||||
|
||||
if (*(args[4]) == 0) {
|
||||
Alert("parsing [%s:%d] : '%s' expects 'cookie' <cookie_name> 'len' <len>.\n",
|
||||
if (*(args[4]) == 0) {
|
||||
Alert("parsing [%s:%d] : '%s' expects 'cookie' <cookie_name> 'len' <len>.\n",
|
||||
file, linenum, args[0]);
|
||||
return -1;
|
||||
}
|
||||
curproxy->capture_name = strdup(args[2]);
|
||||
curproxy->capture_namelen = strlen(curproxy->capture_name);
|
||||
curproxy->capture_len = atol(args[4]);
|
||||
if (curproxy->capture_len >= CAPTURE_LEN) {
|
||||
Warning("parsing [%s:%d] : truncating capture length to %d bytes.\n",
|
||||
file, linenum, CAPTURE_LEN - 1);
|
||||
curproxy->capture_len = CAPTURE_LEN - 1;
|
||||
}
|
||||
curproxy->to_log |= LW_COOKIE;
|
||||
}
|
||||
else if (!strcmp(args[1], "request") && !strcmp(args[2], "header")) {
|
||||
struct cap_hdr *hdr;
|
||||
|
||||
if (curproxy == &defproxy) {
|
||||
Alert("parsing [%s:%d] : '%s %s' not allowed in 'defaults' section.\n", file, linenum, args[0], args[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*(args[3]) == 0 || strcmp(args[4], "len") != 0 || *(args[5]) == 0) {
|
||||
Alert("parsing [%s:%d] : '%s %s' expects 'header' <header_name> 'len' <len>.\n",
|
||||
file, linenum, args[0], args[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr = calloc(sizeof(struct cap_hdr), 1);
|
||||
hdr->next = curproxy->req_cap;
|
||||
hdr->name = strdup(args[3]);
|
||||
hdr->namelen = strlen(args[3]);
|
||||
hdr->len = atol(args[5]);
|
||||
hdr->index = curproxy->nb_req_cap++;
|
||||
curproxy->req_cap = hdr;
|
||||
curproxy->to_log |= LW_REQHDR;
|
||||
}
|
||||
else if (!strcmp(args[1], "response") && !strcmp(args[2], "header")) {
|
||||
struct cap_hdr *hdr;
|
||||
|
||||
if (curproxy == &defproxy) {
|
||||
Alert("parsing [%s:%d] : '%s %s' not allowed in 'defaults' section.\n", file, linenum, args[0], args[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*(args[3]) == 0 || strcmp(args[4], "len") != 0 || *(args[5]) == 0) {
|
||||
Alert("parsing [%s:%d] : '%s %s' expects 'header' <header_name> 'len' <len>.\n",
|
||||
file, linenum, args[0], args[1]);
|
||||
return -1;
|
||||
}
|
||||
hdr = calloc(sizeof(struct cap_hdr), 1);
|
||||
hdr->next = curproxy->rsp_cap;
|
||||
hdr->name = strdup(args[3]);
|
||||
hdr->namelen = strlen(args[3]);
|
||||
hdr->len = atol(args[5]);
|
||||
hdr->index = curproxy->nb_rsp_cap++;
|
||||
curproxy->rsp_cap = hdr;
|
||||
curproxy->to_log |= LW_RSPHDR;
|
||||
}
|
||||
else {
|
||||
Alert("parsing [%s:%d] : '%s' expects 'cookie' or 'request header' or 'response header'.\n",
|
||||
file, linenum, args[0]);
|
||||
return -1;
|
||||
}
|
||||
curproxy->capture_name = strdup(args[2]);
|
||||
curproxy->capture_namelen = strlen(curproxy->capture_name);
|
||||
curproxy->capture_len = atol(args[4]);
|
||||
if (curproxy->capture_len >= CAPTURE_LEN) {
|
||||
Warning("parsing [%s:%d] : truncating capture length to %d bytes.\n",
|
||||
file, linenum, CAPTURE_LEN - 1);
|
||||
curproxy->capture_len = CAPTURE_LEN - 1;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(args[0], "contimeout")) { /* connect timeout */
|
||||
if (curproxy->contimeout != defproxy.contimeout) {
|
||||
@ -5446,25 +5733,25 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
curproxy->req_add[curproxy->nb_reqadd++] = strdup(args[1]);
|
||||
curproxy->req_add[curproxy->nb_reqadd++] = strdup(args[1]);
|
||||
}
|
||||
else if (!strcmp(args[0], "srvexp") || !strcmp(args[0], "rsprep")) { /* replace response header from a regex */
|
||||
regex_t *preg;
|
||||
|
||||
if (*(args[1]) == 0 || *(args[2]) == 0) {
|
||||
Alert("parsing [%s:%d] : '%s' expects <search> and <replace> as arguments.\n",
|
||||
file, linenum, args[0]);
|
||||
return -1;
|
||||
}
|
||||
else if (!strcmp(args[0], "srvexp") || !strcmp(args[0], "rsprep")) { /* replace response header from a regex */
|
||||
regex_t *preg;
|
||||
|
||||
if (*(args[1]) == 0 || *(args[2]) == 0) {
|
||||
Alert("parsing [%s:%d] : '%s' expects <search> and <replace> as arguments.\n",
|
||||
file, linenum, args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
preg = calloc(1, sizeof(regex_t));
|
||||
if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
|
||||
Alert("parsing [%s:%d] : bad regular expression '%s'.\n", file, linenum, args[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
|
||||
|
||||
preg = calloc(1, sizeof(regex_t));
|
||||
if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
|
||||
Alert("parsing [%s:%d] : bad regular expression '%s'.\n", file, linenum, args[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2]));
|
||||
}
|
||||
else if (!strcmp(args[0], "rspdel")) { /* delete response header from a regex */
|
||||
regex_t *preg;
|
||||
if (curproxy == &defproxy) {
|
||||
@ -5923,6 +6210,34 @@ void init(int argc, char **argv) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* initialize the log header encoding map : '{|}"#' should be encoded with
|
||||
* '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
|
||||
* URL encoding only requires '"', '#' to be encoded as well as non-
|
||||
* printable characters above.
|
||||
*/
|
||||
memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
|
||||
memset(url_encode_map, 0, sizeof(url_encode_map));
|
||||
for (i = 0; i < 32; i++) {
|
||||
FD_SET(i, hdr_encode_map);
|
||||
FD_SET(i, url_encode_map);
|
||||
}
|
||||
for (i = 127; i < 256; i++) {
|
||||
FD_SET(i, hdr_encode_map);
|
||||
FD_SET(i, url_encode_map);
|
||||
}
|
||||
|
||||
tmp = "\"#{|}";
|
||||
while (*tmp) {
|
||||
FD_SET(*tmp, hdr_encode_map);
|
||||
tmp++;
|
||||
}
|
||||
|
||||
tmp = "\"#";
|
||||
while (*tmp) {
|
||||
FD_SET(*tmp, url_encode_map);
|
||||
tmp++;
|
||||
}
|
||||
|
||||
pid = getpid();
|
||||
progname = *argv;
|
||||
while ((tmp = strchr(progname, '/')) != NULL)
|
||||
|
Loading…
Reference in New Issue
Block a user