views:

164

answers:

2

I'm trying to connect to a web service using IO::Socket::INET (yes, I know that there are lots of better modules for doing this, but I don't have them and can't add them, so please don't suggest it), but I'm timing out (I think that's what it's doing) waiting for a response.

Here's the basic crux of my code (I previously populate the content with all the proper headers, and set it up, etc):

$httpSock->print($content);

my @lines = $httpSock->getlines();
foreach my $line ( @lines ) {
    print $line;
}

It appears that my request is made immediately, then it waits about 2 minutes before spitting back the response. If I alter the code to use a raw socket recv instead of getlines(), ala:

$httpSock->recv($data, 1024);

I get the response immediately (although only the first 1024 chars). Am I doing something wrong here? I'm using a late enough version of IO::Socket that autoflush should be enabled by default, but turning it on explicitly didn't seem to make any difference. I could probably also just keep reading from the socket until I got the entire response, but that's definitely messier than using getlines() or <$httpSock>.

Thanks in advance.

+1  A: 

I'm having an issue re-creating the problem with the code snippet you've posted. Here's the code I tested with:

use strict;
use warnings;
use IO::Socket;

my $httpSock = new IO::Socket::INET(
    PeerAddr => 'www.google.com',
    PeerPort => '80',
    Proto    => 'tcp',
);

my $content = "HEAD / HTTP/1.0\r\nHost: www.google.com\r\n\r\n";

$httpSock->print($content);
my @lines = $httpSock->getlines();
foreach my $line (@lines) {
    print $line;
}

Here are the results:

$ time ./1.pl
HTTP/1.0 200 OK
-snip-  

real    0m0.084s
user    0m0.025s
sys 0m0.007s
Mike Wade
Yeah, that's pretty close to what I have. Further investigation leads me to believe it's an EOF issue of some kind. If I use `getline()` (in a loop) it grabs all but the last line. If I use `recv()` it will get all the data except the last bit. Does that help at all?
Morinar
I'm also performing a POST rather than a HEAD or a GET. Not sure if that makes any difference...
Morinar
Your example also works for me, however if you change HTTP/1.0 to HTTP/1.1 (which is what I was using) your example hangs just as mine. What is the difference here between HTML/1.0 and HTML/1.1?
Morinar
My answer: http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html. HTTP/1.1 has and assumes a persistent connection. If I add the `Connection: close` header, HTTP/1.1 performs as expected in this case.
Morinar
Yes, this is exactly it. As for modules -- you really don't even have LWP on your system? LWP gives you an HTTP protocol parser that will take care of all of this for you, including reading the right amount of body when there's a persistent connection.
hobbs
+1  A: 

The problem is that getlines() waits until the connection is closed. If the web service you are connecting to doesn't close your connection, the getlines function will wait, thinking more data is on the way. When your connection times out after those 2 minutes or so, getlines is seeing the connection close, and returning the lines it received to you. Recv on the other hand will grab everything up to the predetermined limit that is on the connection at that time and return it to the buffer you hand it immediately, but it will wait until it gets some data if there is none currently. I know you think its messy, but this might work out for you:

$httpSock->recv($buf, 1024);
$message = "";
while (length($buf) > 0) {
    $message .= $buf;
    $httpSock->recv($buf, 1024, MSG_DONTWAIT);
}
print $message;

The MSG_DONTWAIT will cause recv to not wait for a message if the connection is empty. You can also increase 1024 to some big number to decrease the number of loops, or even possibly even get the whole message at once.

This should also let you keep the sockets open for further use until you close it yourself.

I am wondering if the google example works because google.com is closing the connection after it responds.

FModa3
Some good thoughts in there, but please see my comments in the other answer for the "real" problem/solution.
Morinar