]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
* released 1.1.11 v1.1.11
authorwilly tarreau <willy@wtap.(none)>
Sat, 17 Dec 2005 12:14:35 +0000 (13:14 +0100)
committerwilly tarreau <willy@wtap.(none)>
Sat, 17 Dec 2005 12:14:35 +0000 (13:14 +0100)
* fixed multi-cookie handling in client request to allow clean deletion
  in insert+indirect mode. Now, only the server cookie is deleted and not
  all the header. Should now be compliant to RFC2109.
* added a "nocache" option to "cookie" to specify that we explicitly want
  to add a "cache-control" header when we add a cookie.
  It is also possible to add an "Expires: <old-date>" to keep compatibility
  with old/broken caches.
* some doc and examples cleanups

NOTES
TODO
doc/haproxy.txt
examples/examples.cfg [moved from examples/cfg with 100% similarity]
examples/haproxy.cfg [new file with mode: 0644]
examples/rc.highsock
haproxy.c

diff --git a/NOTES b/NOTES
index 13b19bab0666b1f7577e8123fc0451438d19d6a0..78de2c16e61dbff21dc00a90ac322759317d9e4b 100644 (file)
--- a/NOTES
+++ b/NOTES
   * 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
 1.1.9 -> 1.1.10
-  - automatically remove client cookie in insert+indirect mode
-
+  * automatically remove client cookie in insert+indirect mode
+1.1.10 -> 1.1.11
+  * support multi-cookie as described in RFC2109
+  * added "nocache" option to prevent caches from storing cookies.
diff --git a/TODO b/TODO
index d690e70b9a05fa2afaa52e04ef4bc4bcf93da528..97236802d63ec5e62c21bd84f09c9fbd3ea80a77 100644 (file)
--- a/TODO
+++ b/TODO
   - 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
+- filtrage sur l'adresse IP source, et stocker le pointeur sur la dernière regex
+  matchée dans la "session" pour accélérer les regex.
 - gestion keep-alive
+
+- handle parametrable HTTP health-checks replies
+- differentiate http headers and http uris
+- support environment variables in config file
+- support keep-alive
+
index 36c3c9945b894dc548cf07377f20f615c5a02152..23c2a0e5b24369425808d3d022643a02ac0871b9 100644 (file)
@@ -1,9 +1,9 @@
 
                             H A - P r o x y
                             ---------------
-                             version 1.1.10
+                             version 1.1.11
                              willy tarreau
-                              2002/05/10
+                              2002/06/11
 
 ================
 | Introduction |
@@ -417,7 +417,29 @@ Ce param
 'balance'.
 
 
-2.8) Définition du nom du cookie
+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.
+
+Exemples :
+----------
+    listen http_proxy 0.0.0.0: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
+       # utiliser l'adresse 192.168.1.200 et le port réservé 900
+       source 192.168.1.200:900
+
+
+2.9) Définition du nom du cookie
 --------------------------------
 En mode HTTP, il est possible de rechercher la valeur d'un cookie pour savoir
 vers quel serveur aiguiller la requête utilisateur. Le nom du cookie est donné
@@ -433,7 +455,8 @@ On peut modifier l'utilisation du cookie pour la rendre plus intelligente
 vis-à-vis des applications relayées. Il est possible, notamment de supprimer ou
 réécrire un cookie retourné par un serveur accédé en direct, et d'insérer un
 cookie dans une réponse HTTP adressée à un serveur sélectionné en répartition
-de charge.
+de charge, et même de signaler aux proxies amont de ne pas cacher le cookie
+inséré.
 
 Exemples :
 ----------
@@ -455,6 +478,12 @@ serveurs aient un cookie renseign
 
        cookie SERVERID insert
 
+Pour insérer un cookie, en s'assurant qu'un cache en amont ne le stockera pas,
+ajouter le mot clé 'nocache' après 'insert' :
+
+       cookie SERVERID insert nocache
+
+
 Remarques :
 -----------
 - Il est possible de combiner 'insert' avec 'indirect' ou 'rewrite' pour s'adapter
@@ -463,8 +492,13 @@ Remarques :
 - dans le cas où 'insert' et 'indirect' sont spécifiés, le cookie n'est jamais
   transmis au serveur vu qu'il n'en a pas connaissance et ne pourrait pas le
   comprendre.
