Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Code for sendHttpRequest for allow_url_fopen = 0 #793

Closed
anonymous-matomo-user opened this issue Jun 12, 2009 · 12 comments
Closed

New Code for sendHttpRequest for allow_url_fopen = 0 #793

anonymous-matomo-user opened this issue Jun 12, 2009 · 12 comments
Assignees
Labels
Enhancement For new feature suggestions that enhance Matomo's capabilities or add a new report, new API etc.
Milestone

Comments

@anonymous-matomo-user
Copy link

I've changed the sendHttpRequest to allow checking for new versions on servers where allow_url_fopen equals zero. My code is tested on my server, and works as expected.

Open core/Piwik.php and replace sendHttpRequest by the following code:

#!php
    static public function sendHttpRequest($url, $timeout)
    {
        // Modified by Uli <m [AT] il [DOT] wolf-u [DOT] li>
        $response = false;
        if (ini_get('allow_url_fopen') == 0) {
            if(function_exists(curl_init)) {
                $ch = @curl_init();
                @curl_setopt($ch, CURLOPT_URL, $url);
                @curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
                @curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
                $response = @curl_exec ($ch);
                @curl_close ($ch);
                unset($ch);
            } else {
                $fsockurl = @parse_url($url);
                if(empty($fsockurl['port'])) { $fsockurl['port'] = 80; }
                $fsock = @fsockopen($fsockurl['host'],  $fsockurl['port'], $errno, $errstr, $timeout);
                @fputs($fsock, "GET " . $fsockurl['path'] . " HTTP/1.0\r\n");
                @fputs($fsock, "HOST: " . $fsockurl['host'] . "\r\n");
                @fputs($fsock, "Connection: close\r\n\r\n");
                while (!@feof($fsock)) {
                    if ($get_info) {
                        $response = @fread($fsock, 1024);
                    } else {
                        if (@fgets($fsock, 1024) == "\r\n") {
                            $get_info = true;
                        }
                    }
                }
                @fclose($fsock);
                unset($fsockurl, $fsock, $get_info);
            }
        } else {
            // we make sure the request takes less than a few seconds to fail
            // we also set the socket_timeout (for php < 5.2.1)
            $default_socket_timeout = @ini_get('default_socket_timeout');
            @ini_set('default_socket_timeout', $timeout);

            // we create a stream_context (works in php >= 5.2.1)
            $ctx = null;
            if(function_exists('stream_context_create')) {
                    $ctx = stream_context_create(array('http' => array( 'timeout' => $timeout)));
            }
            $response = trim(@file_get_contents($url, 0, $ctx));

            // restore the socket_timeout value
            if(!empty($default_socket_timeout))
            {
                    @ini_set('default_socket_timeout', $default_socket_timeout);
            }
        }
        return $response;
    }

Checks by curl if allow_url_fopen = 0. If curl is not available, it gets checked by a socket.

I hope this (or something similar) gets included into one of the next releases ;)

@robocoder
Copy link
Contributor

Why 3 methods? Why not just use sockets if that's the lowest common denominator?

Code comments re: socket version:

  • should call stream_set_timeout()
  • need to concatenate response, otherwise it doesn't work on files > 1K
  • should check the response header for HTTP error code

@anonymous-matomo-user
Copy link
Author

Replying to vipsoft:

Why 3 methods? Why not just use sockets if that's the lowest common denominator?
Hmmm good thought. Don't think sockets get disabled by hosters, so yes, this would be the lowest common denominator. You are probably right.

Code comments re: socket version:

  • should call stream_set_timeout()
    I didn't really dig into the socket-thing, i only checked if it works.
  • need to concatenate response, otherwise it doesn't work on files > 1K
    Yeah right, i only saw the updater for this
  • should check the response header for HTTP error code
    Also right, this code is probably too rudimentary

Will update the code, but need to check a few things first.

@anonymous-matomo-user
Copy link
Author

I've recoded the sendHttpRequest for sockets with your hints. Furthermore it should now meet the CodingStandard:

static public function sendHttpRequest($url, $timeout)
{
    // Modified by Uli <m [AT] il [DOT] wolf-u [DOT] li>
    $response = false;
    // Parse the url for fsockopen
    $fsockurl = @parse_url($url);
    if(empty($fsockurl['port']))
    {
        $fsockurl['port'] = 80;
    }
    if(!empty($fsockurl['query']))
    {
        $fsockurl['path'].="?" . $fsockurl['query'];
    }
    // Make the request
    $fsock = @fsockopen($fsockurl['host'],  $fsockurl['port'], $errno, $errstr, $timeout);
    if($fsock||!is_resource($fsock))
    {
        @fputs($fsock, "GET " . $fsockurl['path'] . " HTTP/1.0\r\n");
        @fputs($fsock, "HOST: " . $fsockurl['host'] . "\r\n");
        @fputs($fsock, "Connection: close\r\n\r\n");
        @stream_set_blocking($fsock, TRUE);
        @stream_set_timeout($fsock,$timeout);

        while ((!@feof($fsock)) && (!$streamMetaData['timed_out']))
        {
            // Load data as long as there is data and the connection didn't time out
            $fgetsData .= @fgets($fsock, 1024);
            $streamMetaData = @stream_get_meta_data($fsock);
            @ob_flush;
            @flush();
        }

        @fclose($fsock);
        unset($fsockurl, $fsock);
    }

    if (!$streamMetaData['timed_out'] && $fgetsData != "")
    {
        // The connection didn't time out and there is data available
        // Split into headers & body
        $hunks = explode("\r\n\r\n",trim($fgetsData));
        if (is_array($hunks) && count($hunks) >= 2)
        {
            $headers = explode("\n",$hunks[count($hunks) - 2]);
            $body = $hunks[count($hunks) - 1];
            unset($hunks);
            if (is_array($headers) && count($headers) >= 1)
            {
                // Check if the server also told us that everything went well
                switch(trim(strtolower($headers[0])))
                {
                    case 'http/1.0 100 ok':
                    case 'http/1.0 200 ok':
                    case 'http/1.1 100 ok':
                    case 'http/1.1 200 ok':
                        $response = $body;
                    break;
                }
            }
        }
    }
    return $response;
}

