* 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:
willy tarreau 2005-12-18 01:00:37 +01:00
parent 982249e9e7
commit 4302f49525
6 changed files with 525 additions and 66 deletions

View File

@ -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
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

409
haproxy.c
View File

@ -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",
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 (*(args[4]) == 0) {
Alert("parsing [%s:%d] : '%s' expects 'cookie' <cookie_name> 'len' <len>.\n",
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)