+- il est particulièrement recommandé d'utiliser 'nocache' en mode insertion si
+  des caches peuvent se trouver entre les clients et l'instance du proxy. Dans
+  le cas contraire, un cache HTTP 1.0 pourrait cacher la réponse, incluant le
+  cookie de persistence inséré, donc provoquer des changements de serveurs pour
+  des clients partageant le même cache.
 
-2.9) Assignation d'un serveur à une valeur de cookie
+2.10) Assignation d'un serveur à une valeur de cookie
 ----------------------------------------------------
 En mode HTTP, il est possible d'associer des serveurs à des valeurs de
 cookie par le paramètre 'server'. La syntaxe est :
@@ -536,10 +570,11 @@ Exemples :
        server web2 192.168.1.2:80 cookie server02 check inter 500 rise 1 fall 2
 
 # Insertion automatique de cookie dans la réponse du serveur, et suppression
-# automatique dans la requête.
+# automatique dans la requête, tout en indiquant aux caches de ne pas garder
+# ce cookie.
     listen web_appl 0.0.0.0:80
        mode http
-       cookie SERVERID insert indirect
+       cookie SERVERID insert nocache indirect
        balance roundrobin
        server web1 192.168.1.1:80 cookie server01 check
        server web2 192.168.1.2:80 cookie server02 check
@@ -667,7 +702,7 @@ La syntaxe est :
    reqallow  <search>             autoriser une requête qui valide <search>
    reqiallow <search>             idem sans distinction majuscules/minuscules
    reqdeny   <search>             interdire une requête qui valide <search>
-   reqdeny   <search>             idem sans distinction majuscules/minuscules
+   reqideny  <search>             idem sans distinction majuscules/minuscules
 
    rspadd   <string>             pour ajouter un en-tête dans la réponse
    rsprep   <search> <replace>   pour modifier la réponse
@@ -733,14 +768,15 @@ permet d'assurer une persistence dans les sessions HTTP d'une mani
 pratiquement transparente pour les applications. Le principe est simple :
   - attribuer une valeur d'un cookie à chaque serveur
   - effectuer une répartition interne
-  - insérer un cookie dans les réponses issues d'une répartition uniquement
-  - cacher ce cookie à l'application
+  - insérer un cookie dans les réponses issues d'une répartition uniquement,
+    et faire en sorte que des caches ne mémorisent pas ce cookie.
+  - cacher ce cookie à l'application lors des requêtes ultérieures.
 
 Exemple :
 -------
     listen application 0.0.0.0:80
        mode http
-       cookie SERVERID insert indirect
+       cookie SERVERID insert nocache indirect
        balance roundrobin
        server 192.168.1.1:80 cookie server01 check
        server 192.168.1.2:80 cookie server02 check
@@ -772,7 +808,6 @@ if [ -e /proc/sys/net/ipv4/netfilter/ip_ct_tcp_timeout_fin_wait ]; then
 fi
 
 echo 1024 60999 > /proc/sys/net/ipv4/ip_local_port_range
-echo 32768 > /proc/sys/net/ipv4/ip_queue_maxlen
 echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
 echo 4096 > /proc/sys/net/ipv4/tcp_max_syn_backlog
 echo 262144 > /proc/sys/net/ipv4/tcp_max_tw_buckets
