I have had a request here to add a feature to haproxy.  I don't know if it 
would be generally useful or even if there might be
a better or more general method to do it.

In a load balanced configuration the customer wants the HTTP Host header to be 
re-written specifically for each destination server.
The destination server is running an Apache HTTPD with a set of VirtualHost 
entries who's names may not match the original Host header or even the original 
host name.  Replacing the Host header allows the HTTPD on the destination to 
select the correct virtual host to service the request.

I looked at using the ordinary header rewrite rules but there are two problems. 
 First they are generally applied before the destination server is selected.  
Second there seems to be no "macro" system to allow the value to be defined 
differently for each destination server.

I use the server ID string as the Host value for that destination server since 
it does not seem to be used except for informational
purposes.

I've added an option to enable this and the code to do the rewrite just after a 
load balancer selects a new server for a transaction.
I also added a clause to the configuration.txt document with the new option and 
its behavior.

The option is 'http-host-server-id'.  This reflects the fact that the HTTP Host 
header will be replaced with the server ID string.
I'm not wedded to this name, so if people think "http-rewrite-host-header" or 
some such would be better, feel free to hash it out.

I've also created an SRPM by modifying the current Fedora 16 SRPM.  That is 
available on request until I find a place to put it publicly.

Ideally I'd like to have this (or some method to do the same thing) reviewed 
and accepted into the main line.

The patch is attached and an example simple config is included.


# Test Rewriting Host header

defaults
  mode http
  timeout connect 1000ms
  timeout client 1000ms
  timeout server 1000ms

listen balance
  bind 127.0.1.1:8080
  balance roundrobin
  server myapp.host2.example.com host2.example.com:8080
  server myapp.host3.example.com host3.example.com:8080

  # replace outbound Host header with the server ID string.
  option http-host-server-id


-- 
Mark Lamourine <markll...@redhat.com>
Sr. Software Developer, Cloud Engineering
Red Hat, 314 Littleton Road, Westford MA 01886
Voice: +1 978 392 1093
http://people.redhat.com/~mlamouri
markllama @ irc://irc.freenod.org*lopsa
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 48d77c9..a42d709 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -910,6 +910,7 @@ option dontlognull                   (*)  X          X         X         -
 option forceclose                    (*)  X          X         X         X
 -- keyword -------------------------- defaults - frontend - listen -- backend -
 option forwardfor                         X          X         X         X
+option http-host-server-id           (*)  X          -         X         X
 option http-no-delay                 (*)  X          X         X         X
 option http-pretend-keepalive        (*)  X          X         X         X
 option http-server-close             (*)  X          X         X         X
@@ -2978,6 +2979,21 @@ option forwardfor [ except <network> ] [ header <name> ] [ if-none ]
              "option forceclose"
 
 
+option http-host-server-id
+no option http-host-server-id
+  Instruct the server to replace the HTTP "Host" header with the server id
+  string.  
+  May be used in sections :   defaults | frontend | listen | backend
+                                 yes   |    no   |   yes  |   yes
+  Arguments : none
+
+  The HTTP server on the back end may contain multiple virtual servers. The
+  HTTP daemon distinguishes virtual server requests arriving on a single IP
+  interface by the HTTP "Host" header.
+
+  This option causes the proxy server to replace the "Host" header in the
+  outbound request with the id string chosen by the load balancer.
+
 option http-no-delay
 no option http-no-delay
   Instruct the system to favor low interactive delays over performance in HTTP
diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h
index 417d5e6..de5b03e 100644
--- a/include/proto/proto_http.h
+++ b/include/proto/proto_http.h
@@ -66,6 +66,7 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s
 int http_process_request(struct session *t, struct buffer *req, int an_bit);
 int http_process_tarpit(struct session *s, struct buffer *req, int an_bit);
 int http_process_request_body(struct session *s, struct buffer *req, int an_bit);
+void http_change_host_header(struct http_txn *txn, struct http_msg *msg, struct buffer *buf, char *hostname);
 int http_wait_for_response(struct session *s, struct buffer *rep, int an_bit);
 int http_process_res_common(struct session *t, struct buffer *rep, int an_bit, struct proxy *px);
 int http_request_forward_body(struct session *s, struct buffer *req, int an_bit);
