Hi Mark,
sorry, I missed your mail.
On Wed, Nov 23, 2011 at 02:42:58PM -0500, Mark Lamourine wrote:
> 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.
Well, to be fair, you're the first one to request such a feature in
about 10 years of usage and as you explained to me privately, it was
for a very specific usage.
> 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.
In fact you're in a situation where you're scanning multiple virtual
hosts, which is very different from normal load-balancing where all
servers are identical.
> 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.
Indeed. The rewrite rules currently are inbound only. As you can see on
the diagram in the "doc/internal" subdir, there was a plan to add outgoing
processing but this has never been implemented because there are issues
related to the redispatching : server selection may be executed multiple
times for a given request due to first server failure then redispatch on
another one. However I'm well aware that some users need to add a header
or cookie to indicate which server was selected when they're building
silo-like architectures.
What I'd like to do is to be able to have a server setting which would
cause a name or string to be added in a specific header when forwarding
the request. This would imply that this header would be deleted and
re-built upon redispatch, so it would not be a general purpose header.
If we permitted to name the header in the backend section, then this
would also cover your need, as you would set the header name to "Host"
and would specify the expected vhost name on the "server" line.
For instance, some people would have :
backend foo
http-send-name-header "silo-id"
server srv1 192.168.0.1:80 send-name srv1
server srv2 192.168.0.2:80 send-name srv2
server srv3 192.168.0.3:80 send-name srv3
while you could use (for instance) :
backend foo
http-send-name-header "host"
server srv1 192.168.0.1:80 send-name 192.168.0.1
server srv2 192.168.0.2:80 send-name 192.168.0.2
server srv3 192.168.0.3:80 send-name 192.168.0.3
Would you be interested in trying to implement this ? I'd be willing to
merge it since it's of more general use than your current patch. If so,
keep in mind that redispatch must be tested, as I'm not 100% confident
that it's OK in your case, though at first glance it looks OK.
> Second there seems to be no "macro" system to allow the value to be defined
> differently for each destination server.
Indeed. It will be much easier when we later support pattern extraction
and replacement in strings.
> 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.
The ID string also appears in logs, stats and CLI. But it should support any
name that is valid for an FQDN so you it should cause no trouble in your case.
> 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.
That's where it's important to test if redispatching works as expected.
> I also added a clause to the configuration.txt document with the new option
> and its behavior.
That's nice not to forget the doc :-)
> 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.
I'd be OK for the more general feature explained above, so if you can
tweak your patch to do this, I'm OK with merging it.
> The patch is attached and an example simple config is included.
I have a few comments about it :
+void http_change_host_header(struct http_txn *txn, struct http_msg *msg,
struct buffer *buf, char *hostname)
Just a minor note: prefer "const char *hostname" since the host name is passed
as a const from the caller.
+{
+ struct hdr_ctx ctx;
+ const char *hdr_val = "Host";
+ int hdr_len = 4;
You could have passed these ones directly as the function's argument
for better readability.
+ 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);
Warning: potential buffer overflow here if the host name in the conf is
larger than what can fit in the trash. Prefer snprintf() or even better,
a simple memcpy() since you know all the lengths.
+ hdr_val = trash;
+ hdr_len = strlen(hdr_val);
strlen() can be avoided here as sprintf() returns the length.
+ http_header_add_tail2(buf, msg, &txn->hdr_idx, hdr_val, hdr_len);
+
+ return;
+}
@@ -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)) {
It would be better to check for (s->be->options2 && PR_O2_HOST_SID) before
s->flags, because the former is less likely to be true and will save a test
in the most common case.
Thanks !
Willy