Mailing List Archive

[mod_backhand-users] Patch: Connection pool uses host-header
Hi,

as some might remember I am using mod_backhand to redistribute load to nt
servers. Doing so I dicovered something I'd call a bug in IIS, but maybe
it's only some unclearness in the RFCs. When you open a keep-alive
connection to a nt server configured to serve for different vhosts only the
first Host:-Header is important, subsequent Host-Headers will be ignored.
Looks like the connections are handed over to some sort of worker threads
which are specialized on a singular vhost. I have not tested if other
popular web server products expose the same behaviour.

An example:

First I connect to the nt server, queriing for a file on vhost1 and vhost2
using a different connection for each request. The file exists on vhost1 but
not on vhost2.

-----------------------------------------------------------
BigBrother src/mod_backhand# telnet nt-server 80
Trying 1.2.3.4...
Connected to nt-server.
Escape character is '^]'.
GET /test.asp HTTP/1.1
Host: vhost1
Connection: Close

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Tue, 30 Oct 2001 00:34:11 GMT
Connection: close
Content-Length: 1078
Content-Type: text/html
Set-Cookie: ASPSESSIONIDQQGGGXIQ=GKDJOIFBIFACIACKEEPHCEDO; path=/
Cache-control: private

[...]

-----------------------------------------------------------
BigBrother src/mod_backhand# telnet nt-server 80
Trying 1.2.3.4...
Connected to nt-server.
Escape character is '^]'.
GET /test.asp HTTP/1.1
Host: vhost2
Connection: Close

HTTP/1.1 404 Object Not Found
Server: Microsoft-IIS/5.0
Date: Tue, 30 Oct 2001 00:35:33 GMT
Connection: close
Content-Type: text/html
Content-Length: 3247

[...]
-----------------------------------------------------------

Now I do the same expect using on single connection for both requests:

-----------------------------------------------------------
BigBrother src/mod_backhand# telnet nt-server 80
Trying 1.2.3.4...
Connected to nt-server.
Escape character is '^]'.
GET /test.asp HTTP/1.1
Host: vhost1
Connection: Keep-Alive

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Tue, 30 Oct 2001 00:29:04 GMT
Content-Length: 1093
Content-Type: text/html
Set-Cookie: ASPSESSIONIDQQGGGXIQ=DFDJOIFBHLPMKMAMJCPBMCKG; path=/
Cache-control: private

[...]
GET /test.asp HTTP/1.1
Host: vhost2
Connection: Keep-Alive

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Tue, 30 Oct 2001 00:29:33 GMT
Content-Length: 1089
Content-Type: text/html
Set-Cookie: ASPSESSIONIDQQGGGXIQ=HFDJOIFBDELILDNFEKCNHKDL; path=/
Cache-control: private

[...]

-----------------------------------------------------------

As you can see I got a 200 OK for both requests this time (and the file
normally served by vhost1 as you may have guessed)

There are some solutions to this problem:
1. You can turn off Keep-Alives on the target server
2. You can turn off Connection-Pooling in mod_backhand
3. You can make mod_backhand only reuse connections for requests with the
same host-header.

While 1.-2. are easy to configure they may result in a loss of performance
3. would be the best solution in my eyes, so I prepeared a patch for this.
Maybe you can habe a look at it.

Thank you,

Stefan

BigBrother src/mod_backhand# cvs diff -u
cvs server: Diffing .
Index: back_util.c
===================================================================
RCS file: /storage/cvs/jesus/mod_backhand/back_util.c,v
retrieving revision 1.17
diff -u -r1.17 back_util.c
--- back_util.c 2001/10/30 19:53:47 1.17
+++ back_util.c 2001/11/01 22:04:49
@@ -44,6 +44,7 @@
typedef struct {
int fd;
int pid;
+ char host[100];
} aConn;

