* released 1.1.22

* 'listen' now supports optionnal address:port-range lists
* 'bind' introduced to add new listen addresses
* fixed a bug which caused a session to be kept established on a server till
  it timed out if the client closed during the DATA phase.
* the port part of each server address can now be empty to make the proxy
  connect to the server on the same port it was connected to, be an absolute
  unsigned number to reflect a single port (as in older versions), or an
  explicitly signed number (+N/-N) to indicate that this offset must be
  applied to the port the proxy was connected to, when connecting to the
  server.
* the 'port' server option allows the user to specify a different
  health-check port than the service one. It is mandatory when only relative
  ports have been specified and check is required. By default, the checks are
  sent to the service port.
* new 'defaults' section which is rather similar to 'listen' except that all
  values are only used as default values for future 'listen' sections, until
  a new 'defaults' resets them. At the moment, server options, regexes,
  cookie names and captures cannot be set in the 'defaults' section.
* Makefile now optimizes for Ultrasparc by default on Solaris/Sparc
* large documentation updates and fixes
* new 'tests' directory with some debug files
This commit is contained in:
willy tarreau 2005-12-17 14:02:24 +01:00
parent 2f6ba6579f
commit a41a8b4ee6
8 changed files with 810 additions and 155 deletions

View File

@ -1,6 +1,26 @@
ChangeLog :
===========
2003/09/10 :
- 'listen' now supports optionnal address:port-range lists
- 'bind' introduced to add new listen addresses
- fixed a bug which caused a session to be kept established on a server till
it timed out if the client closed during the DATA phase.
- the port part of each server address can now be empty to make the proxy
connect to the server on the same port it was connected to, be an absolute
unsigned number to reflect a single port (as in older versions), or an
explicitly signed number (+N/-N) to indicate that this offset must be
applied to the port the proxy was connected to, when connecting to the
server.
- the 'port' server option allows the user to specify a different
health-check port than the service one. It is mandatory when only relative
ports have been specified and check is required. By default, the checks are
sent to the service port.
- new 'defaults' section which is rather similar to 'listen' except that all
values are only used as default values for future 'listen' sections, until
a new 'defaults' resets them. At the moment, server options, regexes,
cookie names and captures cannot be set in the 'defaults' section.
2003/05/06 : 1.1.21
- changed the debug output format so that it now includes the session unique
ID followed by the instance name at the beginning of each line.

View File

@ -31,7 +31,7 @@ COPTS.solaris = -O2 -fomit-frame-pointer -DSOLARIS
LIBS.solaris = -lnsl -lsocket
# This is for Solaris 8 on UltraSparc2 processor
COPTS.solarisv9 = -O6 -mcpu=v9 -fomit-frame-pointer -DSOLARIS
COPTS.solarisv9 = -O6 -mcpu=v9 -mtune=ultrasparc -fomit-frame-pointer -DSOLARIS
LIBS.solarisv9 = -lnsl -lsocket
# This is for OpenBSD 3.0

73
TODO
View File

@ -27,3 +27,76 @@
- support environment variables in config file
- support keep-alive
- support SSL
===================== demandes ==========================
ok> 1) écoute sur une plage de ports :
ok> listen XXX 1.2.3.4:21000-21060
ok>
ok> 2) écoutes multiples :
ok> listen XXX 1.2.3.4:21000
ok> bind 2.3.4.5:21001
ok> bind 2.3.4.5:21000-21060
ok>
ok> => on en arrive à ceci :
ok>
ok> listen XXX [ address:port ]
ok> bind addr:plage-port[,[addr:]plage-port]*
ok> bind ...
ok> ...
ok>
ok> => proxy->listen_fd et proxy->listen_addr doivent être
ok> mis dans des listes
ok> => OK pour listen, implémenter le BIND.
ok>
ok> 3) reconnexion sur le même port sur le serveur :
ok>
ok> server XXX 1.2.3.4[:port]
ok> si <port> n'est pas spécifié, on utilise le même port que celui qui a reçu
ok> la connexion. Dans ce cas, il faut pouvoir forcer le port du health-check
ok> par un nouveau parametre "port".
ok>
ok> => ça permet les forwardings de plages :
ok>
ok> listen XXX
ok> bind 1.2.3.4:10000-11000
ok> server 1.2.3.5
ok>
4) paramètres par défaut :
créer une section "defaults" qui précise les paramètres par défaut pour les
sections suivantes, concernant les paramètres suivants :
ok- les logs
ok- les modes (tcp/http)
ok- le balancing (round-robin/source)
ok- les time-outs
ok- maxconn
ok- redisp
ok- les options
ok- le retry
ok- les checks
- les options des serveurs ?
- les filtres et regex ?
- les cookies/captures
5) implémenter "balance source" pour faire un hash sur la source.
6) possibilité d'un process séparé par listen :
listen XXX
fork [ group_id ]
le fait de spécifier group_id fera que toutes les instances utilisant le même
identifiant de groupe seront gérées par un même processus.
7) gérer un chroot/uid/gid différents par process :
listen XXX
chroot /truc
uid 123
gid 456
8) beaucoup de paramètres pourraient être spécifiques aux serveurs et non
aux instances. Exemples :
- adresse IP source pour atteindre le serveur
- méthode de health-check (proto, port, ...)
- poids
- alerte en cas de disparition
- le nombre max de sessions à lui envoyer

View File

