Home > Archive > ithreads > June 2007 > Threads mess with simple HTTP daemon
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
Threads mess with simple HTTP daemon
|
|
| Arno Velds 2007-06-14, 8:18 am |
| I've been working on a simple http server that keeps a pool of threads
around that do multiple crops on an image.
It's a very simple blocking webserver that handles 1 request per
connection. It uses a Thread::Queue::Multiplex (TQM) to assign the
request to the corresponding image cropping thread using the
enqueue_and_wait function. The image threads use the dequeue_until
function to limit their lifetime.
After a connection is made and the url is parsed it first checks with
the TQM for a listener with that image id. If it doesn't exist it will
immediately detach a new thread for that image and sleep for a couple
of seconds. The image thread registers as a listener with the TQM and
serves a tiny jpg image as a response to the TQM.
This works perfectly fine except that the request following the thread
starting request from the same client is never answered. Subsequent
requests as well as requests from other clients are handled without
any problems. The strange thing is that the browser will wait until
the image thread finishes and then re-requests the image. If you
tunnel the request through an apache proxy (as I intend to do) it
raises an error. So it seems somehow this thread starting messes with
the (HTTP, TCP/IP?) connection.
I've looked a little bit at the network traffic using wireshark and
did notice that the first request never exchanges a FIN, ACK with the
server. But that stuff is way over my head :-)
I cleaned up my program a bit and pasted it below. It now just tosses
around text strings, but it still shows the same behavior on my
computer (i386 Ubunbtu feisty, perl 5.8.8). It starts a local server
on port 8080 and handles urls in the form of http://localhost/<number>/<number>
.. You can start the program, do a request from your browser and wait
for the answer, then press the reload button. The browser will wait
until the image thread times out (40 seconds), then re-request.
Do you think this is a bug? Should I report this somewhere?
BTW, I worked around this problem by creating a thread starting thread
that listens to another queue so no more threads are started from
within the connection loop. This works fine, but it is a bit of a
hack!
Regards, Arno
Example program:
#!/usr/bin/perl
use strict;
use warnings;
use threads;
use Thread::Queue::Multiplex;
use HTTP::Daemon;
use HTTP::Response;
use HTTP::Status;
$|=1;
my $requestqueue_store = new
Thread::Queue::Multiplex(ListenerRequire
d=>0);
my $d = HTTP::Daemon->new(Listen=>10,LocalPort=>8080, ReuseAddr=>1) ||
die;
print "Please contact me at: <URL:", $d->url, ">\n";
while (my $c = $d->accept) {
print "connection!\n";
while(my $r = $c->get_request) {
$c->force_last_request;
if ($r->method eq 'GET' and $r->url->path =~ m/\/(\d+)\/(\d+)\/
{0,1}$/) {
my $hybid = $1;
my $featureid = $2;
print "W-hybid = $hybid, featureid = $featureid\n";
#do we hava a running thread for this hyb?
my @subids = $requestqueue_store->get_subscribers();
if((grep {$_ == $hybid} @subids) == 0) {
#spawn new thread
print "W- Creating new image thread...";
threads->create("image_thread", $hybid)->detach;
sleep 2;
}
#queue request and wait for response
my $img_resp = $requestqueue_store->enqueue_and_wait($hybid,
$featureid);
my $img = $img_resp->{$hybid}->[0];
my $resp = new HTTP::Response(200);
$resp->content($img);
$resp->header('content_type'=>'text/html',
data_lenght=>length($img));
print "W-Sending data\n";
$c->send_response($resp);
}
else {
$c->send_error(RC_FORBIDDEN)
}
}
print "W- Closing connection\n";
$c->close();
}
sub image_thread {
my $hybid = shift;
$requestqueue_store->subscribe($hybid);
my $tid = threads->self->tid;
print "I-($tid) Testthread for $hybid...";
print "I-($tid) is Waiting\n";
while(my $msg = $requestqueue_store->dequeue_until(40)) {
if(not defined $msg) {
print "I-($tid) inactive for 40\n";
last;
}
my $id = $msg->[0];
my $featureid = $msg->[1];
print "I-($tid)-Got request for $featureid, msgid=$id\n";
my $response = "You requested $featureid, and I ($hybid) served!\n";
#queue the response
$requestqueue_store->respond($id, $response);
my $response = "Answer sent";
}
print "I-($tid) Exiting thread\n";
print "I-($tid) cleaned up modules, deregister listener\n";
$requestqueue_store->unsubscribe();
print "I-($tid) all done...exiting\n";
}
| |
|
|
| Jerry D. Hedden 2007-06-14, 7:19 pm |
| On Arno Velds wrote:
> This works perfectly fine except that the request following the thread
> starting request from the same client is never answered.
If you remove the line:
$c->force_last_request;
then it works as advertised.
| |
| Arno Velds 2007-06-15, 8:19 am |
| On 14 jun, 20:41, jdhed...@cpan.org (Jerry D. Hedden) wrote:
> On Arno Velds wrote:
>
> If you remove the line:
> $c->force_last_request;
> then it works as advertised.
Thanks for thinking along. You are correct, but this has some
di vantages. Standard firefox keepalive time is 300 seconds. So
unless you use a threaded webserver implementation this will block all
other incoming requests. I obviously tried this, but performance is
lower than allowing only one request per connection.
As I mentioned in my somewhat lengthy post I have already worked
around the problem by not starting new threads from the connection
loop. All I would like to know is that if I want to be a HTTP/1.0
server and only serve 1 request per connection why isn't the second
request from the same client answered even though they can be 20
seconds apart and separated by a $c->close on the server side. Please
bear in mind that the second request is not being discarded by the $c-
>force_last_request; because at that time it hasn't even been sent!
Maybe you can try my example and see for yourself.
Cheers,
Arno (The Netherlands)
| |
| Jerry D. Hedden 2007-06-15, 7:25 pm |
| Arno Velds wrote:
> As I mentioned in my somewhat lengthy post I have already worked
> around the problem by not starting new threads from the connection
> All I would like to know is that if I want to be a HTTP/1.0
> server and only serve 1 request per connection why isn't the second
> request from the same client answered even though they can be 20
> seconds apart and separated by a $c->close on the server side.
If you want to be HTTP/1.0, then you need to tell the browser that.
You do this by setting:
$HTTP::Daemon::PROTO = "HTTP/1.0";
This is then sent with the response so that the browser knows you're
not going to service another request on this socket.
| |
| Arno Velds 2007-06-15, 7:25 pm |
| On 15 jun, 15:10, jdhed...@cpan.org (Jerry D. Hedden) wrote:
> Arno Velds wrote:
>
> If you want to be HTTP/1.0, then you need to tell the browser that.
> You do this by setting:
> $HTTP::Daemon::PROTO = "HTTP/1.0";
> This is then sent with the response so that the browser knows you're
> not going to service another request on this socket.
Check....Now I feel silly....
Still, I don't even remotely understand why the exiting of the image
thread has any relation with the browser timeout on this weird form of
HTTP/1.0 - HTTP/1.1 miscommunication. Oh well...
Thanks!
| |
| Jerry D. Hedden 2007-06-15, 7:25 pm |
| Arno Velds wrote:
> Still, I don't even remotely understand why the exiting of the image
> thread has any relation with the browser timeout on this weird form of
> HTTP/1.0 - HTTP/1.1 miscommunication.
It's because the thread still has the socket open. If you pass the
socket to the thread:
threads->create("image_thread", $hybid, $c)->detach;
And then close it in the thread:
sub image_thread {
my $hybid = shift;
my $socket = shift;
$socket->close();
...
You don't get that problem.
| |
|
|
|
|
|