static aConn connection_pool[MAXSERVERS][MAXSESSIONSPERSERVER];
@@ -106,11 +107,12 @@
}
return nsd;
}
-static int reserve_session(struct in_addr *request, pid_t pid) {
+static int reserve_session(struct in_addr *request, pid_t pid, char
*hostheader) {
int i,j,rfd=-1;
if((i=find_server(request))==-1) return -1;
for(j=0;j<MAXSESSIONSPERSERVER && rfd<0;j++)
- if(connection_pool[i][j].fd>=0) {
+ if(connection_pool[i][j].fd>=0 &&
+ !strncmp(connection_pool[i][j].host, hostheader, 100)) {
rfd=connection_pool[i][j].fd;
/* do this because keeping track is REALLY complicated */
connection_pool[i][j].fd=-1;
@@ -127,7 +129,7 @@
}
return rfd;
}
-void replace_session(struct in_addr *submission, int fd) {
+void replace_session(struct in_addr *submission, int fd, char *hostheader)
{
int i,j;
if((i=find_server(submission))==-1) {
if(loglevel & MBLL_NET3) {
@@ -142,6 +144,7 @@
if(connection_pool[i][j].fd<0) {
connection_pool[i][j].fd=fd;
connection_pool[i][j].pid=1;
+ strncpy(connection_pool[i][j].host, hostheader, 100);
return;
}
if(loglevel & MBLL_NET3) {
@@ -586,6 +589,7 @@
char chreq[512]; /* Child request */
int nread;
struct in_addr servreq;
+ char hostheader[100];
if((nread = read(children[i].fd, chreq,
MBCSP_REQ_SIZE))<0) {
if(loglevel & MBLL_NET2) {
@@ -616,10 +620,12 @@
} else {
/* Child is giving us or asking for a session */
memcpy(&servreq, chreq+1, sizeof(struct in_addr));
+ strncpy(hostheader, chreq+1+sizeof(struct in_addr), 100);
+ hostheader[99] = 0; /* terminate string */
if(*chreq == MBCSP_GET) {
/* We need to send them a session */
int reqfd;
- reqfd = reserve_session(&servreq, children[i].pid);
+ reqfd = reserve_session(&servreq, children[i].pid,
hostheader);
send_fd(children[i].fd, reqfd);
close(reqfd);
if(loglevel & MBLL_NET1) {
@@ -635,7 +641,7 @@
"mod_backhand: Receiving back fd(??).");
}
if((recvdfd = recv_fd(children[i].fd))>0)
- replace_session(&servreq, recvdfd);
+ replace_session(&servreq, recvdfd, hostheader);
if(loglevel & MBLL_NET1) {
ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL,
"mod_backhand: Received back fd(%d).",
Index: back_util.h
===================================================================
RCS file: /storage/cvs/jesus/mod_backhand/back_util.h,v
retrieving revision 1.5
diff -u -r1.5 back_util.h
--- back_util.h 2001/04/26 22:57:36 1.5
+++ back_util.h 2001/11/01 22:04:50
@@ -45,5 +45,10 @@
#define MBCSP_GET 0
#define MBCSP_PUT 1
#define MBCSP_CLOSE 2
-#define MBCSP_REQ_SIZE (1+sizeof(struct in_addr))
+/*
+ 1 byte op code (MBCSP_GET|MBCSP_PUT|MPCSP_CLOSE
+ sizeof(struct in_addr) bytes in_addr
+ 100 bytes host header
+*/
+#define MBCSP_REQ_SIZE (1+sizeof(struct in_addr)+100)
#endif
Index: mod_backhand.c
===================================================================
RCS file: /storage/cvs/jesus/mod_backhand/mod_backhand.c,v
retrieving revision 1.44
diff -u -r1.44 mod_backhand.c
--- mod_backhand.c 2001/10/02 19:18:58 1.44
+++ mod_backhand.c 2001/11/01 22:04:56
@@ -623,9 +623,15 @@
if(usepool == MBCP_OFF) {
mbcs = new_session(&serverstats[remote_machine.id].contact);
} else {
+ char *hostheader;
memcpy(mbcsp_request+1,
&serverstats[remote_machine.id].contact.sin_addr,
sizeof(struct in_addr));
+ mbcsp_request[1+sizeof(struct in_addr)] = 0;
+ if (hostheader = ap_table_get(r->headers_in, "Host"))
+ strncpy(mbcsp_request+1+sizeof(struct in_addr),
+ hostheader, 100);
+ mbcsp_request[MBCSP_REQ_SIZE-1] = 0; /* Force termination */
mbcsp_request[0] = MBCSP_GET;
/* Apparently this next stanza can hang on BSD?! */
recvfd_failed = 1;
[mod_backhand-users] Patch: Connection pool uses host-header [ In reply to ]
On Thursday, November 1, 2001, at 05:09 PM, Seufert, Stefan wrote:
> as some might remember I am using mod_backhand to redistribute load to
> nt
> servers. Doing so I dicovered something I'd call a bug in IIS, but maybe
> it's only some unclearness in the RFCs. When you open a keep-alive
> connection to a nt server configured to serve for different vhosts only
> the
> first Host:-Header is important, subsequent Host-Headers will be
> ignored.
> Looks like the connections are handed over to some sort of worker
> threads
> which are specialized on a singular vhost. I have not tested if other
> popular web server products expose the same behaviour.

As the RFC states that the Host: header should dictate the virtual host
that is serviced, this behaviour is horribly non-compliant. This is
nasty.

I do applaud your patch. I will look at integrating it as it shouldn't
break anything. I will have to wrap it with a clause so that the
workaround is only applied if the servers are "brain-damaged." Perhaps:

BackhandBrainDamagedHostHeaders (on|off)

This would be a per-directory, inherited configuration option. This
will allow only doing this for /vbasps/ only that directoy backhands to
NT IIS servers.

Thoughts?

--
Theo Schlossnagle
1024D/82844984/95FD 30F1 489E 4613 F22E 491A 7E88 364C 8284 4984
2047R/33131B65/71 F7 95 64 49 76 5D BA 3D 90 B9 9F BE 27 24 E7
[mod_backhand-users] Patch: Connection pool uses host-header [ In reply to ]
> break anything. I will have to wrap it with a clause so that the
> workaround is only applied if the servers are
> "brain-damaged." Perhaps:
>
> BackhandBrainDamagedHostHeaders (on|off)
>
> This would be a per-directory, inherited configuration option. This
> will allow only doing this for /vbasps/ only that directoy
> backhands to NT IIS servers.
>
> Thoughts?

Great... thought about this myself but was too lazy to find out how that
config stuff works.

Have a nice evening,

Stefan