similarity index 100%
rename from examples/cfg
rename to examples/examples.cfg
diff --git a/examples/haproxy.cfg b/examples/haproxy.cfg
new file mode 100644 (file)
index 0000000..44fd8d7
--- /dev/null
@@ -0,0 +1,63 @@
+global
+       log 127.0.0.1   local0
+       maxconn 4096
+       chroot /tmp
+       uid 11
+       gid 2
+       daemon
+       #debug
+       #quiet
+
+listen appli1-rewrite 0.0.0.0:10001
+       log     global
+       mode    http
+       option  httplog
+       option  dontlognull
+       cookie  SERVERID rewrite
+       balance roundrobin
+       server  app1_1 192.168.34.23:8080 cookie app1inst1 check inter 2000 rise 2 fall 5
+       server  app1_2 192.168.34.32:8080 cookie app1inst2 check inter 2000 rise 2 fall 5
+       server  app1_3 192.168.34.27:8080 cookie app1inst3 check inter 2000 rise 2 fall 5
+       server  app1_4 192.168.34.42:8080 cookie app1inst4 check inter 2000 rise 2 fall 5
+       retries 3
+       redispatch
+       maxconn 2000
+       contimeout      5000
+       clitimeout      50000
+       srvtimeout      50000
+       
+listen appli2-insert 0.0.0.0:10002
+       log     global
+       mode    http
+       option  httplog
+       option  dontlognull
+       balance roundrobin
+       cookie  SERVERID insert indirect nocache
+       server  inst1 192.168.114.56:80 cookie server01 check inter 2000 fall 3
+       server  inst2 192.168.114.56:81 cookie server02 check inter 2000 fall 3
+       retries 3
+       redispatch
+       maxconn 2000
+       contimeout      5000
+       clitimeout      50000
+       srvtimeout      50000
+
+       reqidel ^Connection:            # desactivation du keep-alive
+       reqadd  Connection:\ close
+       rspidel ^Connection:
+       rspadd  Connection:\ close
+       rspidel ^Set-cookie:\ IP=       # ne pas laisser sortir une adresse privee
+       
+listen appli3-relais 0.0.0.0:10003
+       log     global
+               mode    http
+       option  httplog
+       option  dontlognull
+       dispatch 192.168.135.17:80
+       retries 3
+       redispatch
+       maxconn 2000
+       contimeout      5000
+       clitimeout      50000
+       srvtimeout      50000
+
index 9325ee0620337247d3910efb12d73e51b81bb591..d85935af1a81abd41ea4ad2b685d388ccedc76be 100644 (file)
@@ -17,7 +17,7 @@ if [ -e /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_fin_wait ]; then
 fi
 
 echo 1024 60999 > /proc/sys/net/ipv4/ip_local_port_range
-echo 32768 > /proc/sys/net/ipv4/ip_queue_maxlen
+#echo 32768 > /proc/sys/net/ipv4/ip_queue_maxlen
 echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
 echo 4096 > /proc/sys/net/ipv4/tcp_max_syn_backlog
 echo 262144 > /proc/sys/net/ipv4/tcp_max_tw_buckets
index 9ab37b1c544ae3c9f4808b43c19b53d08b590ad0..e6721d7cc76660e26c23585774a81e878997df33 100644 (file)
--- a/haproxy.c
+++ b/haproxy.c
  *
  * ChangeLog :
  *
+ * 2002/06/04 : 1.1.11
+ *   - fixed multi-cookie handling in client request to allow clean deletion
+ *     in insert+indirect mode. Now, only the server cookie is deleted and not
+ *     all the header. Should now be compliant to RFC2109.
+ *   - added a "nocache" option to "cookie" to specify that we explicitly want
+ *     to add a "cache-control" header when we add a cookie.
+ *     It is also possible to add an "Expires: <old-date>" to keep compatibility
+ *     with old/broken caches.
  * 2002/05/10 : 1.1.10
  *   - if a cookie is used in insert+indirect mode, it's desirable that the
  *     the servers don't see it. It was not possible to remove it correctly
 #include <linux/netfilter_ipv4.h>
 #endif
 
-#define HAPROXY_VERSION "1.1.9"
-#define HAPROXY_DATE   "2002/04/19"
+#define HAPROXY_VERSION "1.1.11"
+#define HAPROXY_DATE   "2002/06/04"
 
 /* this is for libc5 for example */
 #ifndef TCP_NODELAY
@@ -313,6 +321,7 @@ int strlcpy(char *dst, const char *src, int size) {
 #define        PR_O_FWDFOR     128     /* insert x-forwarded-for with client address */
 #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 */
+#define PR_O_COOK_NOC  1024    /* add a 'Cache-control' header with the cookie */
 
 
 /* various session flags */
@@ -2021,7 +2030,9 @@ int buffer_replace(struct buffer *b, char *pos, char *end, char *str) {
     return delta;
 }
 
-/* same except that the string len is given */
+/* same except that the string len is given, which allows str to be NULL if
+ * len is 0.
+ */
 int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len) {
     int delta;
 
@@ -2034,7 +2045,8 @@ int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len)
     memmove(end + delta, end, b->data + b->l - end);
 
     /* now, copy str over pos */
