Location: PHPKode > projects > VideoDB > videodb/core/httpclient.php
<?php
/**
 * HTTP client functions
 *
 * @todo    Encapsulate httpClient and Cache as separate classes
 *
 * @package Core
 * @author  Andreas Goetz   <hide@address.com>
 * @author  Andreas Gohr    <hide@address.com>
 * @author  Chinamann       <hide@address.com>
 * @version $Id: httpclient.php,v 1.10 2008/07/19 09:01:01 andig2 Exp $
 */

require_once './core/cache.php';

/**
 * Reads a saved HTTP response from a cachefile.
 * If caching is globally disabled ($config['IMDBage'] <= 0), file is not loaded.
 *
 * @param   string $url URL of the cached response
 * @return  mixed       HTTP Response, false on errors
 */
function getHTTPcache($url)
{
    global $config;

    return cache_get($url, CACHE_HTML, $config['IMDBage'], true);
}

/**
 * Saves a HTTP resonse to a cachefile
 * If caching is globally disabled ($config['IMDBage'] <= 0), file is not saved.
 *
 * @param  string $url  URL of the response
 * @param  mixed  $resp HTTP Response
 */
function putHTTPcache($url, $data)
{
    global $config;

    cache_put($url, $data, CACHE_HTML, $config['IMDBage'], true);
}

/**
 * httpClient helper function to decode gzip-encode responses
 * used on Content-encoding: gzip
 */
function gz_decode(&$string)
{
    return gzinflate(substr($string, 10));
}

/**
 * httpClient helper function to convert array of cookies to http header
 */
function cookies2header($cookies)
{
    global $request_cookies;

    // concatenate cookie string
    foreach ($cookies as $key => $val)
    {
        // remember cookies for next request
        $request_cookies[$key] = $val;

        if ($headers) $headers .= '; ';
        $headers .= $key.'='.$val;
    }

    // build header
    if ($headers) $headers = 'Cookie: '.$headers."\r\n";
    return $headers;
}

/**
 * Collect cookies from httpclient response and add them to an existing array
 *
 * @param  mixed    $response   HTTP response
 * @param  array    $oldcookies old cookies
 * @return array                new and old cookies
 */
function get_cookies_from_response($response, $oldcookies = null) 
{
    if (preg_match_all('/Set-Cookie:\s*(.+?);/', $response['header'], $_cookies, PREG_PATTERN_ORDER))
    {
        foreach ($_cookies[1] as $cookie)
        {
            // limit split to 2 elements (key/value)
            list($key, $value) = split('=', $cookie, 2);
            $oldcookies[$key] = $value;
        }
    }
    
    return $oldcookies;
}

/**
 * HTTP Client
 *
 * Returns the raw data from the given URL, uses proxy when configured
 * and follows redirects
 *
 * @author Andreas Goetz <hide@address.com>
 * @param  string  $url      URL to fetch
 * @param  bool    $cache    use caching? defaults to false
 * @param  string  $post     POST data, if nonempty POST is used instead of GET
 * @param  integer $timeout  Timeout in seconds defaults to 15
 * @return mixed             HTTP response
 */
