* released 1.1.8

* option "dontlognull"
* fixed "double space" bug in config parser
* fixed an uninitialized server field in case of dispatch
  with no existing server which could cause a segfault during
  logging.
* the pid logged was always the father's, which was wrong for daemons.
* fixed wrong level "LOG_INFO" for message "proxy started".
* http logging is now complete :
  - ip:port, date, proxy, server
  - req_time, conn_time, hdr_time, tot_time
  - status, size, request
* source address binding
This commit is contained in:
willy tarreau 2005-12-17 13:08:06 +01:00
parent 9fe663a113
commit a159808bf2
6 changed files with 301 additions and 92 deletions

View File

@ -32,8 +32,8 @@ LIBS=$(LIBS.$(TARGET))
# - use -DSTATTIME=0 to disable statistics, else specify an interval in
# milliseconds.
# - use -DTRANSPARENT to compile with transparent proxy support.
CFLAGS = -Wall $(COPTS) $(DEBUG) -DSTATTIME=0 -DTRANSPARENT
# - use -DTPROXY to compile with transparent proxy support.
CFLAGS = -Wall $(COPTS) $(DEBUG) -DSTATTIME=0 -DTPROXY
LDFLAGS = -g
all: haproxy

14
NOTES
View File

@ -9,7 +9,19 @@
* add x-forwarded-for
* log http requests on demand, and destination server name
1.1.7 -> 1.1.8
- log destination server IP
* full HTTP log with destination server ID, req and resp time.
* source address of outgoing connections
1.1.8 -> 1.1.9
- handle parametrable HTTP health-checks replies
- differentiate http headers and http uris
- support environment variables in config file
- support keep-alive
--- Notes about cookie usage ---
Cookie insertion is done at the end of server response.
Cookie matching is done after header replacement, but before
header deletion. This means that it's perfectly possible to
delete an inserted cookie once it has been matched, so that
the server never knows about it.

9
TODO
View File