@ -1,9 +1,9 @@
H A - P r o x y
---------------
version 1.1.21
version 1.1.22
willy tarreau
2003/05/06
2003/09/11
================
| Introduction |
@ -73,6 +73,7 @@ tels que :
- 'global'
- 'listen'
- 'defaults'
Tous les paramètres font référence à la section définie par le dernier mot clé
reconnu.
@ -245,27 +246,54 @@ Exemple :
Les sections de service débutent par le mot clé "listen" :
listen <nom_instance> <adresse_IP>:<port>
listen <nom_instance> [ <adresse_IP>:<plage_ports>[,...] ]
- <nom_instance> est le nom de l'instance décrite. Ce nom sera envoyé dans les
logs, donc il est souhaitable d'utiliser un nom relatif au service relayé. Aucun
test n'est effectué concernant l'unicité de ce nom, qui n'est pas obligatoire,
mais fortement recommandée.
mais fortement recommandée.
- <adresse_IP> est l'adresse IP sur laquelle le relais attend ses
connexions. L'adresse 0.0.0.0 signifie que les connexions pourront s'effectuer
sur toutes les adresses de la machine.
- <adresse_IP> est l'adresse IP sur laquelle le relais attend ses connexions.
L'absence d'adresse ainsi que l'adresse 0.0.0.0 signifient que les connexions
pourront s'effectuer sur toutes les adresses de la machine.
- <port> est le numéro de port TCP sur lequel le relais attend ses
connexions. Le couple <adresse_IP>:<port> doit être unique pour toutes les
instances d'une même machine. L'attachement à un port inférieur à 1024
nécessite un niveau de privilège particulier lors du lancement du programme
(indépendamment du paramètre 'uid' de la section 'global').
- <plage_ports> correspond soit à un port, soit à une plage de ports sur
lesquels le relais acceptera des connexions pour l'adresse IP spécifiée.
Cette plage peut être :
- soit un port numérique (ex: '80')
- soit une plage constituée de deux valeurs séparées par un tiret
(ex: '2000-2100') représentant les extrémités incluses dans la
plage.
Il faut faire attention à l'usage des plages, car chaque combinaison
<adresse_IP>:<port> consomme une socket, donc un descripteur de fichier.
Le couple <adresse_IP>:<port> doit être unique pour toutes les instances
d'une même machine. L'attachement à un port inférieur à 1024 nécessite un
niveau de privilège particulier lors du lancement du programme (indépendamment
du paramètre 'uid' de la section 'global').
Exemple :
- le couple <adresse_IP>:<plage_ports> peut être répété indéfiniment pour
demander au relais d'écouter également sur d'autres adresses et/ou d'autres
plages de ports. Pour cela, il suffit de séparer les couples par une virgule.
Exemples :
---------
listen http_proxy 127.0.0.1:80
listen http_proxy :80
listen x11_proxy 127.0.0.1:6000-6009
listen smtp_proxy 127.0.0.1:25,127.0.0.1:587
listen ldap_proxy :389,:663
Si toutes les adresses ne tiennent pas sur une ligne, il est possible d'en
rajouter à l'aide du mot clé 'bind'. Dans ce cas, il n'est même pas nécessaire
de spécifier la première adresse sur la ligne listen, ce qui facilite parfois
l'écriture de configurations :
bind [ <adresse_IP>:<plage_ports>[,...] ]
Exemples :
----------
listen http_proxy
bind :80,:443
bind 10.0.0.1:10080,10.0.0.1:10443
2.1) Inhibition d'un service
----------------------------
@ -417,7 +445,9 @@ Le serveur vers lequel sont redirig
le paramètre "dispatch" sous la forme <adresse_ip>:<port>. Il correspond à un
serveur d'assignation de cookie dans le cas où le service consiste à assurer
uniquement une persistence HTTP, ou bien simplement au serveur destination dans
le cas de relayage TCP simple.
le cas de relayage TCP simple. Cet ancien mode ne permet pas de tester l'état
du serveur distant, et il est maintenant recommandé d'utiliser de préférence
le mode 'balance'.
Exemple :
---------
@ -432,24 +462,24 @@ Ce param
2.8) Adresse de sortie
----------------------
Il est possible de forcer l'adresse utilisée pour établir les connexions
vers les serveurs à l'aide du paramètre "source". Il est même possible de
forcer le port, bien que cette fonctionnalité se limite à des usages très
spécifiques. C'est particulièrement utile en cas d'adressage multiple, et
plus généralement pour permettre aux serveurs de trouver le chemin de
retour dans des contextes de routage difficiles. Si l'adresse est 0.0.0.0,
elle sera choisie librement par le systeme. Si le port est 0, il
sera choisi librement par le système. Il est à noter que depuis la version
1.1.18, les tests de bon foncitonnement des serveurs seront aussi effectués à
partir de la source spécifiée par ce paramètre.
Il est possible de forcer l'adresse utilisée pour établir les connexions vers
les serveurs à l'aide du paramètre "source". Il est même possible de forcer le
port, bien que cette fonctionnalité se limite à des usages très spécifiques.
C'est particulièrement utile en cas d'adressage multiple, et plus généralement
pour permettre aux serveurs de trouver le chemin de retour dans des contextes de
routage difficiles. Si l'adresse est '0.0.0.0' ou '*' ou vide, elle sera choisie
librement par le systeme. Si le port est '0' ou vide, il sera choisi librement
par le système. Il est à noter que depuis la version 1.1.18, les tests de bon
foncitonnement des serveurs seront aussi effectués à partir de la source
spécifiée par ce paramètre.
Exemples :
----------
listen http_proxy 0.0.0.0:80
listen http_proxy *:80
# toutes les connexions prennent l'adresse 192.168.1.200
source 192.168.1.200:0
listen rlogin_proxy 0.0.0.0:513
listen rlogin_proxy *:513
# utiliser l'adresse 192.168.1.200 et le port réservé 900
source 192.168.1.200:900
@ -462,7 +492,7 @@ par le param
Exemple :
---------
listen http_proxy 0.0.0.0:80
listen http_proxy :80
mode http
cookie SERVERID
@ -538,7 +568,7 @@ par le param
Exemple : le cookie SERVERID peut contenir server01 ou server02
---------
listen http_proxy 0.0.0.0:80
listen http_proxy :80
mode http
cookie SERVERID
dispatch 192.168.1.100:80
@ -548,7 +578,7 @@ Exemple : le cookie SERVERID peut contenir server01 ou server02
Attention : la syntaxe a changé depuis la version 1.0.
-----------
3) Répartiteur de charge interne
3) Répartiteur de charge autonome
=================================
Le relais peut effectuer lui-même la répartition de charge entre les différents
@ -562,13 +592,63 @@ sp
Exemple : même que précédemment en répartition interne
---------
listen http_proxy 0.0.0.0:80
listen http_proxy :80
mode http
cookie SERVERID
balance roundrobin
server web1 192.168.1.1:80 cookie server01
server web2 192.168.1.2:80 cookie server02
Depuis la version 1.1.22, il est possible de déterminer automatiquement le port
du serveur vers lequel sera envoyée la connexion, en fonction du port d'écoute
sur lequel le client s'est connecté. En effet, il y a 4 possibilités pour le
champ <port> de l'adresse serveur :
- non spécifié ou nul :
la connexion sera envoyée au serveur sur le même port que celui sur
lequel le relais a reçu la connexion.
- valeur numérique (seul cas supporté pour les versions antérieures) :
le serveur recevra la connexion sur le port désigné.
- valeur numérique précédée d'un signe '+' :
la connexion sera envoyée au serveur sur le même port que celui sur
lequel le relais a reçu la connexion, auquel on ajoute la valeur désignée.
- valeur numérique précédée d'un signe '-' :
la connexion sera envoyée au serveur sur le même port que celui sur
lequel le relais a reçu la connexion, duquel on soustrait la valeur
désignée.
Exemples :
----------
# même que précédemment
listen http_proxy :80
mode http
cookie SERVERID
balance roundrobin
server web1 192.168.1.1 cookie server01
server web2 192.168.1.2 cookie server02
# relayage simultané des ports 80 et 81 et 8080-8089
listen http_proxy :80,:81,:8080-8089
mode http
cookie SERVERID
balance roundrobin
server web1 192.168.1.1 cookie server01
server web2 192.168.1.2 cookie server02
# relayage TCP des ports 25, 389 et 663 vers les ports 1025, 1389 et 1663
listen http_proxy :25,:389,:663
mode tcp
balance roundrobin
server srv1 192.168.1.1:+1000
server srv2 192.168.1.2:+1000
3.1) Surveillance des serveurs
------------------------------
@ -583,6 +663,7 @@ param
- inter : 2000
- rise : 2
- fall : 3
- port : port de connexion du serveur
Le mode par défaut consiste à établir des connexions TCP uniquement. Dans
certains cas de pannes, des serveurs peuvent continuer à accepter les connexions
@ -613,6 +694,12 @@ rencontrent n'y soient pas affect
de cookie envoie un cookie vide, ce qui a pour effet de supprimer un éventuel
cookie affecté précédemment.
Depuis la version 1.1.22, il est possible d'envoyer les tests de fonctionnement
vers un port différent de celui de service. C'est nécessaire principalement
pour les configurations où le serveur n'a pas de port prédéfini, par exemple
lorsqu'il est déduit du port d'acceptation de la connexion. Pour cela, utiliser
le paramètre 'port' suivi du numéro de port devant répondre aux requêtes.
Enfin, depuis la version 1.1.17, il est possible de visualiser rapidement l'état
courant de tous les serveurs. Pour cela, il suffit d'envoyer un signal SIGHUP au
processus proxy. L'état de tous les serveurs de tous les proxies est envoyé dans
@ -622,7 +709,7 @@ niveau notice.
Exemples :
----------
# même que précédemment avec surveillance TCP
# conf du paragraphe 3) avec surveillance TCP
listen http_proxy 0.0.0.0:80
mode http
cookie SERVERID
@ -668,6 +755,14 @@ Exemples :
server web-backup 192.168.1.3:80 cookie server03 check backup
server web-excuse 192.168.1.4:80 check backup
# relayage SMTP+TLS avec test du serveur et serveur de backup
listen http_proxy :25,:587
mode tcp
balance roundrobin
server srv1 192.168.1.1 check port 25 inter 30000 rise 1 fall 2
server srv2 192.168.1.2 backup
3.2) Reconnexion vers un répartiteur en cas d'échec direct
----------------------------------------------------------
@ -731,6 +826,22 @@ Exemple :
# iptables -t nat -A PREROUTING -i eth0 -p tcp -d 192.168.1.100 \
--dport 80 -j REDIRECT --to-ports 65000
Remarque :
----------
Si le port n'est pas spécifié sur le serveur, c'est le port auquel s'est adressé
le client qui sera utilisé. Cela permet de relayer tous les ports TCP d'une même
adresse avec une même instance et sans utiliser directement le mode transparent.
Exemple :
---------
listen http_proxy 0.0.0.0:65000
mode tcp
server server01 192.168.1.1 check port 60000
server server02 192.168.1.2 check port 60000
# iptables -t nat -A PREROUTING -i eth0 -p tcp -d 192.168.1.100 \
-j REDIRECT --to-ports 65000
4.2) Journalisation des connexions
----------------------------------
@ -1003,6 +1114,88 @@ Exemple :
errorloc 503 http://192.168.114.58/error50x.html
errorloc 504 http://192.168.114.58/error50x.html
4.6) Changement des valeurs par défaut
--------------------------------------
Dans la version 1.1.22 est apparue la notion de valeurs par défaut, ce qui évite
de répéter des paramètres communs à toutes les instances, tels que les timeouts,
adresses de log, modes de fonctionnement, etc.
Les valeurs par défaut sont positionnées dans la dernière section 'defaults'
précédent l'instance qui les utilisera. On peut donc mettre autant de sections
'defaults' que l'on veut. Il faut juste se rappeler que la présence d'une telle
section implique une annulation de tous les paramètres par défaut positionnés
précédemment, dans le but de les remplacer.
La section 'defaults' utilise la même syntaxe que la section 'listen', aux
paramètres près qui ne sont pas supportés. Le mot clé 'defaults' peut accepter
un commentaire en guise paramètre.
Dans la version 1.1.22, seuls les paramètres suivants peuvent être positionnés
dans une section 'defaults' :
- log (le premier et le second)
- mode { tcp, http, health }
- balance { roundrobin }
- disabled (pour désactiver toutes les instances qui suivent)
- enabled (pour faire l'opération inverse, mais c'est le cas par défaut)
- contimeout, clitimeout, srvtimeout, grace, retries, maxconn
- option { redispatch, transparent, keepalive, forwardfor, httplog,
dontlognull, persist }
- redispatch, redisp, transparent, source { addr:port }
Ne sont pas supportés dans cette version, d'une manière générale, tous les
paramètres qui nécessitent de mémoriser autre chose que des adresses IP uniques
et valeurs numériques simples :
- dispatch, server,
- cookie, capture
- errorloc
- req*, rsp*,
Enfin, il n'y a pas le moyen, pour le moment, d'invalider un paramètre booléen
positionné par défaut. Donc si une option est spécifiée dans les paramètres par
défaut, le seul moyen de la désactiver pour une instance, c'est de changer les
paramètres par défaut avant la déclaration de l'instance.
Exemples :
----------
defaults applications TCP
log global
mode tcp
balance roundrobin
clitimeout 180000
srvtimeout 180000
contimeout 4000
retries 3
redispatch
listen app_tcp1 10.0.0.1:6000-6063
server srv1 192.168.1.1 check port 6000 inter 10000
server srv2 192.168.1.2 backup
listen app_tcp2 10.0.0.2:6000-6063
server srv1 192.168.2.1 check port 6000 inter 10000
server srv2 192.168.2.2 backup
defaults applications HTTP
log global
mode http
option httplog
option forwardfor
option dontlognull
balance roundrobin
clitimeout 20000
srvtimeout 20000
contimeout 4000
retries 3
listen app_http1 10.0.0.1:80-81
cookie SERVERID postonly insert indirect
capture cookie userid= len 10
server srv1 192.168.1.1:+8000 cookie srv1 check port 8080 inter 1000
server srv1 192.168.1.2:+8000 cookie srv2 check port 8080 inter 1000
defaults
# section vide qui annule tous les paramètes par défaut.
=======================
| Paramétrage système |