Works on my server ;)

@robocoder
Copy link
Contributor

Thanks, it looks good (cursory inspection). I'll do a more complete review before commiting.

I'm also going to throw in a couple of additional requirements. (You're welcome to tackle these as well.)

  • Add a check for Content-length (if present in the header).
  • Refactor fetchRemoteFile

@anonymous-matomo-user
Copy link
Author

Alright, i've added the check for the parameter "content-length". If it is present, the data gets checked and if the two values are not equal, the output will be false again.

Also removed two possible errors by initializing the variables $fgetsData and $streamMetaData.

/**
 * Sends http request ensuring the request will fail before $timeout seconds
 * Returns the response content (no header, trimmed)
 * @author Uli <m [AT] il [DOT] wolf-u [DOT] li>
 * @param string $url
 * @param int $timeout
 * @return string|false false if request failed
 */
static public function sendHttpRequest($url, $timeout)
{
    $response = false;
    // Parse the url for fsockopen
    $fsockurl = @parse_url($url);
    $fgetsData = "";
    $streamMetaData = array();
    if(empty($fsockurl['port']))
    {
        $fsockurl['port'] = 80;
    }
    if(!empty($fsockurl['query']))
    {
        $fsockurl['path'].="?" . $fsockurl['query'];
    }
    // Make the request
    $fsock = @fsockopen($fsockurl['host'],  $fsockurl['port'], $errno, $errstr, $timeout);
    if($fsock||!is_resource($fsock))
    {
        @fputs($fsock, "GET " . $fsockurl['path'] . " HTTP/1.0\r\n");
        @fputs($fsock, "HOST: " . $fsockurl['host'] . "\r\n");
        @fputs($fsock, "Connection: close\r\n\r\n");
        @stream_set_blocking($fsock, TRUE);
        @stream_set_timeout($fsock,$timeout);

        while ((!@feof($fsock)) && (!$streamMetaData['timed_out']))
        {
            // Load data as long as there is data and the connection didn't time out
            $fgetsData .= @fgets($fsock, 1024);
            $streamMetaData = @stream_get_meta_data($fsock);
            @ob_flush;
            @flush();
        }

        @fclose($fsock);
        unset($fsockurl, $fsock);
    }

    if (!$streamMetaData['timed_out'] && $fgetsData != "")
    {
        // The connection didn't time out and there is data available
        // Split into headers & body
        $hunks = @explode("\r\n\r\n",$fgetsData);
        if (is_array($hunks) && count($hunks) >= 2)
        {
            $headers = @explode("\n",$hunks[count($hunks) - 2]);
            $body = $hunks[count($hunks) - 1];
            unset($hunks);
            if (is_array($headers) && count($headers) >= 1)
            {
                // Check if the server also told us that everything went well
                switch(trim(strtolower($headers[0])))
                {
                    case 'http/1.0 100 ok':
                    case 'http/1.0 200 ok':
                    case 'http/1.1 100 ok':
                    case 'http/1.1 200 ok':
                        // Generally the answer will be ok
                        $response = true;
                        // Now check the content-length if available
                        foreach($headers as $header) {
                            if(substr(trim(strtolower($header)),0,15) == "content-length:") {
                                // Check the length against the content
                                if(substr(trim($header),16) != strlen($body)) {
                                    //Reset the response if this is the wrong length
                                    $response = false;
                                }
                            }
                        }
                    break;
                }
            }
        }
    }

    if($response === true) {
        return trim($body);
    } else {
        return false;
    }
}

If i have time in the next few days i will try to refactor fetchRemoteFile, but this will be at the weekend at the earliest.

@robocoder
Copy link
Contributor

If you can, please attach your code as a diff. Thanks.

@mattab
Copy link
Member

mattab commented Jun 15, 2009

did you check that it was working when doing the one click upgrade?

@anonymous-matomo-user
Copy link
Author

I didn't test sendHttpRequest against the one-click upgrade as the code was tested against various types of content and servers. It does what it should do (as far as i have tested this function).

Perhaps you mean fetchRemoteFile? I didn't have a look into it yet.

@anonymous-matomo-user
Copy link
Author

Attachment: Patch of sendHttpRequest against 0.4.1
patch-sendHttpRequest-0.4.1-1.patch

@anonymous-matomo-user
Copy link
Author

Added a patch against 0.4.1 with one addition, the array $streamMetaData needed to be initialized.

@robocoder
Copy link
Contributor

(In [1369]) fixes #793 - rewrite sendHttpRequest() to work when allow_url_fopen=0; also refactor fetchRemoteFile

@robocoder
Copy link
Contributor

In [1391], fixes #925, refs #793 - provide curl and stream methods (as originally proposed by Uli)

Order of preference: curl (6s), stream (10s), socket (13s) to d/l latest.zip

Add Piwik::getTransportMethod() and add to Installation system check

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement For new feature suggestions that enhance Matomo's capabilities or add a new report, new API etc.
Projects
None yet
Development

No branches or pull requests

3 participants