diff --git a/Makefile b/Makefile index 602b05706..81d1a39b0 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/NOTES b/NOTES index 382018c52..ce994f7ff 100644 --- a/NOTES +++ b/NOTES @@ -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. + diff --git a/TODO b/TODO index 41827a208..d690e70b9 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/doc/haproxy.txt b/doc/haproxy.txt index 71b0cb726..dec7e1e0e 100644 --- a/doc/haproxy.txt +++ b/doc/haproxy.txt @@ -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 + - maxconn + - uid + - gid + - chroot + - nbproc + - 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 + +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 diff --git a/examples/cfg b/examples/cfg index ab63dbae2..96d5ba552 100644 --- a/examples/cfg +++ b/examples/cfg @@ -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 diff --git a/haproxy.c b/haproxy.c index 0d3d9e84d..290432be3 100644 --- a/haproxy.c +++ b/haproxy.c @@ -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 #include #include -#if defined(TRANSPARENT) && defined(NETFILTER) +#if defined(TPROXY) && defined(NETFILTER) #include #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 : ""; + uri = (log & LW_REQ) ? s->logs.uri : ""; pxid = p->id; //srv = (log & LW_SVID) ? s->srv->id : ""; - srv = ((p->to_log & LW_SVID) && s->srv != NULL) ? s->srv->id : ""; - - 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 : ""; + + 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] : expects 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); } +