diff --git a/include/types/proxy.h b/include/types/proxy.h
index 3063516..4091f8f 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -152,6 +152,7 @@
 /* unused: 0x10000000 */
 #define PR_O2_FF_ALWAYS 0x20000000      /* always set x-forwarded-for */
 #define PR_O2_NODELAY   0x40000000      /* fully interactive mode, never delay outgoing data */
+#define PR_O2_HOST_SID  0x80000000      /* replace HTTP Host header with server id */
 /* end of proxy->options2 */
 
 /* bits for sticking rules */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 5cc7df2..5ce75c7 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -152,6 +152,7 @@ static const struct cfg_opt cfg_opts2[] =
 	{ "http-use-proxy-header",        PR_O2_USE_PXHDR, PR_CAP_FE, 0, PR_MODE_HTTP },
 	{ "http-pretend-keepalive",       PR_O2_FAKE_KA,   PR_CAP_FE|PR_CAP_BE, 0, PR_MODE_HTTP },
 	{ "http-no-delay",                PR_O2_NODELAY,   PR_CAP_FE|PR_CAP_BE, 0, PR_MODE_HTTP },
+	{ "http-host-server-id",          PR_O2_HOST_SID,  PR_CAP_BE, 0, PR_MODE_HTTP },
 	{ NULL, 0, 0, 0 }
 };
 
diff --git a/src/proto_http.c b/src/proto_http.c
index 4cdfc62..263a088 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -2055,6 +2055,34 @@ void http_change_connection_header(struct http_txn *txn, struct http_msg *msg, s
 	return;
 }
 
+/*
+ *
+ */
+void http_change_host_header(struct http_txn *txn, struct http_msg *msg, struct buffer *buf, char *hostname)
+{
+	struct hdr_ctx ctx;
+	const char *hdr_val = "Host";
+	int hdr_len = 4;
+
+	ctx.idx = 0;
+
+	/* remove any existing Host: header */
+	while (http_find_header2(hdr_val, hdr_len, msg->sol, &txn->hdr_idx, &ctx)) { 
+		/* remove */
+		http_remove_header2(msg, buf, &txn->hdr_idx, &ctx);
+	}
+
+	/* add a new Host: header on the end with the new value */
+	sprintf(trash, "Host: %s", hostname);
+	hdr_val = trash;
+	hdr_len = strlen(hdr_val);
+
+	http_header_add_tail2(buf, msg, &txn->hdr_idx, hdr_val, hdr_len);
+
+	return;
+}
+
+
 /* Parse the chunk size at buf->lr. Once done, it adjusts ->lr to point to the
  * first byte of body, and increments msg->sov by the number of bytes parsed,
  * so that we know we can forward between ->som and ->sov. Note that due to
diff --git a/src/session.c b/src/session.c
index 705439a..ac168e2 100644
--- a/src/session.c
+++ b/src/session.c
@@ -552,6 +552,7 @@ static void sess_prepare_conn_req(struct session *s, struct stream_interface *si
 	/* The server is assigned */
 	s->logs.t_queue = tv_ms_elapsed(&s->logs.tv_accept, &now);
 	si->state = SI_ST_ASS;
+
 }
 
 /* This stream analyser checks the switching rules and changes the backend
@@ -1408,6 +1409,17 @@ resync_stream_interface:
 			    s->srv->rdr_len && (s->flags & SN_REDIRECTABLE))
 				perform_http_redirect(s, &s->si[1]);
 		} while (s->si[1].state == SI_ST_ASS);
+
+		/* now we can update the Hosts: header if requested */
+		/* check s->be->options2 for  PR_O2_HOST_SID */
+		if ((s->flags && SN_BE_ASSIGNED) && 
+		    (s->be->options2 && PR_O2_HOST_SID)) {
+			http_change_host_header(&s->txn, &(&s->txn)->req, 
+						s->req, s->srv->id);
+
+		}
+
+
 	}
 
 	/* Benchmarks have shown that it's optimal to do a full resync now */

Reply via email to