function httpClient($url, $cache = false, $para = null)
{
    global $config;

    // use this as workaround for php bug http://bugs.php.net/bug.php?id=22526 session_start/popen hang
    //session_write_close(); // <-- We don't use popen! But sometimes we need the session afterwards! (Chinamann)

    $method  = 'GET';
    $headers = '';  // additional HTTP headers, used for post data
    
    $post    = $para['post'];
    if (is_array($post)) $post = http_build_query($post);
    
    if (!empty($post))
    {
        // POST request
        $method   = 'POST';
        $headers .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $headers .= "Content-Length: ".strlen($post)."\r\n";
    }
    
    // get data from cache?
    if ($cache)
    {
        $resp = getHTTPcache($url.$post);
        if ($resp !== false) {
            return $resp;
        }
    }

    $response['error']  = '';
    $response['header'] = '';
    $response['data']   = '';
    $response['url']    = $url;
    $response['success']= false;

    $uri    = parse_url($url);
    $server = $uri['host'];
    $path   = $uri['path'];
    if (empty($path)) $path = '/';  
    if (!empty($uri['query'])) $path .= '?'.$uri['query'];
    $port   = $uri['port'];

    // proxy setup
    if (!empty($config['proxy_host']) && !$para['no_proxy'])
    {
        $request_url = $url;
        $server      = $config['proxy_host'];
        $port        = $config['proxy_port'];
        if (!$port) $port = 8080;
    }
    else
    {
        $request_url = $path;  // hide@address.com: use $path instead of $url if HTTP/1.0
        $server      = $server;
        if (!$port) $port = 80;
    }

    // open socket
    $socket = fsockopen($server, $port);
    if (!$socket)
    {
        $response['error'] = "Could not connect to $server";
        return $response;
    }
    socket_set_timeout($socket, $para['timeout'] ? $para['timeout'] : 15);

    // build request
    $request  = "$method $request_url HTTP/1.0\r\n";
    $request .= "Host: ".$uri['host']."\r\n";
    $request .= "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98)\r\n";
    if (extension_loaded('zlib')) $request .= "Accept-encoding: gzip\r\n";
    
    // add cookies- these are expcted to be in array form
    if ($para['cookies']) $request .= cookies2header($para['cookies']);
    
    $request .= "Connection: Close\r\n";
    
    // additional request headers
    $request .= $headers;
    if ($para['header']) $request .= $para['header'];
    
    $request .= "\r\n";
    $request .= $post;

    // send request
    fputs($socket, $request);

    if ($config['debug']) echo "request:<br>".nl2br($request)."<p>";

    // log request
    if ($config['httpclientlog'])
    {
        $log = fopen('httpClient.log', 'a');
        fwrite($log, $request."\n");
        fclose($log);
    }

    // read headers from socket
    while (!(feof($socket) || preg_match('/\r\n\r\n$/', $response['header'])))
    {
        $read                = fgets($socket);
        $response['header'] .= $read;
        $header_size        += strlen($read);
    }

    // chunked encoding?
    if (preg_match('/transfer\-(en)?coding:\s+chunked\r\n/i',$response['header']))
    {
        do {
            unset($chunk_size);
            do
            {
                $byte = fread($socket,1);
                $chunk_size .= $byte;
            }
            while (preg_match('/[a-zA-Z0-9]/',$byte));  // read chunksize including \r

            $byte       = fread($socket, 1);            // readtrailing \n
            $chunk_size = hexdec($chunk_size);
            $this_chunk = fread($socket, $chunk_size);
            $response['data'] .= $this_chunk;
            if ($chunk_size) $byte = fread($socket, 2); // read trailing \r\n
        }
        while ($chunk_size);
    }
    else
    {
        // read entire socket
        while (!feof($socket))
        {
            $response['data'] .= fread($socket,4096);
        }
    }

    // close socket
    $status = socket_get_status($socket);
    fclose($socket);

    if ($config['debug']) echo "header:<br>".nl2br($response['header'])."<p>";
    // if ($config['debug']) echo "data:<br>".htmlspecialchars($response['data'])."<p>";

    // check for timeout
    if ($status['timed_out'])
    {
        $response['error'] = "Connection timed out";
        return $response;
    }

    // log response
    if ($config['httpclientlog'])
    {
        $log = fopen('httpClient.log', 'a');
        fwrite($log, $response['header']."\n");
        fclose($log);
    }

    // check server status code to follow redirect
    if (preg_match("/^HTTP\/1.\d 30[12].*?\n/s", $response['header']))
    {
        preg_match("/Location:\s+(.*?)\n/is",$response['header'],$matches);
        if (empty($matches[1]))
        {
            $response['error'] = 'Redirect but no Location header found';
            return $response;
        }
        else
        {
			// in case no redirect is needed stop here and respond success
            if ($para['no_redirect'])
            {
                // save time if result is not needed
        		$response['error']   = '';
        		$response['success'] = true;
        		return $response;
        	}

            // get redirection target
            $location = trim($matches[1]);

            if (preg_match("/^\//", $location))
            {
                // local redirect
                $location = 'http://'.$uri['host'].':'.$uri['port'].$location;
            }
            elseif (!preg_match('/^http/', $location))
            {
                // local redirect without path
            	$path     = substr($uri['path'], 0, strrpos($uri['path'], '/') + 1);
            	$location = 'http://'.$uri['host'].':'.$uri['port'].$path.$location;
            }

            // don't use old headers again
            $headers	= '';

            // add new cookies from response
            $para['cookies'] = get_cookies_from_response($response, $para['cookies']);

            // perform redirected request; we must GET, not POST
            if ($para['post']) unset($para['post']);
            $response = httpClient($location, $cache, $para);

            // remember we were redirected
            $response['redirect'] = $location;

            // store response a 2nd time under the the original post attributes
            if ($response['success'] == true && $cache) putHTTPcache($url.$post, $response);

            return $response;
        }
    }

    // verify status code
    if (!preg_match("/^.*? 200 .*?\n/s", $response['header']))
    {
        $response['error'] = 'Server returned wrong status.';
        return $response;
    }

    $response['success'] = true;

    // decode data if necessary- do not modify original headers
    if (preg_match("/Content-Encoding:\s+gzip\r?\n/i", $response['header']))
    {
        $response['data'] = gz_decode($response['data']);
    }

    // decode UTF8
    if (preg_match("/Content-Type:.*?charset=UTF-8/i", $response['header']))
    {
        $response['data'] = utf8_decode($response['data']);
    }

    // commit successful request to cache
    if ($cache) putHTTPcache($url.$post, $response);

    return $response;
}

/**
 * Downloads an URL to the given local file
 *
 * @param   string  $url    URL to download
 * @param   string  $local  Full path to save to
 * @return  bool            true on succes else false
 */
function download($url, $local)
{
    $resp = httpClient($url);
    if (!$resp['success']) return false;

    return(@file_put_contents($local, $resp['data']) !== false);
}

?>
Return current item: VideoDB