@ -2,20 +2,21 @@
* implémenter l'option "log global" au niveau proxy pour utiliser les logs
globaux.
* matching case-insensitive
* implémenter outgoing addr
* loguer t_cnx, t_data, t_total
+ factoriser la fonction de log (send_log = send_syslog+warning+alert)
+ désactivation du keep-alive (suppression des ^Connection: et ajout des Connection: close)
-> 4 lignes (2 del, 2 add) suffisent.
- loguer t_cnx, t_data, t_total
+ ne pas loguer certaines adresses IP sources
-> pour les health-checks uniquement -> pas de log pour les requêtes
vides (option dontlognull)
- mesurer le tps consommé entre deux select, et fournir la conso CPU :
%cpu = 100 * (tpreselect(n+1)-tpostselect(n)) / (tpreselect(n+1)-tpreselect(n))
- implémenter limitation fd dans la conf : setrlimit(RLIMIT_NOFILE, ...)
- implémenter core/no-core dans la conf : setrlimit(RLIMIT_CORE, ...)
- implémenter outgoing addr
- optimiser les regex pour accélérer les matches :
- compter les matches
- si match(n) & ([n].cpt > [n-1].cpt) & ([n].action == [n-1].action), swap(n,n-1)
- régulièrement, diviser tous les compteurs (lors d'un dépassement par exemple)
- filtrage sur l'adresse IP source
- ne pas loguer certaines adresses IP sources
- gestion keep-alive

View File

@ -1,9 +1,9 @@
H A - P r o x y
---------------
version 1.1.5
version 1.1.8
willy tarreau
2002/04/03
2002/04/16
================
| Introduction |
@ -12,15 +12,17 @@
HA-Proxy est un relais TCP/HTTP offrant des facilités d'intégration en
environnement hautement disponible. En effet, il est capable de :
- assurer un aiguillage statique défini par des cookies ;
- assurer une répartition de charge avec création de cookies pour
assurer la persistence de session ;
- assurer une répartition de charge avec création de cookies pour assurer la
persistence de session ;
- fournir une visibilité externe de son état de santé ;
- s'arrêter en douceur sans perte brutale de service.
- modifier/ajouter/supprimer des entêtes dans la requête et la réponse.
- interdire des requêtes qui vérifient certaines conditions.
Il requiert peu de ressources, et son architecture événementielle mono-processus
lui permet facilement de gérer plusieurs milliers de connexions simultanées sur
plusieurs relais sans effondrer le système.
Il requiert peu de ressources, et son architecture événementielle
mono-processus lui permet facilement de gérer plusieurs milliers de
connexions simultanées sur plusieurs relais sans effondrer le système.
===========================
| Paramètres de lancement |
@ -36,25 +38,97 @@ Les options de lancement sont peu nombreuses :
-s affiche les statistiques (si option compilée)
-l ajoute des informations aux statistiques
Le nombre maximal de connexion simultanées par proxy est le paramètre
par défaut pour les proxies pour lesquels ce paramètre n'est pas
précisé dans le fichier de configuration.
Le nombre maximal de connexion simultanées par proxy est le paramètre par défaut
pour les proxies pour lesquels ce paramètre n'est pas précisé dans le fichier de
configuration. Il s'agit du paramètre 'maxconn' dans les sections 'listen'.
Le nombre maximal total de connexions simultanées limite le nombre de connexions
TCP utilisables à un instant par le processus, tous proxies confondus. Ce
paramètre remplace le paramètre 'maxconn' de la section 'global'.
Le nombre maximal total de connexions simultanées limite le nombre de
connexions TCP utilisables à un instant par le processus, tous proxies
confondus.
============================
| Fichier de configuration |
============================
Commentaires
============
L'analyseur du fichier de configuration ignore des lignes vides, les
espaces, les tabulations, et tout ce qui est compris entre le symbole
'#' (s'il n'est pas précédé d'un '\'), et la fin de la ligne.
L'analyseur du fichier de configuration ignore des lignes vides, les espaces,
les tabulations, et tout ce qui est compris entre le symbole '#' (s'il n'est pas
précédé d'un '\'), et la fin de la ligne.
Le fichier de configuration est découpé en sections répérées par des mots clés
tels que :
- 'global'
- 'listen'
Tous les paramètres font référence à la section définie par le dernier mot clé
reconnu.
1) Paramètres généraux
======================
Il s'agit des paramètres agissant sur le processus, ou bien sur l'ensemble des
proxies. Ils sont tous spécifiés dans la section 'global'. Les paramètres
supportés sont :
- log <adresse> <catégorie>
- maxconn <nombre>
- uid <identifiant>
- gid <identifiant>
- chroot <répertoire>
- nbproc <nombre>
- daemon
- debug
- quiet
1.1) Journalisation des événements
----------------------------------
La plupart des événements sont journalisés : démarrages, arrêts, disparition et
apparition de serveurs, connexions, erreurs. Tous les messages sont envoyés en
syslog vers un ou deux serveurs. La syntaxe est la suivante :
log <adresse_ip> <facility>
Les connexions sont envoyées en niveau "info". Les démarrages de service et de
serveurs seront envoyés en "notice", les signaux d'arrêts en "warning" et les
arrêts définitifs de services et de serveurs en "alert". Ceci est valable aussi
bien pour les proxies que pour les serveurs testés au sein des proxies.
Les catégories possibles sont :
kern, user, mail, daemon, auth, syslog, lpr, news,
uucp, cron, auth2, ftp, ntp, audit, alert, cron2,
local0, local1, local2, local3, local4, local5, local6, local7
Exemple :
---------
global
log 192.168.2.200 local3
log 192.168.2.201 local4
1.2) limitation du nombre de connexions
---------------------------------------
Il est possible et conseillé de limiter le nombre global de connexions par
processus. Les connexions sont comprises au sens 'acceptation de connexion',
donc il faut s'attendre en règle général à avoir un peu plus du double de
sessions TCP que le maximum de connexions fixé. C'est important pour fixer le
paramètre 'ulimit -n' avant de lancer le proxy. Pour comptabiliser le nombre
de sockets nécessaires, il faut prendre en compte ces paramètres :
- 1 socket par connexion entrante
- 1 socket par connexion sortante
- 1 socket par proxy
- 1 socket pour chaque serveur en cours de health-check
- 1 socket pour les logs
Positionner la limite du nombre de descripteurs de fichiers (ulimit -n) à
2 * maxconn + nbproxy + nbserveurs + 1.
1.3) Changement d'uid et de gid
-------------------------------
Serveur

