Hi Theo, hi list,
it's definitley a long time since my last post... but i promised you to send
you the perl script which i used to fool a backhand moderator to proxy
requests to a NT server... and here it is (someone asked for it a few weeks
ago)
---cut here---
#!/usr/bin/perl
use Socket;
use Sys::Hostname;
$iaddr = gethostbyname('0.0.0.0');
$proto = getprotobyname('udp');
$paddr = sockaddr_in(4445, $iaddr);
socket(SOCKET, PF_INET, SOCK_DGRAM, $proto) || die "socket: $!";
$hisiaddr = inet_aton('192.168.0.1') || die "unknown host";
$hispaddr = sockaddr_in(4445, $hisiaddr);
while(1) {
$buf = pack("a40N*", 'my.server.de', 0, 80, 3232235522, 0, 0,
414309, 0, 0, 80, 2097152000, 1000, 1, 131002368, 101639360,
0, 0);
defined(send(SOCKET, $buf, 0, $hispaddr)) || warn "send $host:
$!";
sleep 1;
}
---cut here---
Most of it is ripped from the perlipc manpage and the for pack() are those
i've recorded before for a mod_backhand broadcast. If you like to try this
script... simply adjust the ip address in line 11 to the adress you want to
send to (the mod_backhand amchine) and replace 3232235522 with the ip of the
machine you are simulating the broadcasts for... (3232235522 is 192.168.0.2:
192*256*256*256 + 168*256*256 + 0*256 + 2 = 3232235522)
Yes, i agree, this is very very ugly... but it's done it's job pretty good
for a long time and since it's pretty short I used as sort of introduction
example before i present to you the real mad script:
---cut here---
use Win32::PerfLib;
use Socket;
use Sys::Hostname;
my $server = "";
my $system_obj = 2;
my $cpu_id = 240;
## if the defaults defined above do not work you may uncomment the
## following lines to rederterminate the correct ids
#Win32::PerfLib::GetCounterNames($server, \%counter);
## reverse hash
#%r_counter = map { $counter{$_} => $_ } keys %counter;
## retrieve the id for process object
#$system_obj = $r_counter{'System'};
## retrieve the id for the process ID counter
#$cpu_id = $r_counter{'% Total Processor Time'};
# create udp socket
$iaddr = gethostbyname(hostname());
$proto = getprotobyname('udp');
$paddr = sockaddr_in(0, $iaddr);
socket(SOCKET, PF_INET, SOCK_DGRAM, $proto) || die "socket: $!";
# our moderator we are broadcasting to
$hisiaddr = inet_aton('192.168.0.1') || die "unknown host";
$hispaddr = sockaddr_in(4445, $hisiaddr);
# create connection to $server
$perflib = new Win32::PerfLib($server);
$proc_ref = {};
$proc_ref2 = {};
# get the performance data for the system object
$perflib->GetObjectList($system_obj, $proc_ref);
while(1) {
sleep 3;
$perflib->GetObjectList($system_obj, $proc_ref2);
$perflib->Close();
$counter_ref=$proc_ref->{Objects}->{$system_obj}->{Counters};
$counter_ref2=$proc_ref2->{Objects}->{$system_obj}->{Counters};
foreach $i (keys %{$counter_ref}) {
if ($counter_ref->{$i}->{CounterNameTitleIndex} == $cpu_id)
{
$Numerator0 = $counter_ref->{$i}->{Counter};
$Denominator0 = $proc_ref->{PerfTime100nSec};
$Numerator1 = $counter_ref2->{$i}->{Counter};
$Denominator1 = $proc_ref2->{PerfTime100nSec};
$idle = ((($Numerator1 - $Numerator0) /
($Denominator1 - $Denominator0 ))) * 1000;
}
}
$buf = pack("a40N*", hostname(), 0, 80, unpack('N', $iaddr),
0, 0, 414309, 0, 0, 80, 2097152000, $idle,
1, 131002368, 101639360, 0, 0);
defined(send(SOCKET, $buf, 0, $hispaddr)) || warn "send $host: $!";
$proc_ref = $proc_ref2;
$proc_ref2 = {};
$perflib->GetObjectList($system_obj, $proc_ref2);
}
---cut here---
As the "use Win32" staement suggests... yes, this script is intended to run
under windows... it queries the performance counters to get the current cpu
load (System/Total Processor Time) and sends the information to the
moderator. Pretty easy, huh? Works with ActivePerl and WinNT; Haven't tested
with Win2000 or other perl ports so far. Of course it would be a much better
deal to do this in C and I'm looking forward to see the implemention someone
has announced because this perl variant is inaccurate and consumes too much
resources on the web server.
Back to mod_backhand itself... i've now been running (and I still am) the
following combination "Apache/1.3.19 (Unix) mod_gzip/1.3.19.1a
balanced_by_mod_backhand/1.1.1pre4" for some months. The Linux server
accepts the requests from the internet proxies them to an nt server using
mod_backhand and compresses the results with mod_gzip. Now I am installing a
second nt server and a second moderator because the two old servers are no
longer able to handle the load. Having played around with the
mod_gzip/mod_backhand combination for serveral days now, i've got the
following questions:
1) bySession
Quoted from builtins.c:
/* Note: this won't work if the client sends more than one cookie.
* I've tested mozilla M18, sends one cookie header containing all
* variables that have been set via cookies. Hope this is standard
* and all the other clients act the same way.
*/
This is a bit confusing to me... when I read this comment I understand the
following "if more than one cookie is set bySession will not work." But when
I read the code it tells me "It will work aslong as the browser returns all
cookies in one single line" (which I can confirm for MSIE and Opera, too).
So, what is the rigth interpretation?
2) HTTP Status Code 100 - continue
For some very strange reasons the IIS likes "100 - continue" responses.
According to the rfc the client has to ignore this response and must wait
for another http header. However this seems to be causing problems with
mod_backhand since mod_backhand sends the 100-continue response to the
client and closes the connection immediately. The user agent which expected
another header after the 100-continue is "very surprised" about this and
displays an error message. I patched my 1.1.1pre4 to simply read another
header when it recieves an 100-continue from the server it proxies to. The
way it is implemented is not very nice and it has most probably a memory
leak, however it works, it even works with mod_backhand1.2.0:
(patch against mod_backhand1.2.0)
---cut here---
@@ -710,6 +710,9 @@
ap_clear_table(r->headers_out);
rv = b_get_mime_headers_out(myconn, r,
&myconn_headers_rcvd, &cookies_rcvd);
+ if(r->status == 100) //reread if 100;
+ rv = b_get_mime_headers_out(myconn, r,
+ &myconn_headers_rcvd, &cookies_rcvd);
ap_overlap_tables(r->headers_out, myconn_headers_rcvd,
AP_OVERLAP_TABLES_SET);
if(!ap_is_empty_table(cookies_rcvd)) {
---cut here---
In the changelog for 1.2.0 there is the following entry: "Fixed RFC
compliance issue with HTTP response codes that are 304, 204, and less that
200. They are explicitly defined to have no content length". Does this the
same problem the three lines above do? A quick grep of the source code told
me there are not other handlers for http 100 status codes but i may be
wrong... Any comments on that?
3) ap_is_initial_req()
Again this is a patch i first used against mod_backhand_1.1.1pre4 and again
it is neccessary to make mod_backhand1.2.0 work as expected. As I've
explained before I use mod_gzip to compress the content before sending it
back to the user. having mod_gzip in the modules chain before mod_backhand
ap_is_inital_req(r) is no longer true for any request mod_backhand gets...
so I had to comment out the if block as shown in the patch below. Again this
most probaly is very bad style because I'm sure you had your reasons to but
it in... Is there a clean solution for that problem?
---cut here---
@@ -1247,7 +1250,7 @@
}
}
} else {
- if(ap_is_initial_req(r)) {
+// if(ap_is_initial_req(r)) {
request_rec *subr;
mbcfg *tmpcfg;
ap_table_setn(r->notes, "BackhandProxyRequest", "YES");
@@ -1270,10 +1273,10 @@
}
ap_destroy_sub_req(subr);
return OK;
- } else {
+/* } else {
ap_table_setn(r->notes, "BackhandProxyRequest", "NO");
ap_destroy_sub_req(subr);
- }
+ }*/
}
}
if(loglevel & MBLL_DCSN1) {
---cut here---
4) Graceful restart causes problems
I think the answer to this question is hidden somewhere in my patches i
presented above... when ia ask apache to do a graceful restart some childs
(about 1-3 of 200) refuse to kill themselves but start consuming very high
amounts of memory and cpu. Do you see any reasons why I am experiencing this
strange behaviour?
Thank you in advance
Stefan
This is the complete patch:
--- mod_backhand-1.2.0/mod_backhand.c Wed May 30 23:05:17 2001
+++ mod_backhand-1.2.0.patched/mod_backhand.c Tue Sep 18 19:03:29 2001
@@ -710,6 +710,9 @@
ap_clear_table(r->headers_out);
rv = b_get_mime_headers_out(myconn, r,
&myconn_headers_rcvd, &cookies_rcvd);
+ if(r->status == 100) //reread if 100;
+ rv = b_get_mime_headers_out(myconn, r,
+ &myconn_headers_rcvd, &cookies_rcvd);
ap_overlap_tables(r->headers_out, myconn_headers_rcvd,
AP_OVERLAP_TABLES_SET);
if(!ap_is_empty_table(cookies_rcvd)) {
@@ -1247,7 +1250,7 @@
}
}
} else {
- if(ap_is_initial_req(r)) {
+// if(ap_is_initial_req(r)) {
request_rec *subr;
mbcfg *tmpcfg;
ap_table_setn(r->notes, "BackhandProxyRequest", "YES");
@@ -1270,10 +1273,10 @@
}
ap_destroy_sub_req(subr);
return OK;
- } else {
+/* } else {
ap_table_setn(r->notes, "BackhandProxyRequest", "NO");
ap_destroy_sub_req(subr);
- }
+ }*/
}
}
if(loglevel & MBLL_DCSN1) {
it's definitley a long time since my last post... but i promised you to send
you the perl script which i used to fool a backhand moderator to proxy
requests to a NT server... and here it is (someone asked for it a few weeks
ago)
---cut here---
#!/usr/bin/perl
use Socket;
use Sys::Hostname;
$iaddr = gethostbyname('0.0.0.0');
$proto = getprotobyname('udp');
$paddr = sockaddr_in(4445, $iaddr);
socket(SOCKET, PF_INET, SOCK_DGRAM, $proto) || die "socket: $!";
$hisiaddr = inet_aton('192.168.0.1') || die "unknown host";
$hispaddr = sockaddr_in(4445, $hisiaddr);
while(1) {
$buf = pack("a40N*", 'my.server.de', 0, 80, 3232235522, 0, 0,
414309, 0, 0, 80, 2097152000, 1000, 1, 131002368, 101639360,
0, 0);
defined(send(SOCKET, $buf, 0, $hispaddr)) || warn "send $host:
$!";
sleep 1;
}
---cut here---
Most of it is ripped from the perlipc manpage and the for pack() are those
i've recorded before for a mod_backhand broadcast. If you like to try this
script... simply adjust the ip address in line 11 to the adress you want to
send to (the mod_backhand amchine) and replace 3232235522 with the ip of the
machine you are simulating the broadcasts for... (3232235522 is 192.168.0.2:
192*256*256*256 + 168*256*256 + 0*256 + 2 = 3232235522)
Yes, i agree, this is very very ugly... but it's done it's job pretty good
for a long time and since it's pretty short I used as sort of introduction
example before i present to you the real mad script:
---cut here---
use Win32::PerfLib;
use Socket;
use Sys::Hostname;
my $server = "";
my $system_obj = 2;
my $cpu_id = 240;
## if the defaults defined above do not work you may uncomment the
## following lines to rederterminate the correct ids
#Win32::PerfLib::GetCounterNames($server, \%counter);
## reverse hash
#%r_counter = map { $counter{$_} => $_ } keys %counter;
## retrieve the id for process object
#$system_obj = $r_counter{'System'};
## retrieve the id for the process ID counter
#$cpu_id = $r_counter{'% Total Processor Time'};
# create udp socket
$iaddr = gethostbyname(hostname());
$proto = getprotobyname('udp');
$paddr = sockaddr_in(0, $iaddr);
socket(SOCKET, PF_INET, SOCK_DGRAM, $proto) || die "socket: $!";
# our moderator we are broadcasting to
$hisiaddr = inet_aton('192.168.0.1') || die "unknown host";
$hispaddr = sockaddr_in(4445, $hisiaddr);
# create connection to $server
$perflib = new Win32::PerfLib($server);
$proc_ref = {};
$proc_ref2 = {};
# get the performance data for the system object
$perflib->GetObjectList($system_obj, $proc_ref);
while(1) {
sleep 3;
$perflib->GetObjectList($system_obj, $proc_ref2);
$perflib->Close();
$counter_ref=$proc_ref->{Objects}->{$system_obj}->{Counters};
$counter_ref2=$proc_ref2->{Objects}->{$system_obj}->{Counters};
foreach $i (keys %{$counter_ref}) {
if ($counter_ref->{$i}->{CounterNameTitleIndex} == $cpu_id)
{
$Numerator0 = $counter_ref->{$i}->{Counter};
$Denominator0 = $proc_ref->{PerfTime100nSec};
$Numerator1 = $counter_ref2->{$i}->{Counter};
$Denominator1 = $proc_ref2->{PerfTime100nSec};
$idle = ((($Numerator1 - $Numerator0) /
($Denominator1 - $Denominator0 ))) * 1000;
}
}
$buf = pack("a40N*", hostname(), 0, 80, unpack('N', $iaddr),
0, 0, 414309, 0, 0, 80, 2097152000, $idle,
1, 131002368, 101639360, 0, 0);
defined(send(SOCKET, $buf, 0, $hispaddr)) || warn "send $host: $!";
$proc_ref = $proc_ref2;
$proc_ref2 = {};
$perflib->GetObjectList($system_obj, $proc_ref2);
}
---cut here---
As the "use Win32" staement suggests... yes, this script is intended to run
under windows... it queries the performance counters to get the current cpu
load (System/Total Processor Time) and sends the information to the
moderator. Pretty easy, huh? Works with ActivePerl and WinNT; Haven't tested
with Win2000 or other perl ports so far. Of course it would be a much better
deal to do this in C and I'm looking forward to see the implemention someone
has announced because this perl variant is inaccurate and consumes too much
resources on the web server.
Back to mod_backhand itself... i've now been running (and I still am) the
following combination "Apache/1.3.19 (Unix) mod_gzip/1.3.19.1a
balanced_by_mod_backhand/1.1.1pre4" for some months. The Linux server
accepts the requests from the internet proxies them to an nt server using
mod_backhand and compresses the results with mod_gzip. Now I am installing a
second nt server and a second moderator because the two old servers are no
longer able to handle the load. Having played around with the
mod_gzip/mod_backhand combination for serveral days now, i've got the
following questions:
1) bySession
Quoted from builtins.c:
/* Note: this won't work if the client sends more than one cookie.
* I've tested mozilla M18, sends one cookie header containing all
* variables that have been set via cookies. Hope this is standard
* and all the other clients act the same way.
*/
This is a bit confusing to me... when I read this comment I understand the
following "if more than one cookie is set bySession will not work." But when
I read the code it tells me "It will work aslong as the browser returns all
cookies in one single line" (which I can confirm for MSIE and Opera, too).
So, what is the rigth interpretation?
2) HTTP Status Code 100 - continue
For some very strange reasons the IIS likes "100 - continue" responses.
According to the rfc the client has to ignore this response and must wait
for another http header. However this seems to be causing problems with
mod_backhand since mod_backhand sends the 100-continue response to the
client and closes the connection immediately. The user agent which expected
another header after the 100-continue is "very surprised" about this and
displays an error message. I patched my 1.1.1pre4 to simply read another
header when it recieves an 100-continue from the server it proxies to. The
way it is implemented is not very nice and it has most probably a memory
leak, however it works, it even works with mod_backhand1.2.0:
(patch against mod_backhand1.2.0)
---cut here---
@@ -710,6 +710,9 @@
ap_clear_table(r->headers_out);
rv = b_get_mime_headers_out(myconn, r,
&myconn_headers_rcvd, &cookies_rcvd);
+ if(r->status == 100) //reread if 100;
+ rv = b_get_mime_headers_out(myconn, r,
+ &myconn_headers_rcvd, &cookies_rcvd);
ap_overlap_tables(r->headers_out, myconn_headers_rcvd,
AP_OVERLAP_TABLES_SET);
if(!ap_is_empty_table(cookies_rcvd)) {
---cut here---
In the changelog for 1.2.0 there is the following entry: "Fixed RFC
compliance issue with HTTP response codes that are 304, 204, and less that
200. They are explicitly defined to have no content length". Does this the
same problem the three lines above do? A quick grep of the source code told
me there are not other handlers for http 100 status codes but i may be
wrong... Any comments on that?
3) ap_is_initial_req()
Again this is a patch i first used against mod_backhand_1.1.1pre4 and again
it is neccessary to make mod_backhand1.2.0 work as expected. As I've
explained before I use mod_gzip to compress the content before sending it
back to the user. having mod_gzip in the modules chain before mod_backhand
ap_is_inital_req(r) is no longer true for any request mod_backhand gets...
so I had to comment out the if block as shown in the patch below. Again this
most probaly is very bad style because I'm sure you had your reasons to but
it in... Is there a clean solution for that problem?
---cut here---
@@ -1247,7 +1250,7 @@
}
}
} else {
- if(ap_is_initial_req(r)) {
+// if(ap_is_initial_req(r)) {
request_rec *subr;
mbcfg *tmpcfg;
ap_table_setn(r->notes, "BackhandProxyRequest", "YES");
@@ -1270,10 +1273,10 @@
}
ap_destroy_sub_req(subr);
return OK;
- } else {
+/* } else {
ap_table_setn(r->notes, "BackhandProxyRequest", "NO");
ap_destroy_sub_req(subr);
- }
+ }*/
}
}
if(loglevel & MBLL_DCSN1) {
---cut here---
4) Graceful restart causes problems
I think the answer to this question is hidden somewhere in my patches i
presented above... when ia ask apache to do a graceful restart some childs
(about 1-3 of 200) refuse to kill themselves but start consuming very high
amounts of memory and cpu. Do you see any reasons why I am experiencing this
strange behaviour?
Thank you in advance
Stefan
This is the complete patch:
--- mod_backhand-1.2.0/mod_backhand.c Wed May 30 23:05:17 2001
+++ mod_backhand-1.2.0.patched/mod_backhand.c Tue Sep 18 19:03:29 2001
@@ -710,6 +710,9 @@
ap_clear_table(r->headers_out);
rv = b_get_mime_headers_out(myconn, r,
&myconn_headers_rcvd, &cookies_rcvd);
+ if(r->status == 100) //reread if 100;
+ rv = b_get_mime_headers_out(myconn, r,
+ &myconn_headers_rcvd, &cookies_rcvd);
ap_overlap_tables(r->headers_out, myconn_headers_rcvd,
AP_OVERLAP_TABLES_SET);
if(!ap_is_empty_table(cookies_rcvd)) {
@@ -1247,7 +1250,7 @@
}
}
} else {
- if(ap_is_initial_req(r)) {
+// if(ap_is_initial_req(r)) {
request_rec *subr;
mbcfg *tmpcfg;
ap_table_setn(r->notes, "BackhandProxyRequest", "YES");
@@ -1270,10 +1273,10 @@
}
ap_destroy_sub_req(subr);
return OK;
- } else {
+/* } else {
ap_table_setn(r->notes, "BackhandProxyRequest", "NO");
ap_destroy_sub_req(subr);
- }
+ }*/
}
}
if(loglevel & MBLL_DCSN1) {