533
haproxy.c
View File

@ -53,8 +53,8 @@
#include <linux/netfilter_ipv4.h>
#endif
#define HAPROXY_VERSION "1.1.21"
#define HAPROXY_DATE "2003/05/06"
#define HAPROXY_VERSION "1.1.22"
#define HAPROXY_DATE "2003/09/11"
/* this is for libc5 for example */
#ifndef TCP_NODELAY
@ -295,8 +295,9 @@ int strlcpy2(char *dst, const char *src, int size) {
#define MODE_QUIET 16
/* server flags */
#define SRV_RUNNING 1
#define SRV_BACKUP 2
#define SRV_RUNNING 1 /* the server is UP */
#define SRV_BACKUP 2 /* this server is a backup server */
#define SRV_MAPPORTS 4 /* this server uses mapped ports */
/* what to do when a header matches a regex */
#define ACT_ALLOW 0 /* allow the request */
@ -349,6 +350,7 @@ struct server {
char *cookie; /* the id set in the cookie */
char *id; /* just for identification */
struct sockaddr_in addr; /* the address to connect to */
short check_port; /* the port to use for the health checks */
int health; /* 0->rise-1 = bad; rise->rise+fall-1 = good */
int rise, fall; /* time in iterations */
int inter; /* time in milliseconds */
@ -406,10 +408,16 @@ struct session {
unsigned int uniq_id; /* unique ID used for the traces */
};
struct listener {
int fd; /* the listen socket */
struct sockaddr_in addr; /* the address we listen to */
struct listener *next; /* next address or NULL */
};
struct proxy {
int listen_fd; /* the listen socket */
struct listener *listen; /* the listen addresses and sockets */
int state; /* proxy state */
struct sockaddr_in listen_addr; /* the address we listen to */
struct sockaddr_in dispatch_addr; /* the default address to connect to */
struct server *srv, *cursrv; /* known servers, current server */
int nbservers; /* # of servers */
@ -522,6 +530,7 @@ static int maxfd = 0; /* # of the highest fd + 1 */
static int listeners = 0; /* # of listeners */
static int stopping = 0; /* non zero means stopping in progress */
static struct timeval now = {0,0}; /* the current date at any moment */
static struct proxy defproxy; /* fake proxy used to assign default values on all instances */
static regmatch_t pmatch[MAX_MATCH]; /* rm_so, rm_eo for regular expressions */
/* this is used to drain data, and as a temporary buffer for sprintf()... */
@ -788,6 +797,79 @@ struct sockaddr_in *str2sa(char *str) {
}
/*
* converts <str> to a list of listeners which are dynamically allocated.
* The format is "{addr|'*'}:port[-end][,{addr|'*'}:port[-end]]*", where :
* - <addr> can be empty or "*" to indicate INADDR_ANY ;
* - <port> is a numerical port from 1 to 65535 ;
* - <end> indicates to use the range from <port> to <end> instead (inclusive).
* This can be repeated as many times as necessary, separated by a coma.
* The <tail> argument is a pointer to a current list which should be appended
* to the tail of the new list. The pointer to the new list is returned.
*/
struct listener *str2listener(char *str, struct listener *tail) {
struct listener *l;
char *c, *next, *range, *dupstr;
int port, end;
next = dupstr = strdup(str);
while (next && *next) {
str = next;
/* 1) look for the end of the first address */
if ((next = strrchr(str, ',')) != NULL) {
*next++ = 0;
}
/* 2) look for the addr/port delimiter */
if ((range = strrchr(str, ':')) != NULL) {
*range++ = 0;
}
else {
Alert("Missing port number: '%s'\n", str);
}
/* 3) look for the port-end delimiter */
if ((c = strchr(range, '-')) != NULL) {
*c++ = 0;
end = atol(c);
}
else {
end = atol(range);
}
for (port = atol(range); port <= end; port++) {
l = (struct listener *)calloc(1, sizeof(struct listener));
l->next = tail;
tail = l;
if (*str == '*' || *str == '\0') { /* INADDR_ANY */
l->addr.sin_addr.s_addr = INADDR_ANY;
}
else if (
#ifndef SOLARIS
!inet_aton(str, &l->addr.sin_addr)
#else
!inet_pton(AF_INET, str, &l->addr.sin_addr)
#endif
) {
struct hostent *he;
if ((he = gethostbyname(str)) == NULL) {
Alert("Invalid server name: '%s'\n", str);
}
else
l->addr.sin_addr = *(struct in_addr *) *(he->h_addr_list);
}
l->addr.sin_port=htons(port);
l->addr.sin_family=AF_INET;
} /* end for(port) */
} /* end while(next) */
free(dupstr);
return tail;
}
/*
* This function sends a syslog message to both log servers of a proxy,
* or to global log servers if the proxy is NULL.
@ -1391,6 +1473,18 @@ int connect_server(struct session *s) {
}
}
/* if this server remaps proxied ports, we'll use
* the port the client connected to with an offset. */
if (s->srv->state & SRV_MAPPORTS) {
struct sockaddr_in sockname;
int namelen;
namelen = sizeof(sockname);
if (get_original_dst(s->cli_fd, (struct sockaddr_in *)&sockname, &namelen) == -1)
getsockname(s->cli_fd, (struct sockaddr *)&sockname, &namelen);
s->srv_addr.sin_port = htons(ntohs(s->srv_addr.sin_port) + ntohs(sockname.sin_port));
}
if ((fd = s->srv_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
qfprintf(stderr, "Cannot get a server socket.\n");
return -1;
@ -3395,7 +3489,15 @@ int process_srv(struct session *t) {
shutdown(t->srv_fd, SHUT_RD);
t->srv_state = SV_STSHUTR;
return 1;
}
}
/* end of client read and no more data to send */
else if ((c == CL_STSHUTR || c == CL_STCLOSE) && (req->l == 0)) {
FD_CLR(t->srv_fd, StaticWriteEvent);
tv_eternity(&t->swexpire);
shutdown(t->srv_fd, SHUT_WR);
t->srv_state = SV_STSHUTW;
return 1;
}
/* read timeout */
else if (tv_cmp2_ms(&t->srexpire, &now) <= 0) {
FD_CLR(t->srv_fd, StaticReadEvent);
@ -3640,6 +3742,7 @@ int process_session(struct task *t) {
*/
int process_chk(struct task *t) {
struct server *s = t->context;
struct sockaddr_in sa;
int fd = s->curfd;
int one = 1;
@ -3660,6 +3763,10 @@ int process_chk(struct task *t) {
(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != -1)) {
//fprintf(stderr, "process_chk: 3\n");
/* we'll connect to the check port on the server */
sa = s->addr;
sa.sin_port = htons(s->check_port);
/* 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) {
@ -3667,7 +3774,7 @@ int process_chk(struct task *t) {
close(fd);
s->result = -1;
}
else if ((connect(fd, (struct sockaddr *)&s->addr, sizeof(s->addr)) != -1) || (errno == EINPROGRESS)) {
else if ((connect(fd, (struct sockaddr *)&sa, sizeof(sa)) != -1) || (errno == EINPROGRESS)) {
/* OK, connection in progress or established */
//fprintf(stderr, "process_chk: 4\n");
@ -3967,6 +4074,7 @@ int stats(void) {
*/
static int maintain_proxies(void) {
struct proxy *p;
struct listener *l;
int tleft; /* time left */
p = proxy;
@ -3977,13 +4085,17 @@ static int maintain_proxies(void) {
while (p) {
if (p->nbconn < p->maxconn) {
if (p->state == PR_STIDLE) {
FD_SET(p->listen_fd, StaticReadEvent);
for (l = p->listen; l != NULL; l = l->next) {
FD_SET(l->fd, StaticReadEvent);
}
p->state = PR_STRUN;
}
}
else {
if (p->state == PR_STRUN) {
FD_CLR(p->listen_fd, StaticReadEvent);
for (l = p->listen; l != NULL; l = l->next) {
FD_CLR(l->fd, StaticReadEvent);
}
p->state = PR_STIDLE;
}
}
@ -3993,7 +4105,9 @@ static int maintain_proxies(void) {
else { /* block all proxies */
while (p) {
if (p->state == PR_STRUN) {
FD_CLR(p->listen_fd, StaticReadEvent);
for (l = p->listen; l != NULL; l = l->next) {
FD_CLR(l->fd, StaticReadEvent);
}
p->state = PR_STIDLE;
}
p = p->next;
@ -4010,9 +4124,11 @@ static int maintain_proxies(void) {
Warning("Proxy %s stopped.\n", p->id);
send_log(p, LOG_WARNING, "Proxy %s stopped.\n", p->id);
fd_delete(p->listen_fd);
for (l = p->listen; l != NULL; l = l->next) {
fd_delete(l->fd);
listeners--;
}
p->state = PR_STDISABLED;
listeners--;
}
else {
tleft = MINTIME(t, tleft);
@ -4250,6 +4366,15 @@ int cfg_parse_global(char *file, int linenum, char **args) {
}
void init_default_instance() {
memset(&defproxy, 0, sizeof(defproxy));
defproxy.mode = PR_MODE_TCP;
defproxy.state = PR_STNEW;
defproxy.maxconn = cfg_maxpconn;
defproxy.conn_retries = CONN_RETRIES;
defproxy.logfac1 = defproxy.logfac2 = -1; /* log disabled */
}
/*
* parse a line in a <listen> section. Returns 0 if OK, -1 if error.
*/
@ -4258,8 +4383,9 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
struct server *newsrv = NULL;
if (!strcmp(args[0], "listen")) { /* new proxy */
if (strchr(args[2], ':') == NULL) {
Alert("parsing [%s:%d] : '%s' expects <id> and <addr:port> as arguments.\n",
if (!*args[1]) {
Alert("parsing [%s:%d] : '%s' expects an <id> argument and\n"
" optionnally supports [addr1]:port1[-end1]{,[addr]:port[-end]}...\n",
file, linenum, args[0]);
return -1;
}
@ -4271,24 +4397,53 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
curproxy->next = proxy;
proxy = curproxy;
curproxy->id = strdup(args[1]);
curproxy->listen_addr = *str2sa(args[2]);
curproxy->state = PR_STNEW;
if (strchr(args[2], ':') != NULL)
curproxy->listen = str2listener(args[2], curproxy->listen);
/* set default values */
curproxy->maxconn = cfg_maxpconn;
curproxy->conn_retries = CONN_RETRIES;
curproxy->options = 0;
curproxy->clitimeout = curproxy->contimeout = curproxy->srvtimeout = 0;
curproxy->mode = PR_MODE_TCP;
curproxy->logfac1 = curproxy->logfac2 = -1; /* log disabled */
curproxy->to_log = 0;
curproxy->state = defproxy.state;
curproxy->maxconn = defproxy.maxconn;
curproxy->conn_retries = defproxy.conn_retries;
curproxy->options = defproxy.options;
curproxy->clitimeout = defproxy.clitimeout;
curproxy->contimeout = defproxy.contimeout;
curproxy->srvtimeout = defproxy.srvtimeout;
curproxy->mode = defproxy.mode;
curproxy->logfac1 = defproxy.logfac1;
curproxy->logsrv1 = defproxy.logsrv1;
curproxy->loglev1 = defproxy.loglev1;
curproxy->logfac2 = defproxy.logfac2;
curproxy->logsrv2 = defproxy.logsrv2;
curproxy->loglev2 = defproxy.loglev2;
curproxy->to_log = defproxy.to_log;
curproxy->grace = defproxy.grace;
curproxy->source_addr = defproxy.source_addr;
return 0;
}
else if (!strcmp(args[0], "defaults")) { /* use this one to assign default values */
curproxy = &defproxy;
return 0;
}
else if (curproxy == NULL) {
Alert("parsing [%s:%d] : 'listen' expected.\n", file, linenum);
Alert("parsing [%s:%d] : 'listen' or 'defaults' expected.\n", file, linenum);
return -1;
}
if (!strcmp(args[0], "mode")) { /* sets the proxy mode */
if (!strcmp(args[0], "bind")) { /* new listen addresses */
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (strchr(args[1], ':') == NULL) {
Alert("parsing [%s:%d] : '%s' expects [addr1]:port1[-end1]{,[addr]:port[-end]}... as arguments.\n",
file, linenum, args[0]);
return -1;
}
curproxy->listen = str2listener(args[1], curproxy->listen);
return 0;
}
else if (!strcmp(args[0], "mode")) { /* sets the proxy mode */
if (!strcmp(args[1], "http")) curproxy->mode = PR_MODE_HTTP;
else if (!strcmp(args[1], "tcp")) curproxy->mode = PR_MODE_TCP;
else if (!strcmp(args[1], "health")) curproxy->mode = PR_MODE_HEALTH;
@ -4300,8 +4455,16 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
else if (!strcmp(args[0], "disabled")) { /* disables this proxy */
curproxy->state = PR_STDISABLED;
}
else if (!strcmp(args[0], "enabled")) { /* enables this proxy (used to revert a disabled default) */
curproxy->state = PR_STNEW;
}
else if (!strcmp(args[0], "cookie")) { /* cookie name */
int cur_arg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (curproxy->cookie_name != NULL) {
Alert("parsing [%s:%d] : cookie name already specified. Continuing.\n",
file, linenum);
@ -4341,12 +4504,17 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
cur_arg++;
}
if ((curproxy->options & (PR_O_COOK_RW|PR_O_COOK_IND)) == (PR_O_COOK_RW|PR_O_COOK_IND)) {
Alert("parsing [%s:%d] : cookie 'rewrite' and 'indirect' mode are incompatibles.\n",
Alert("parsing [%s:%d] : cookie 'rewrite' and 'indirect' mode are incompatible.\n",
file, linenum);
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;
}
if (curproxy->capture_name != NULL) {
Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n",
file, linenum, args[0]);
@ -4368,7 +4536,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
}
else if (!strcmp(args[0], "contimeout")) { /* connect timeout */
if (curproxy->contimeout != 0) {
if (curproxy->contimeout != defproxy.contimeout) {
Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
return 0;
}
@ -4380,7 +4548,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
curproxy->contimeout = atol(args[1]);
}
else if (!strcmp(args[0], "clitimeout")) { /* client timeout */
if (curproxy->clitimeout != 0) {
if (curproxy->clitimeout != defproxy.clitimeout) {
Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n",
file, linenum, args[0]);
return 0;
@ -4393,7 +4561,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
curproxy->clitimeout = atol(args[1]);
}
else if (!strcmp(args[0], "srvtimeout")) { /* server timeout */
if (curproxy->srvtimeout != 0) {
if (curproxy->srvtimeout != defproxy.srvtimeout) {
Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
return 0;
}
@ -4487,6 +4655,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
curproxy->grace = atol(args[1]);
}
else if (!strcmp(args[0], "dispatch")) { /* dispatch address */
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (strchr(args[1], ':') == NULL) {
Alert("parsing [%s:%d] : '%s' expects <addr:port> as argument.\n", file, linenum, args[0]);
return -1;
@ -4508,9 +4680,18 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
else if (!strcmp(args[0], "server")) { /* server address */
int cur_arg;
char *rport;
char *raddr;
short realport;
int do_check;
if (strchr(args[2], ':') == NULL) {
Alert("parsing [%s:%d] : '%s' expects <name> and <addr:port> as arguments.\n",
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (!*args[2]) {
Alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
file, linenum, args[0]);
return -1;
}
@ -4521,9 +4702,34 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
newsrv->next = curproxy->srv;
curproxy->srv = newsrv;
newsrv->proxy = curproxy;
newsrv->id = strdup(args[1]);
newsrv->addr = *str2sa(args[2]);
do_check = 0;
newsrv->state = SRV_RUNNING; /* early server setup */
newsrv->id = strdup(args[1]);
/* several ways to check the port component :
* - IP => port=+0, relative
* - IP: => port=+0, relative
* - IP:N => port=N, absolute
* - IP:+N => port=+N, relative
* - IP:-N => port=-N, relative
*/
raddr = strdup(args[2]);
rport = strchr(raddr, ':');
if (rport) {
*rport++ = 0;
realport = atol(rport);
if (!isdigit((int)*rport))
newsrv->state |= SRV_MAPPORTS;
} else {
realport = 0;
newsrv->state |= SRV_MAPPORTS;
}
newsrv->addr = *str2sa(raddr);
newsrv->addr.sin_port = htons(realport);
free(raddr);
newsrv->curfd = -1; /* no health-check in progress */
newsrv->inter = DEF_CHKINTR;
newsrv->rise = DEF_RISETIME;
@ -4549,38 +4755,54 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
newsrv->inter = atol(args[cur_arg + 1]);
cur_arg += 2;
}
else if (!strcmp(args[cur_arg], "port")) {
newsrv->check_port = atol(args[cur_arg + 1]);
cur_arg += 2;
}
else if (!strcmp(args[cur_arg], "backup")) {
newsrv->state |= SRV_BACKUP;
cur_arg ++;
}
else if (!strcmp(args[cur_arg], "check")) {
struct task *t;
if ((t = pool_alloc(task)) == NULL) { /* disable this proxy for a while */
Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
return -1;
}
t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
t->wq = LIST_HEAD(wait_queue); /* but already has a wait queue assigned */
t->state = TASK_IDLE;
t->process = process_chk;
t->context = newsrv;
if (curproxy->state != PR_STDISABLED) {
tv_delayfrom(&t->expire, &now, newsrv->inter); /* check this every ms */
task_queue(t);
task_wakeup(&rq, t);
}
do_check = 1;
cur_arg += 1;
}
else {
Alert("parsing [%s:%d] : server %s only supports options 'cookie', 'check', 'inter', 'rise' and 'fall'.\n",
Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise' and 'fall'.\n",
file, linenum, newsrv->id);
return -1;
}
}
if (do_check) {
struct task *t;
if (!newsrv->check_port && !(newsrv->state & SRV_MAPPORTS))
newsrv->check_port = realport; /* by default */
if (!newsrv->check_port) {
Alert("parsing [%s:%d] : server %s has neither service port nor check port. Check has been disabled.\n",
file, linenum, newsrv->id);
return -1;
}
if ((t = pool_alloc(task)) == NULL) {
Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
return -1;
}
t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
t->wq = LIST_HEAD(wait_queue); /* but already has a wait queue assigned */
t->state = TASK_IDLE;
t->process = process_chk;
t->context = newsrv;
if (curproxy->state != PR_STDISABLED) {
tv_delayfrom(&t->expire, &now, newsrv->inter); /* check this every ms */
task_queue(t);
task_wakeup(&rq, t);
}
}
curproxy->nbservers++;
}
else if (!strcmp(args[0], "log")) { /* syslog server address */
@ -4643,8 +4865,8 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
}
else if (!strcmp(args[0], "source")) { /* address to which we bind when connecting */
if (strchr(args[1], ':') == NULL) {
Alert("parsing [%s:%d] : '%s' expects <addr:port> as argument.\n",
if (!*args[1]) {
Alert("parsing [%s:%d] : '%s' expects <addr>[:<port>] as argument.\n",
file, linenum, "source");
return -1;
}
@ -4654,6 +4876,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) { /* replace request header from a regex */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0 || *(args[2]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <search> and <replace> as arguments.\n",
@ -4671,6 +4897,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
else if (!strcmp(args[0], "reqdel")) { /* delete request header from a regex */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@ -4687,6 +4917,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
else if (!strcmp(args[0], "reqdeny")) { /* deny a request if a header matches this regex */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@ -4703,6 +4937,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
else if (!strcmp(args[0], "reqpass")) { /* pass this header without allowing or denying the request */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@ -4719,6 +4957,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
else if (!strcmp(args[0], "reqallow")) { /* allow a request if a header matches this regex */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@ -4735,6 +4977,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
else if (!strcmp(args[0], "reqirep")) { /* replace request header from a regex, ignoring case */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0 || *(args[2]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <search> and <replace> as arguments.\n",
@ -4752,6 +4998,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
else if (!strcmp(args[0], "reqidel")) { /* delete request header from a regex ignoring case */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@ -4768,6 +5018,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
else if (!strcmp(args[0], "reqideny")) { /* deny a request if a header matches this regex ignoring case */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@ -4784,6 +5038,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
else if (!strcmp(args[0], "reqipass")) { /* pass this header without allowing or denying the request */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@ -4800,6 +5058,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
else if (!strcmp(args[0], "reqiallow")) { /* allow a request if a header matches this regex ignoring case */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <regex> as an argument.\n", file, linenum, args[0]);
@ -4815,6 +5077,11 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
chain_regex(&curproxy->req_exp, preg, ACT_ALLOW, NULL);
}
else if (!strcmp(args[0], "reqadd")) { /* add request header */
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (curproxy->nb_reqadd >= MAX_NEWHDR) {
Alert("parsing [%s:%d] : too many '%s'. Continuing.\n", file, linenum, args[0]);
return 0;
@ -4846,6 +5113,10 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
}
else if (!strcmp(args[0], "rspdel")) { /* delete response header from a regex */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <search> as an argument.\n", file, linenum, args[0]);
@ -4861,24 +5132,32 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
}
else if (!strcmp(args[0], "rspirep")) { /* replace response header from a regex ignoring case */
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 | REG_ICASE) != 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]));
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
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 | REG_ICASE) != 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], "rspidel")) { /* delete response header from a regex ignoring case */
regex_t *preg;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects <search> as an argument.\n", file, linenum, args[0]);
@ -4894,6 +5173,11 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2]));
}
else if (!strcmp(args[0], "rspadd")) { /* add response header */
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (curproxy->nb_rspadd >= MAX_NEWHDR) {
Alert("parsing [%s:%d] : too many '%s'. Continuing.\n", file, linenum, args[0]);
return 0;
@ -4910,6 +5194,11 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
int errnum;
char *err;
if (curproxy == &defproxy) {
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
return -1;
}
if (*(args[2]) == 0) {
Alert("parsing [%s:%d] : <errorloc> expects <error> and <url> as arguments.\n", file, linenum);
return -1;
@ -5083,7 +5372,7 @@ int readcfgfile(char *file) {
args[arg] = line;
}
if (!strcmp(args[0], "listen")) /* new proxy */
if (!strcmp(args[0], "listen") || !strcmp(args[0], "defaults")) /* new proxy */
confsect = CFG_LISTEN;
else if (!strcmp(args[0], "global")) /* global config */
confsect = CFG_GLOBAL;
@ -5342,70 +5631,70 @@ void init(int argc, char **argv) {
*/
int start_proxies() {
struct proxy *curproxy;
struct listener *listener;
int one = 1;
int fd;
for (curproxy = proxy; curproxy != NULL; curproxy = curproxy->next) {
if (curproxy->state == PR_STDISABLED)
continue;
if ((fd = curproxy->listen_fd =
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
Alert("cannot create listening socket for proxy %s. Aborting.\n",
curproxy->id);
return -1;
}
for (listener = curproxy->listen; listener != NULL; listener = listener->next) {
if ((fd = listener->fd =
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
Alert("cannot create listening socket for proxy %s. Aborting.\n",
curproxy->id);
return -1;
}
if (fd >= global.maxsock) {
Alert("socket(): not enough free sockets for proxy %s. Raise -n argument. Aborting.\n",
curproxy->id);
close(fd);
return -1;
}
if (fd >= global.maxsock) {
Alert("socket(): not enough free sockets for proxy %s. Raise -n argument. Aborting.\n",
curproxy->id);
close(fd);
return -1;
}
if ((fcntl(fd, F_SETFL, O_NONBLOCK) == -1) ||
(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
(char *) &one, sizeof(one)) == -1)) {
Alert("cannot make socket non-blocking for proxy %s. Aborting.\n",
curproxy->id);
close(fd);
return -1;
}
if ((fcntl(fd, F_SETFL, O_NONBLOCK) == -1) ||
(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
(char *) &one, sizeof(one)) == -1)) {
Alert("cannot make socket non-blocking for proxy %s. Aborting.\n",
curproxy->id);
close(fd);
return -1;
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) == -1) {
Alert("cannot do so_reuseaddr for proxy %s. Continuing.\n",
curproxy->id);
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) == -1) {
Alert("cannot do so_reuseaddr for proxy %s. Continuing.\n",
curproxy->id);
}
if (bind(fd,
(struct sockaddr *)&curproxy->listen_addr,
sizeof(curproxy->listen_addr)) == -1) {
Alert("cannot bind socket for proxy %s. Aborting.\n",
curproxy->id);
close(fd);
return -1;
}
if (bind(fd,
(struct sockaddr *)&listener->addr,
sizeof(listener->addr)) == -1) {
Alert("cannot bind socket for proxy %s. Aborting.\n",
curproxy->id);
close(fd);
return -1;
}
if (listen(fd, curproxy->maxconn) == -1) {
Alert("cannot listen to socket for proxy %s. Aborting.\n",
curproxy->id);
close(fd);
return -1;
}
if (listen(fd, curproxy->maxconn) == -1) {
Alert("cannot listen to socket for proxy %s. Aborting.\n",
curproxy->id);
close(fd);
return -1;
}
/* the function for the accept() event */
fdtab[fd].read = &event_accept;
fdtab[fd].write = NULL; /* never called */
fdtab[fd].owner = (struct task *)curproxy; /* reference the proxy instead of a task */
curproxy->state = PR_STRUN;
fdtab[fd].state = FD_STLISTEN;
FD_SET(fd, StaticReadEvent);
fd_insert(fd);
listeners++;
/* the function for the accept() event */
fdtab[fd].read = &event_accept;
fdtab[fd].write = NULL; /* never called */
fdtab[fd].owner = (struct task *)curproxy; /* reference the proxy instead of a task */
curproxy->state = PR_STRUN;
fdtab[fd].state = FD_STLISTEN;
FD_SET(fd, StaticReadEvent);
fd_insert(fd);
listeners++;
}
send_log(curproxy, LOG_NOTICE, "Proxy %s started.\n", curproxy->id);
}
return 0;
}

46
tests/defaults.cfg Normal file
View File

@ -0,0 +1,46 @@
global
#log 127.0.0.1 local0
#log 127.0.0.1 local1 notice
#log loghost local0 info
maxconn 4096
#chroot /var/empty
#uid 11
#gid 2
#daemon
#debug
#quiet
defaults applications TCP # :10000-10100,192.64.19.22:3129
#bind 127.0.0.1:1234-1237
log global
mode tcp
#mode http
#option httplog
#option dontlognull
balance roundrobin
#server app1_0 127.0.0.2:0# check inter 100 rise 2 fall 5
# server app1_1 127.0.0.1:8000 check inter 100 rise 2 fall 5
#server app1_2 127.0.0.1:8001 check inter 100 rise 2 fall 5 backup
retries 1
maxconn 2000
contimeout 5000
clitimeout 20000
srvtimeout 20000
listen appli3-relais 127.0.0.1:10000-10100#,192.64.19.22:3129
#bind 127.0.0.1:1234-1237
#log global
#mode tcp
#mode http
#option httplog
#option dontlognull
#balance roundrobin
server app1_0 127.0.0.2 check port 113 inter 100 rise 2 fall 5
# server app1_1 127.0.0.1:8000 check inter 100 rise 2 fall 5
#server app1_2 127.0.0.1:8001 check inter 100 rise 2 fall 5 backup
#retries 1
#maxconn 2000
#contimeout 5000
#clitimeout 20000
#srvtimeout 20000

7
tests/sockstat.txt Normal file
View File

@ -0,0 +1,7 @@
willy@pcw:haproxy-1.1.17-pre3$ cat /proc/net/sockstat
sockets: used 117
TCP: inuse 82 orphan 15 tw 561836 alloc 88 mem 75
UDP: inuse 13
RAW: inuse 0
FRAG: inuse 0 memory 0

27
tests/testinet.c Normal file
View File

@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <sys/resource.h>
#include <time.h>
#include <regex.h>
#include <syslog.h>
main() {
printf("sizeof sockaddr=%d\n", sizeof(struct sockaddr));
printf("sizeof sockaddr_in=%d\n", sizeof(struct sockaddr_in));
printf("sizeof sockaddr_in6=%d\n", sizeof(struct sockaddr_in6));
}