[MEDIUM] implement the slowstart parameter for servers

The new 'slowstart' parameter for a server accepts a value in
milliseconds which indicates after how long a server which has
just come back up will run at full speed. The speed grows
linearly from 0 to 100% during this time. The limitation applies
to two parameters :

  - maxconn: the number of connections accepted by the server
    will grow from 1 to 100% of the usual dynamic limit defined
    by (minconn,maxconn,fullconn).

  - weight: when the backend uses a dynamic weighted algorithm,
    the weight grows linearly from 1 to 100%. In this case, the
    weight is updated at every health-check. For this reason, it
    is important that the 'inter' parameter is smaller than the
    'slowstart', in order to maximize the number of steps.

The slowstart never applies when haproxy starts, otherwise it
would cause trouble to running servers. It only applies when
a server has been previously seen as failed.
diff --git a/include/types/server.h b/include/types/server.h
index b50e193..36ae7cb 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -42,6 +42,7 @@
 #define SRV_BIND_SRC	0x0008	/* this server uses a specific source address */
 #define SRV_CHECKED	0x0010	/* this server needs to be checked */
 #define SRV_GOINGDOWN	0x0020	/* this server says that it's going down (404) */
+#define SRV_WARMINGUP	0x0040	/* this server is warming up after a failure */
 
 #define SRV_TPROXY_ADDR	0x0020	/* bind to this non-local address to reach this server */
 #define SRV_TPROXY_CIP	0x0040	/* bind to the client's IP address to reach this server */
@@ -87,6 +88,7 @@
 	int health;				/* 0->rise-1 = bad; rise->rise+fall-1 = good */
 	int rise, fall;				/* time in iterations */
 	int inter;				/* time in milliseconds */
+	int slowstart;				/* slowstart time in seconds (ms in the conf) */
 	int result;				/* health-check result : SRV_CHK_* */
 	int curfd;				/* file desc used for current test, or -1 if not in test */
 
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 07caa72..869567f 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1428,6 +1428,7 @@
 		newsrv->health = newsrv->rise; /* up, but will fall down at first failure */
 		newsrv->uweight = 1;
 		newsrv->maxqueue = 0;
+		newsrv->slowstart = 0;
 
 		cur_arg = 3;
 		while (*args[cur_arg]) {
@@ -1484,6 +1485,11 @@
 				newsrv->maxqueue = atol(args[cur_arg + 1]);
 				cur_arg += 2;
 			}
+			else if (!strcmp(args[cur_arg], "slowstart")) {
+				/* slowstart is stored in seconds */
+				newsrv->slowstart = (atol(args[cur_arg + 1]) + 999) / 1000;
+				cur_arg += 2;
+			}
 			else if (!strcmp(args[cur_arg], "check")) {
 				global.maxsock++;
 				do_check = 1;
@@ -1540,7 +1546,7 @@
 			}
 #endif
 			else {
-				Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn', 'maxqueue', and 'weight'.\n",
+				Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn', 'maxqueue', 'slowstart' and 'weight'.\n",
 				      file, linenum, newsrv->id);
 				return -1;
 			}
diff --git a/src/checks.c b/src/checks.c
index d3aa6c8..c72b55f 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -530,6 +530,25 @@
 		if ((s->result & (SRV_CHK_ERROR|SRV_CHK_RUNNING)) == SRV_CHK_RUNNING) { /* good server detected */
 			//fprintf(stderr, "process_chk: 9\n");
 
+			if (s->state & SRV_WARMINGUP) {
+				if (now.tv_sec < s->last_change || now.tv_sec >= s->last_change + s->slowstart) {
+					s->state &= ~SRV_WARMINGUP;
+					if (s->proxy->lbprm.algo & BE_LB_PROP_DYN)
+						s->eweight = s->uweight * BE_WEIGHT_SCALE;
+					if (s->proxy->lbprm.update_server_eweight)
+						s->proxy->lbprm.update_server_eweight(s);
+				}
+				else if (s->proxy->lbprm.algo & BE_LB_PROP_DYN) {
+					/* for dynamic algorithms, let's update the weight */
+					s->eweight = BE_WEIGHT_SCALE * (now.tv_sec - s->last_change) / s->slowstart;
+					s->eweight *= s->uweight;
+					if (s->proxy->lbprm.update_server_eweight)
+						s->proxy->lbprm.update_server_eweight(s);
+				}
+				/* probably that we can refill this server with a bit more connections */
+				check_for_pending(s);
+			}
+
 			/* we may have to add/remove this server from the LB group */
 			if ((s->state & SRV_RUNNING) && (s->proxy->options & PR_O_DISABLE404)) {
 				if ((s->state & SRV_GOINGDOWN) &&
@@ -598,6 +617,17 @@
 
 					s->last_change = now.tv_sec;
 					s->state |= SRV_RUNNING;
+					if (s->slowstart > 0) {
+						s->state |= SRV_WARMINGUP;
+						if (s->proxy->lbprm.algo & BE_LB_PROP_DYN) {
+							/* For dynamic algorithms, start at the first step of the weight,
+							 * without multiplying by BE_WEIGHT_SCALE.
+							 */
+							s->eweight = s->uweight;
+							if (s->proxy->lbprm.update_server_eweight)
+								s->proxy->lbprm.update_server_eweight(s);
+						}
+					}
 					s->proxy->lbprm.set_server_status_up(s);
 
 					/* check if we can handle some connections queued at the proxy. We
diff --git a/src/queue.c b/src/queue.c
index a4670a8..d282fec 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -33,20 +33,32 @@
 
 /* returns the effective dynamic maxconn for a server, considering the minconn
  * and the proxy's usage relative to its dynamic connections limit. It is
- * expected that 0 < s->minconn <= s->maxconn when this is called.
+ * expected that 0 < s->minconn <= s->maxconn when this is called. If the
+ * server is currently warming up, the slowstart is also applied to the
+ * resulting value, which can be lower than minconn in this case, but never
+ * less than 1.
  */
 unsigned int srv_dynamic_maxconn(const struct server *s)
 {
+	unsigned int max;
+
 	if (s->proxy->beconn >= s->proxy->fullconn)
 		/* no fullconn or proxy is full */
-		return s->maxconn;
-
-	if (s->minconn == s->maxconn)
+		max = s->maxconn;
+	else if (s->minconn == s->maxconn)
 		/* static limit */
-		return s->maxconn;
+		max = s->maxconn;
+	else max = MAX(s->minconn,
+		       s->proxy->beconn * s->maxconn / s->proxy->fullconn);
 
-	return MAX(s->minconn,
-		   s->proxy->beconn * s->maxconn / s->proxy->fullconn);
+	if ((s->state & SRV_WARMINGUP) &&
+	    now.tv_sec < s->last_change + s->slowstart &&
+	    now.tv_sec >= s->last_change) {
+		unsigned int ratio;
+		ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
+		max = max * ratio / 100;
+	}
+	return max;
 }