-    memcpy(pos, str,len);
+    if (len)
+       memcpy(pos, str, len);
 
     /* we only move data after the displaced zone */
     if (b->r  > pos) b->r  += delta;
@@ -2247,43 +2259,59 @@ int process_cli(struct session *t) {
                *ptr = term; /* restore the string terminator */
            }
            
-           /* now look for cookies */
-           if (!delete_header && (req->r >= req->h + 8) && (t->proxy->cookie_name != NULL)
+           /* Now look for cookies. Conforming to RFC2109, we have to support
+            * attributes whose name begin with a '$', and associate them with
+            * the right cookie, if we want to delete this cookie.
+            * So there are 3 cases for each cookie read :
+            * 1) it's a special attribute, beginning with a '$' : ignore it.
+            * 2) it's a server id cookie that we *MAY* want to delete : save
+            *    some pointers on it (last semi-colon, beginning of cookie...)
+            * 3) it's an application cookie : we *MAY* have to delete a previous
+            *    "special" cookie.
+            * At the end of loop, if a "special" cookie remains, we may have to
+            * remove it. If no application cookie persists in the header, we
+            * *MUST* delete it
+            */
+           if (!delete_header && (t->proxy->cookie_name != NULL)
+               && !(t->flags & SN_CLDENY) && (ptr >= req->h + 8)
                && (strncmp(req->h, "Cookie: ", 8) == 0)) {
                char *p1, *p2, *p3, *p4;
-               
+               char *del_colon, *del_cookie, *colon;
+               int app_cookies;
+
                p1 = req->h + 8; /* first char after 'Cookie: ' */
+               colon = p1;
+               /* del_cookie == NULL => nothing to be deleted */
+               del_colon = del_cookie = NULL;
+               app_cookies = 0;
                
                while (p1 < ptr) {
-                   while (p1 < ptr && (isspace((int)*p1) || *p1 == ';'))
+                   /* skip spaces and colons, but keep an eye on these ones */
+                   while (p1 < ptr) {
+                       if (*p1 == ';' || *p1 == ',')
+                           colon = p1;
+                       else if (!isspace((int)*p1))
+                           break;
                        p1++;
+                   }
                    
                    if (p1 == ptr)
                        break;
-                   else if (*p1 == ';') { /* next cookie */
-                       ++p1;
-                       continue;
-                   }
                    
                    /* p1 is at the beginning of the cookie name */
                    p2 = p1;
-                   
-                   while (p2 < ptr && *p2 != '=' && *p2 != ';')
+                   while (p2 < ptr && *p2 != '=')
                        p2++;
                    
                    if (p2 == ptr)
                        break;
-                   else if (*p2 == ';') { /* next cookie */
-                       p1=++p2;
-                       continue;
-                   }
 
                    p3 = p2 + 1; /* skips the '=' sign */
                    if (p3 == ptr)
                        break;
                    
-                   p4=p3;
-                   while (p4 < ptr && !isspace((int)*p4) && *p4 != ';')
+                   p4 = p3;
+                   while (p4 < ptr && !isspace((int)*p4) && *p4 != ';' && *p4 != ',')
                        p4++;
                    
                    /* here, we have the cookie name between p1 and p2,
@@ -2291,8 +2319,11 @@ int process_cli(struct session *t) {
                     * we can process it.
                     */
                    
-                   if ((p2 - p1 == strlen(t->proxy->cookie_name)) &&
-                       (strncmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) {
+                   if (*p1 == '$') {
+                       /* skip this one */
+                   }
+                   else if ((p2 - p1 == strlen(t->proxy->cookie_name)) &&
+                       (memcmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) {
                        /* Cool... it's the right one */
                        struct server *srv = t->proxy->srv;
 
@@ -2304,31 +2335,64 @@ int process_cli(struct session *t) {
                        if (srv) { /* we found the server */
                            t->flags |= SN_DIRECT;
                            t->srv = srv;
-                           /* if this cookie was set in insert+indirect mode, then it's better that the
-                            * server never sees it.
-                            */
-                           if ((t->proxy->options & (PR_O_COOK_INS | PR_O_COOK_IND)) == (PR_O_COOK_INS | PR_O_COOK_IND))
-                               delete_header = 1;
                        }
-
-                       break;
+                       /* if this cookie was set in insert+indirect mode, then it's better that the
+                        * server never sees it.
+                        */
+                       if (del_cookie == NULL &&
+                           (t->proxy->options & (PR_O_COOK_INS | PR_O_COOK_IND)) == (PR_O_COOK_INS | PR_O_COOK_IND)) {
+                               del_cookie = p1;
+                               del_colon = colon;
+                       }
                    }
                    else {
-                       // fprintf(stderr,"Ignoring unknown cookie : ");
-                       // write(2, p1, p2-p1);
-                       // fprintf(stderr," = ");
-                       // write(2, p3, p4-p3);
-                       // fprintf(stderr,"\n");
+                       /* now we know that we must keep this cookie since it's
+                        * not ours. But if we wanted to delete our cookie
+                        * earlier, we cannot remove the complete header, but we
+                        * can remove the previous block itself.
+                        */
+                       app_cookies++;
+
+                       if (del_cookie != NULL) {
+                           buffer_replace2(req, del_cookie, p1, NULL, 0);
+                           p4  -= (p1 - del_cookie);
+                           ptr -= (p1 - del_cookie);
+                           del_cookie = del_colon = NULL;
+                       }
                    }
+
                    /* we'll have to look for another cookie ... */
                    p1 = p4;
                } /* while (p1 < ptr) */
-           } /* end of cookie processing */
+
+               /* There's no more cookie on this line.
+                * We may have marked the last one(s) for deletion.
+                * We must do this now in two ways :
+                *  - if there is no app cookie, we simply delete the header ;
+                *  - if there are app cookies, we must delete the end of the
+                *    string properly, including the colon/semi-colon before
+                *    the cookie name.
+                */
+               if (del_cookie != NULL) {
+                   if (app_cookies) {
+                       buffer_replace2(req, del_colon, ptr, NULL, 0);
+                       /* WARNING! <ptr> becomes invalid for now. If some code
+                        * below needs to rely on it before the end of the global
+                        * header loop, we need to correct it with this code :
+                        * ptr = del_colon;
+                        */
+                   }
+                   else
+                       delete_header = 1;
+               }
+           } /* end of cookie processing on this header */
 
            /* let's look if we have to delete this header */
            if (delete_header && !(t->flags & SN_CLDENY)) {
-               buffer_replace2(req, req->h, req->lr, "", 0);
+               buffer_replace2(req, req->h, req->lr, NULL, 0);
            }
+           /* WARNING: ptr is not valid anymore, since the header may have been deleted or truncated ! */
+
            req->h = req->lr;
        } /* while (req->lr < req->r) */
 
@@ -2634,6 +2698,9 @@ int process_srv(struct session *t) {
                     */
                    len = sprintf(newhdr, "Set-Cookie: %s=%s; path=/\r\n",
                                  t->proxy->cookie_name, t->srv->cookie);
+                   if (t->proxy->options & PR_O_COOK_NOC)
+                       len += sprintf(newhdr + len, "Cache-control: no-cache=\"set-cookie\"\r\n");
+
                    buffer_replace2(rep, rep->h, rep->h, newhdr, len);
                }
 
@@ -2731,8 +2798,9 @@ int process_srv(struct session *t) {
            }
            
            /* check for server cookies */
-           if (!delete_header && (t->proxy->options & PR_O_COOK_ANY) && (rep->r >= rep->h + 12) &&
-               (t->proxy->cookie_name != NULL) && (strncmp(rep->h, "Set-Cookie: ", 12) == 0)) {
+           if (!delete_header && (t->proxy->options & PR_O_COOK_ANY)
+               && (t->proxy->cookie_name != NULL) && (ptr >= rep->h + 12)
+               && (strncmp(rep->h, "Set-Cookie: ", 12) == 0)) {
                char *p1, *p2, *p3, *p4;
                
                p1 = rep->h + 12; /* first char after 'Set-Cookie: ' */
@@ -3709,6 +3777,9 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
            else if (!strcmp(args[cur_arg], "insert")) {
                curproxy->options |= PR_O_COOK_INS;
            }
+           else if (!strcmp(args[cur_arg], "nocache")) {
+               curproxy->options |= PR_O_COOK_NOC;
+           }
            else {
                Alert("parsing [%s:%d] : <cookie> supports 'rewrite', 'insert' and 'indirect' options.\n",
                      file, linenum);