Page 1 of 1

having trouble with fsockopen()

Posted: Sat Jan 10, 2009 12:48 am
by s.dot
Consider the following code:

Code: Select all

$request = 'site=' . $site . '&stats=' . urlencode($stats);
 
$headers = 'POST /parse.php HTTP/1.0' . "\r\n";
$headers .= 'Content-Type: application/x-www-form-urlencoded' . "\r\n";
$headers .= 'Content-Length: ' . strlen($request) . "\r\n\r\n";
 
//send request
if ($fileStream = fsockopen('www.example.com', 80, $errno, $errstr, 30))
{
    fwrite($fileStream, $headers . $request);
 
    $contents = '';
    while (!feof($fileStream))
    {
        //get the contents, 1kb at a time
        $contents .= fgets($fileStream, 1024);
    }
 
  echo $contents;
} else
{
    echo $errno . ': ' . $errstr;
}
I'm POSTing data to a remote page and reading the results and then echoing them.
$contents IS echoing, but the contents is a 404 page. I know for a fact that the script I'm POSTing to exists.

Hmmm?

Re: having trouble with fsockopen()

Posted: Sat Jan 10, 2009 1:31 am
by requinix
Try adding a Host header.

Code: Select all

$headers = 'POST /parse.php HTTP/1.0' . "\r\n";
$headers .= 'Host: http://www.example.com' . "\r\n";
$headers .= 'Content-Type: application/x-www-form-urlencoded' . "\r\n";
$headers .= 'Content-Length: ' . strlen($request) . "\r\n\r\n";

Re: having trouble with fsockopen()

Posted: Sat Jan 10, 2009 2:56 pm
by s.dot
Well now I get a 400 bad request 8O

Re: having trouble with fsockopen()

Posted: Sat Jan 10, 2009 9:48 pm
by requinix
Ah, hadn't noticed. When I put "www.example.com" in the first time the automatic URL parsing thing added the http://, and when I turned it off it was still there.

Code: Select all

$headers .= 'Host: www.example.com' . "\r\n";

Re: having trouble with fsockopen()

Posted: Mon Feb 02, 2009 2:32 pm
by s.dot
Thank you tasairis, you are a very knowledgeable person. ;) This worked beautifully.

But dang, I feel like a newbie again. I am doing many things which I've never done before. Now that I've POST'd data to the file stream, I am retrieving contents from the file stream using fgets() (as shown in the first post)

I am having a serialized array returned and the headers being output won't allow me to unserialize the content.

Is there a quick and easy way to remove the headers from the output without parsing the content (sounds like a dumb question) ?

If I do have to parse the content, is there a "no-fail" way to tell when the header output has ended? Seems kind of messy because the header information could be different on every request.

I know the serialized array that is output will always start with a: so perhaps I could search for that till the end of the string?

The output looks like this:

Code: Select all

HTTP/1.1 200 OK
Date: Mon, 02 Feb 2009 20:27:34 GMT
Server: Apache/2.2.6 (Fedora)
X-Powered-By: PHP/5.1.6
Content-Length: 958
Connection: close
Content-Type: text/html
 
a:1:{s:4:"........................................"}
EDIT| hmmm

Code: Select all

$contents = explode("\n", $contents);
$contents = array_pop($contents);
Since the serialized array is just one loooooooong string, it should always be the last element in the array, correct?

I am just concerned about different line endings. \n, \r, \r\n =/

Re: having trouble with fsockopen()

Posted: Mon Feb 02, 2009 2:57 pm
by requinix
It's a bit risky doing it that way.

First, this will be easier if you can use cURL. The problem is what the server may throw at you: gzipped data, quoted-printable encoding, chunked transfer... If you know that the server will always give you straight data without any funny business then it's okay, but if you don't know...
Then there's the question of whether the connection will close or not. If it doesn't then the script will read everything and then hang at the end until the connection times out. You can request a closed connection by sending a "Connection: close" header with the request.

Anyway, let's pretend everything's good in the world and you don't have to worry about that.

Headers are supposed to end with a \r\n, and headers stop at the first empty (just a \r\n) line, so I'd have something like

Code: Select all

if ($fileStream = fsockopen('www.example.com', 80, $errno, $errstr, 30))
{
    fwrite($fileStream, $headers . $request);
 
    // read in all the headers
    while (!feof($fileStream))
    {
        $line = fgets($fileStream);
        if ($line == "\r\n") break; // empty line = end of headers
    }
 
    // here is where the content begins
    $contents = '';
    while (!feof($fileStream))
    {
        $contents .= fgets($fileStream, 1024);
    }
 
    echo $contents;
}

Re: having trouble with fsockopen()

Posted: Mon Feb 02, 2009 3:05 pm
by s.dot
I can send a text/html header in the remote script so it renders in text always, right?

This is actually what I am doing now.

Code: Select all

//attempt to parse url and get headers from script url
if (($url = @parse_url($teArr['script_url'])) && ($scriptUrlHeaders = @get_headers($teArr['script_url'])))
{
    //we must have a 200 OK
    if (stripos($scriptUrlHeaders[0], '200 OK'))
    {
        //open a socket connection to $url['host']
        if ($fileStream = @fsockopen($url['host'], 80, $errno, $errstr, 30))
        {
            if (@fwrite($fileStream, $headers . $request))
            {
                //we will be generous and allow the script 90 seconds to return the data
                stream_set_blocking($fileStream, false);
                stream_set_timeout($fileStream, 90);
                            
                $contents = '';
                                
                while (!feof($fileStream))
                {
                    $contents .= fgets($fileStream, 1024);
                }
                            
                $contents = explode("\n", $contents);
                $contents = array_pop($contents);
                $contents = trim($contents);
                $contents = unserialize($contents);
                                
                echo '<pre>'; print_r($contents); echo '</pre>';
            }
                        
            fclose($fileStream);
        } else
        {
            //log/Could not open socket connection on url[host] using parse_url on port 80 with a 30 second timeout
        }
    } else
    {
        //log/Did not receive a 200 OK header response for script_url
    }
}
I think I will use your approach. It looks much more elegant.