View File

@ -1,20 +1,21 @@
global
log 127.0.0.1 local0
# log 127.0.0.1 local0
# log 127.0.0.1 local1
maxconn 4000
uid 0
gid 0
# chroot /tmp
# nbproc 4
# nbproc 2
# daemon
# debug
# quiet
listen proxy1 0.0.0.0:8000
mode http
# source 127.0.0.2:0
# log 127.0.0.1 local0
# log 127.0.0.1 local1
log global
# log global
#mode tcp
cookie SERVERID insert indirect
balance roundrobin
@ -22,8 +23,13 @@ listen proxy1 0.0.0.0:8000
#dispatch 127.0.0.1:31300
#dispatch 127.0.0.1:80
#dispatch 127.0.0.1:22
#server nc 127.0.0.1:8080 cookie cookie1 check
server tuxlocal 127.0.0.1:80 cookie cookie1 check
server nc 127.0.0.1:8080 cookie cookie1 check
# server tuxlocal0 10.101.23.9:80 cookie cookie1 check
# server tuxlocal1 127.0.0.1:80 cookie cookie1 check
# server tuxlocal2 127.0.0.1:80 cookie cookie2 check
# server tuxlocal3 127.0.0.1:80 cookie cookie3 check
# server tuxlocal4 127.0.0.1:80 cookie cookie4 check
# server vax 10.101.14.1:80 cookie cookie1 check
#server tuxceleron 10.101.0.1:80 cookie cookie2 check
#server telnet 127.0.0.1:23
#server ssh 127.0.0.1:22
@ -47,13 +53,19 @@ listen proxy1 0.0.0.0:8000
#rsprep ^(Date:\ )([^,]*)(,\ )(.*) LaDate\ est:\ \4\ (\2)
# force connection:close
#reqidel ^Connection:
#rspidel ^Connection:
#rspidel ^Connection:
#reqadd Connection:\ close
#rspadd Connection:\ close
# processing options
#option keepalive
option forwardfor
option httplog
option dontlognull
# reqirep ^(Test:\ ) \0_toto_\1_toto
# reqidel ^Cookie:\ SERVERID=
# reqirep ^(GET|POST)\ .* \0
# reqirep ^(Host:|Connection:|User-agent:|Cookie:)\ .* \0
# reqideny ^
listen proxy1 0.0.0.0:8001
mode http

234
haproxy.c
View File

@ -8,11 +8,28 @@
* 2 of the License, or (at your option) any later version.
*
* Pending bugs :
* - solaris only : sometimes, an HTTP proxy with only a dispatch address causes
* the proxy to terminate (no core) if the client breaks the connection during
* the response. Seen on 1.1.8pre4, but never reproduced.
* - cookie in insert+indirect mode sometimes segfaults !
* - a proxy with an invalid config will prevent the startup even if disabled.
*
* ChangeLog :
*
* 2002/04/18 : 1.1.8
* - option "dontlognull"
* - fixed "double space" bug in config parser
* - fixed an uninitialized server field in case of dispatch
* with no existing server which could cause a segfault during
* logging.
* - the pid logged was always the father's, which was wrong for daemons.
* - fixed wrong level "LOG_INFO" for message "proxy started".
* 2002/04/13 :
* - http logging is now complete :
* - ip:port, date, proxy, server
* - req_time, conn_time, hdr_time, tot_time
* - status, size, request
* - source address
* 2002/04/12 : 1.1.7
* - added option forwardfor
* - added reqirep, reqidel, reqiallow, reqideny, rspirep, rspidel
@ -113,12 +130,12 @@
#include <time.h>
#include <regex.h>
#include <syslog.h>
#if defined(TRANSPARENT) && defined(NETFILTER)
#if defined(TPROXY) && defined(NETFILTER)
#include <linux/netfilter_ipv4.h>
#endif
#define HAPROXY_VERSION "1.1.7"
#define HAPROXY_DATE "2002/04/12"
#define HAPROXY_VERSION "1.1.8"
#define HAPROXY_DATE "2002/04/18"
/* this is for libc5 for example */
#ifndef TCP_NODELAY
@ -283,7 +300,8 @@ int strlcpy(char *dst, const char *src, int size) {
#define PR_O_BALANCE (PR_O_BALANCE_RR)
#define PR_O_KEEPALIVE 64 /* follow keep-alive sessions */
#define PR_O_FWDFOR 128 /* insert x-forwarded-for with client address */
#define PR_O_LOGHTTP 256 /* generate a full HTTP log */
#define PR_O_BIND_SRC 256 /* bind to a specific source address when connect()ing */
#define PR_O_NULLNOLOG 512 /* a connect without request will not be logged */
/* various session flags */
@ -336,7 +354,7 @@ int strlcpy(char *dst, const char *src, int size) {
#define CFG_GLOBAL 1
#define CFG_LISTEN 2
/* fields that need to be logged. They appear as flags in session->logwait */
/* fields that need to be logged. They appear as flags in session->logs.logwait */
#define LW_DATE 1 /* date */
#define LW_CLIP 2 /* CLient IP */
#define LW_SVIP 4 /* SerVer IP */
@ -345,6 +363,7 @@ int strlcpy(char *dst, const char *src, int size) {
#define LW_RESP 32 /* http RESPonse */
#define LW_PXIP 64 /* proxy IP */
#define LW_PXID 128 /* proxy ID */
#define LW_BYTES 256 /* bytes read from server */
/*********************************************************************/
@ -363,6 +382,7 @@ struct buffer {
unsigned int l; /* data length */
char *r, *w, *h, *lr; /* read ptr, write ptr, last header ptr, last read */
char *rlim; /* read limit, used for header rewriting */
unsigned long long total; /* total data read */
char data[BUFSIZE];
};
@ -409,13 +429,22 @@ struct session {
int srv_state; /* state of the server side */
int conn_retries; /* number of connect retries left */
int flags; /* some flags describing the session */
int logwait; /* log things waiting to be collected : LW_* */
struct buffer *req; /* request buffer */
struct buffer *rep; /* response buffer */
struct sockaddr_in cli_addr; /* the client address */
struct sockaddr_in srv_addr; /* the address to connect to */
struct server *srv; /* the server being used */
char *requri; /* first line if log needed, NULL otherwise */
struct {
int logwait; /* log fields waiting to be collected : LW_* */
struct timeval tv_accept; /* date of the accept() (beginning of the session) */
long t_request; /* delay before the end of the request arrives, -1 if never occurs */
long t_connect; /* delay before the connect() to the server succeeds, -1 if never occurs */
long t_data; /* delay before the first data byte from the server ... */
unsigned long t_close; /* total session duration */
char *uri; /* first line if log needed, NULL otherwise */
int status; /* HTTP status from the server, negative if from proxy */
long long bytes; /* number of bytes transferred from the server */
} logs;
};
struct proxy {
@ -435,6 +464,7 @@ struct proxy {
int conn_retries; /* maximum number of connect retries */
int options; /* PR_O_REDISP, PR_O_TRANSP */
int mode; /* mode = PR_MODE_TCP, PR_MODE_HTTP or PR_MODE_HEALTH */
struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */
struct proxy *next;
struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */
char logfac1, logfac2; /* log facility for both servers. -1 = disabled */
@ -539,13 +569,6 @@ const char *monthname[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
#define MAX_HOSTNAME_LEN 32
static char hostname[MAX_HOSTNAME_LEN] = "";
const char *HTTP_403 =
"HTTP/1.0 403 Forbidden\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"\r\n"
"403 Forbidden : Request forbidden by administrative rules.\r\n";
const char *HTTP_400 =
"HTTP/1.0 400 Bad request\r\n"
"Cache-Control: no-cache\r\n"
@ -553,6 +576,13 @@ const char *HTTP_400 =
"\r\n"
"400 Bad request : Your browser sent an invalid request.\r\n";
const char *HTTP_403 =
"HTTP/1.0 403 Forbidden\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"\r\n"
"403 Forbidden : Request forbidden by administrative rules.\r\n";
const char *HTTP_502 =
"HTTP/1.0 502 Proxy Error\r\n"
"Cache-Control: no-cache\r\n"
@ -629,7 +659,7 @@ void Alert(char *fmt, ...) {
gettimeofday(&tv, NULL);
tm=localtime(&tv.tv_sec);
fprintf(stderr, "[ALERT] %03d/%02d%02d%02d (%d) : ",
tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, getpid());
tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)getpid());
vfprintf(stderr, fmt, argp);
fflush(stderr);
va_end(argp);
@ -651,7 +681,7 @@ void Warning(char *fmt, ...) {
gettimeofday(&tv, NULL);
tm=localtime(&tv.tv_sec);
fprintf(stderr, "[WARNING] %03d/%02d%02d%02d (%d) : ",
tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, getpid());
tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)getpid());
vfprintf(stderr, fmt, argp);
fflush(stderr);
va_end(argp);
@ -683,7 +713,7 @@ struct sockaddr_in *str2sa(char *str) {
char *c;
int port;
bzero(&sa, sizeof(sa));
memset(&sa, 0, sizeof(sa));
str=strdup(str);
if ((c=strrchr(str,':')) != NULL) {
@ -1226,10 +1256,10 @@ static int maintain_proxies(void);
* inspired from Patrick Schaaf's example of nf_getsockname() implementation.
*/
static int get_original_dst(int fd, struct sockaddr_in *sa, int *salen) {
#if defined(TRANSPARENT) && defined(SO_ORIGINAL_DST)
#if defined(TPROXY) && defined(SO_ORIGINAL_DST)
return getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, (void *)sa, salen);
#else
#if defined(TRANSPARENT) && defined(USE_GETSOCKNAME)
#if defined(TPROXY) && defined(USE_GETSOCKNAME)
return getsockname(fd, (struct sockaddr *)sa, salen);
#else
return -1;
@ -1245,8 +1275,8 @@ static inline void session_free(struct session *s) {
pool_free(buffer, s->req);
if (s->rep)
pool_free(buffer, s->rep);
if (s->requri)
pool_free(requri, s->requri);
if (s->logs.uri)
pool_free(requri, s->logs.uri);
pool_free(session, s);
}
@ -1288,7 +1318,7 @@ int connect_server(struct session *s) {
else /* unknown balancing algorithm */
return -1;
}
else if (*(int *)&s->proxy->dispatch_addr) {
else if (*(int *)&s->proxy->dispatch_addr.sin_addr) {
/* connect to the defined dispatch addr */
s->srv_addr = s->proxy->dispatch_addr;
}
@ -1319,6 +1349,14 @@ int connect_server(struct session *s) {
return -1;
}
/* allow specific binding */
if (s->proxy->options & PR_O_BIND_SRC &&
bind(fd, (struct sockaddr *)&s->proxy->source_addr, sizeof(s->proxy->source_addr)) == -1) {
Alert("Cannot bind to source address before connect() for proxy %s. Aborting.\n", s->proxy->id);
close(fd);
return -1;
}
if ((connect(fd, (struct sockaddr *)&s->srv_addr, sizeof(s->srv_addr)) == -1) && (errno != EINPROGRESS)) {
if (errno == EAGAIN) { /* no free ports left, try again later */
qfprintf(stderr,"Cannot connect, no free ports.\n");
@ -1405,6 +1443,8 @@ int event_cli_read(int fd) {
if (b->r == b->data + BUFSIZE) {
b->r = b->data; /* wrap around the buffer */
}
b->total += ret;
/* we hope to read more data or to get a close on next round */
continue;
}
@ -1498,6 +1538,8 @@ int event_srv_read(int fd) {
if (b->r == b->data + BUFSIZE) {
b->r = b->data; /* wrap around the buffer */
}
b->total += ret;
/* we hope to read more data or to get a close on next round */
continue;
}
@ -1746,21 +1788,45 @@ void sess_log(struct session *s) {
* computed.
*/
log = p->to_log & ~s->logwait;
log = p->to_log & ~s->logs.logwait;
pn = (log & LW_CLIP) ?
(unsigned char *)&s->cli_addr.sin_addr :
(unsigned char *)"\0\0\0\0";
uri = (log & LW_REQ) ? s->requri : "<requri>";
uri = (log & LW_REQ) ? s->logs.uri : "<BADREQ>";
pxid = p->id;
//srv = (log & LW_SVID) ? s->srv->id : "<svid>";
srv = ((p->to_log & LW_SVID) && s->srv != NULL) ? s->srv->id : "<svid>";
send_log(p, LOG_INFO, "%d.%d.%d.%d:%d %s %s \"%s\"\n",
pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
pxid, srv, uri);
s->logwait = 0;
srv = ((p->to_log & LW_SVID) && s->srv != NULL) ? s->srv->id : "<NOSRV>";
if (p->to_log & LW_DATE) {
struct tm *tm = localtime(&s->logs.tv_accept.tv_sec);
send_log(p, LOG_INFO, "%d.%d.%d.%d:%d [%02d/%s/%04d:%02d:%02d:%02d] %s %s %d/%d/%d/%d %d %lld \"%s\"\n",
pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
tm->tm_mday, monthname[tm->tm_mon], tm->tm_year+1900,
tm->tm_hour, tm->tm_min, tm->tm_sec,
pxid, srv,
s->logs.t_request,
(s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_request : -1,
(s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
s->logs.t_close,
s->logs.status, s->logs.bytes,
uri);
}
else {
send_log(p, LOG_INFO, "%d.%d.%d.%d:%d %s %s %d/%d/%d/%d %d %lld \"%s\"\n",
pn[0], pn[1], pn[2], pn[3], ntohs(s->cli_addr.sin_port),
pxid, srv,
s->logs.t_request,
(s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_request : -1,
(s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
s->logs.t_close,
s->logs.status, s->logs.bytes,
uri);
}
s->logs.logwait = 0;
}
@ -1833,9 +1899,18 @@ int event_accept(int fd) {
s->res_cr = s->res_cw = s->res_sr = s->res_sw = RES_SILENT;
s->cli_fd = cfd;
s->srv_fd = -1;
s->srv = NULL;
s->conn_retries = p->conn_retries;
s->requri = NULL;
s->logwait = p->to_log;
s->logs.logwait = p->to_log;
s->logs.tv_accept = now;
s->logs.t_request = -1;
s->logs.t_connect = -1;
s->logs.t_data = -1;
s->logs.t_close = 0;
s->logs.uri = NULL;
s->logs.status = -1;
s->logs.bytes = 0;
if ((p->mode == PR_MODE_TCP || p->mode == PR_MODE_HTTP)
&& (p->logfac1 >= 0 || p->logfac2 >= 0)) {
@ -1851,8 +1926,8 @@ int event_accept(int fd) {
if (p->to_log) {
/* we have the client ip */
if (s->logwait & LW_CLIP)
if (!(s->logwait &= ~LW_CLIP))
if (s->logs.logwait & LW_CLIP)
if (!(s->logs.logwait &= ~LW_CLIP))
sess_log(s);
}
else
@ -1875,6 +1950,7 @@ int event_accept(int fd) {
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 */
s->req->rlim = s->req->data + BUFSIZE;
if (s->cli_state == CL_STHEADERS) /* reserver some space for header rewriting */
@ -1888,6 +1964,7 @@ int event_accept(int fd) {
return 0;
}
s->rep->l = 0;
s->rep->total = 0;
s->rep->h = s->rep->r = s->rep->lr = s->rep->w = s->rep->rlim = s->rep->data;
fdtab[cfd].read = &event_cli_read;
@ -2088,6 +2165,7 @@ int process_cli(struct session *t) {
if (t->flags & SN_CLDENY) {
/* no need to go further */
t->logs.status = 403;
client_retnclose(t, strlen(HTTP_403), HTTP_403);
return 1;
}
@ -2109,6 +2187,7 @@ int process_cli(struct session *t) {
t->cli_state = CL_STDATA;
req->rlim = req->data + BUFSIZE; /* no more rewrite needed */
t->logs.t_request = tv_delta(&t->logs.tv_accept, &now);
/* FIXME: we'll set the client in a wait state while we try to
* connect to the server. Is this really needed ? wouldn't it be
* better to release the maximum of system buffers instead ? */
@ -2141,25 +2220,25 @@ int process_cli(struct session *t) {
* req->r = end of data (not used at this stage)
*/
if (t->logwait & LW_REQ &&
t->proxy->mode & PR_MODE_HTTP &&
t->proxy->options & PR_O_LOGHTTP) {
if (t->logs.logwait & LW_REQ &&
t->proxy->mode & PR_MODE_HTTP) {
/* we have a complete HTTP request that we must log */
int urilen;
if ((t->requri = pool_alloc(requri)) == NULL) {
if ((t->logs.uri = pool_alloc(requri)) == NULL) {
Alert("HTTP logging : out of memory.\n");
client_retnclose(t, strlen(HTTP_403), HTTP_403);
t->logs.status = 502;
client_retnclose(t, strlen(HTTP_502), HTTP_502);
return 1;
}
urilen = ptr - req->h;
if (urilen >= REQURI_LEN)
urilen = REQURI_LEN - 1;
memcpy(t->requri, req->h, urilen);
t->requri[urilen] = 0;
memcpy(t->logs.uri, req->h, urilen);
t->logs.uri[urilen] = 0;
if (!(t->logwait &= ~LW_REQ))
if (!(t->logs.logwait &= ~LW_REQ))
sess_log(t);
}
@ -2309,6 +2388,7 @@ int process_cli(struct session *t) {
* won't be able to free more later, so the session will never terminate.
*/
if (req->l >= req->rlim - req->data) {
t->logs.status = 400;
client_retnclose(t, strlen(HTTP_400), HTTP_400);
return 1;
}
@ -2507,6 +2587,7 @@ int process_srv(struct session *t) {
/* if conn_retries < 0 or other error, let's abort */
tv_eternity(&t->cnexpire);
t->srv_state = SV_STCLOSE;
t->logs.status = 502;
client_return(t, strlen(HTTP_502), HTTP_502);
}
}
@ -2539,6 +2620,8 @@ int process_srv(struct session *t) {
return 1;
}
else { /* no error or write 0 */
t->logs.t_connect = tv_delta(&t->logs.tv_accept, &now);
//fprintf(stderr,"3: c=%d, s=%d\n", c, s);
if (req->l == 0) /* nothing to write */
FD_CLR(t->srv_fd, StaticWriteEvent);
@ -2600,6 +2683,7 @@ int process_srv(struct session *t) {
t->srv_state = SV_STDATA;
rep->rlim = rep->data + BUFSIZE; /* no more rewrite needed */
t->logs.t_data = tv_delta(&t->logs.tv_accept, &now);
break;
}
@ -2630,6 +2714,12 @@ int process_srv(struct session *t) {
* rep->r = end of data (not used at this stage)
*/
if (t->logs.logwait & LW_RESP) {
t->logs.logwait &= ~LW_RESP;
t->logs.status = atoi(rep->h + 9);
}
delete_header = 0;
if ((global.mode & MODE_DEBUG) && !(global.mode & MODE_QUIET)) {
@ -2994,8 +3084,12 @@ int process_session(struct task *t) {
write(1, trash, len);
}
s->logs.t_close = tv_delta(&s->logs.tv_accept, &now);
if (s->rep != NULL)
s->logs.bytes = s->rep->total;
/* let's do a final log if we need it */
if (s->logwait)
if (s->logs.logwait && (!(s->proxy->options & PR_O_NULLNOLOG) || s->req->total))
sess_log(s);
/* the task MUST not be in the run queue anymore */
@ -3752,7 +3846,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
if (!strcmp(args[1], "redispatch"))
/* enable reconnections to dispatch */
curproxy->options |= PR_O_REDISP;
#ifdef TRANSPARENT
#ifdef TPROXY
else if (!strcmp(args[1], "transparent"))
/* enable transparent proxy connections */
curproxy->options |= PR_O_TRANSP;
@ -3765,8 +3859,11 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
curproxy->options |= PR_O_FWDFOR;
else if (!strcmp(args[1], "httplog")) {
/* generate a complete HTTP log */
curproxy->options |= PR_O_LOGHTTP;
curproxy->to_log |= LW_DATE | LW_CLIP | LW_SVID | LW_REQ | LW_PXID;
curproxy->to_log |= LW_DATE | LW_CLIP | LW_SVID | LW_REQ | LW_PXID | LW_RESP;
}
else if (!strcmp(args[1], "dontlognull")) {
/* don't log empty requests */
curproxy->options |= PR_O_NULLNOLOG;
}
else {
Alert("parsing [%s:%d] : unknown option <%s>.\n", file, linenum, args[1]);
@ -3778,7 +3875,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
/* enable reconnections to dispatch */
curproxy->options |= PR_O_REDISP;
}
#ifdef TRANSPARENT
#ifdef TPROXY
else if (!strcmp(args[0], "transparent")) {
/* enable transparent proxy connections */
curproxy->options |= PR_O_TRANSP;
@ -3934,6 +4031,16 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
return -1;
}
}
else if (!strcmp(args[0], "source")) { /* address to which we bind when connecting */
if (strchr(args[1], ':') == NULL) {
Alert("parsing [%s:%d] : <source> expects <addr:port> as argument.\n",
file, linenum);
return -1;
}
curproxy->source_addr = *str2sa(args[1]);
curproxy->options |= PR_O_BIND_SRC;
}
else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) { /* replace request header from a regex */
regex_t *preg;
@ -4233,19 +4340,20 @@ int readcfgfile(char *file) {
}
line++;
}
else {
if (*line == '#' || *line == '\n' || *line == '\r')
*line = 0; /* end of string, end of loop */
else
line++;
else if (*line == '#' || *line == '\n' || *line == '\r') {
/* end of string, end of loop */
*line = 0;
break;
}
else if (isspace(*line)) {
/* a non-escaped space is an argument separator */
if (isspace(*line)) {
*line++ = 0;
while (isspace(*line))
line++;
args[++arg] = line;
}
*line++ = 0;
while (isspace(*line))
line++;
args[++arg] = line;
}
else {
line++;
}
}
@ -4299,7 +4407,7 @@ int readcfgfile(char *file) {
}
if ((curproxy->mode != PR_MODE_HEALTH) &&
!(curproxy->options & (PR_O_TRANSP | PR_O_BALANCE)) &&
(*(int *)&curproxy->dispatch_addr == 0)) {
(*(int *)&curproxy->dispatch_addr.sin_addr == 0)) {
Alert("parsing %s : listener %s has no dispatch address and is not in transparent or balance mode.\n",
file, curproxy->id);
cfgerr++;
@ -4315,7 +4423,7 @@ int readcfgfile(char *file) {
file, curproxy->id);
cfgerr++;
}
else if (*(int *)&curproxy->dispatch_addr != 0) {
else if (*(int *)&curproxy->dispatch_addr.sin_addr != 0) {
Warning("parsing %s : dispatch address of listener %s will be ignored in balance mode.\n",
file, curproxy->id);
}
@ -4559,7 +4667,7 @@ int start_proxies() {
// if (curproxy->logfac2 >= 0)
// send_syslog(&curproxy->logsrv2, curproxy->logfac2, LOG_INFO, trash);
send_log(curproxy, LOG_INFO, "Proxy %s started.\n", curproxy->id);
send_log(curproxy, LOG_NOTICE, "Proxy %s started.\n", curproxy->id);
}
return 0;
@ -4627,6 +4735,7 @@ int main(int argc, char **argv) {
if (proc == global.nbproc)
exit(0); /* parent must leave */
pid = getpid(); /* update child's pid */
setpgid(1, 0);
}
@ -4634,3 +4743,4 @@ int main(int argc, char **argv) {
exit(0);
}