/src/lib/net/http_client.php
PHP | 467 lines | 287 code | 76 blank | 104 comment | 87 complexity | 0dfd4670e912ede474bc6beade10f88f MD5 | raw file
Possible License(s): MIT, CC0-1.0
- <?php
- /**
- * HTTP client
- * A simple HTTP client
- * ---
- * Written by Jose Carlos Nieto <xiam@menteslibres.org>
- * Copyright (c) 2007 Jose Carlos Nieto <xiam@menteslibres.org>
- *
- * Licensed under The MIT License
- * Redistributions of files must retain the above copyright notice.
- *
- * @author Jose Carlos Nieto <xiam@menteslibres.org>
- * @copyright Copyright (c) 2007-2008, Jose Carlos Nieto <xiam@menteslibres.org>
- * @link http://opensource.astrata.com.mx Astrata Open Source Projects
- * @version $Revision: $
- * @modifiedby $LastChangedBy: $
- * @lastmodified $Date: $
- * @license http://www.opensource.org/licenses/mit-license.php The MIT License
- *
- */
- require_once TM_LIB_DIR.'socket.php';
- class http_client extends socket {
- /**
- * Host to connect
- * @public string
- */
- public $http_host;
- /**
- * Port to connect
- * @public integer
- */
- public $http_port = 80;
- /**
- * User to log in
- * @public string
- */
- public $http_user;
- /**
- * Password to log in
- * @public string
- */
- public $http_pass;
- /**
- * User agent
- * @public string
- */
- public $user_agent = null;
-
- /**
- * Client headers
- * @public array
- */
- public $request_headers;
- /**
- * Response headers
- * @public array
- */
- public $response_headers;
- /**
- * Response document
- * @public array
- */
- public $response_document;
- /**
- * Constructor.
- */
- public function __construct($host = null, $port = 80, $user = null, $pass = null) {
- parent::__construct();
- if ($host) {
- if (preg_match('/([^:]+):([^:]+)@(.+)$/', $host, $match)) {
- $host = $match[3].':80';
- $user = $match[1];
- $pass = $match[2];
- }
- if (preg_match('/([^:]+):(\d+)/', $host, $match)) {
- $host = $match[1];
- $port = $match[2];
- }
- $this->set_data($host, $port, $user, $pass);
- }
- }
- public function set_data($host, $port = 80, $user = null, $pass = null) {
-
- $this->http_host = $host;
- $this->http_port = $port;
- $this->http_user = $user;
- $this->http_pass = $pass;
- $this->set_header('host', $this->http_host.':'.$this->http_port);
- if ($this->http_user) {
- $this->set_header('authorization', 'Basic '.base64_encode($this->http_user.':'.$this->http_pass));
- }
- }
- /**
- * Opens a connection to the web server
- * @returns boolean connection status
- */
- public function open() {
- $this->connect($this->http_host, $this->http_port);
- return $this->status();
- }
- /**
- * Evaluates the served file
- * @parse string &$buff reference to the contents of the downloaded file
- */
- public function parse_response(&$buff) {
- preg_match("/^(.*?)\r\n\r\n(.*?)$/s", $buff, $match);
- if (isset($match[2])) {
- $this->response_document = $match[2];
- $headers = explode("\n", $match[1]);
- $status = false;
- foreach ($headers as $h) {
- if ($status == false) {
- $status_code = preg_match('/^[^\s]+\s(\d+)\s.*$/', $h, $match);
- $status = $h;
- // expecting HTTP/1.1 200 OK
- if (!strpos($status, '200')) {
- return false;
- }
- } else {
- preg_match("/^([^:]*?):\s*(.*?)$/i", $h, $m);
- if (isset($m[2]))
- $this->response_headers[strtolower($m[1])] = trim($m[2]);
- }
- }
- // inflating gzip'ed pages
- if ((isset($this->response_headers['content-encoding']) && ($this->response_headers['content-encoding'] == "gzip")) || (isset($this->response_headers['vary']) && strtolower($this->response_headers['vary']) == 'accept-encoding')) {
- // Read http://www.php.net/manual/en/public function.gzinflate.php
- $this->uncompress($this->response_document);
- }
- }
- }
- /**
- * Sets a client header.
- * @param string $name Name of the HTTP header.
- * @param string $value Value of the header.
- */
- public function set_header($name, $value) {
- $this->request_headers[strtolower($name)] = $value;
- }
- /*
- * Sends client headers.
- *
- */
- public function send_headers() {
- debug('*** Sending headers...', 3);
- $this->set_header('user-agent', $this->user_agent ? $this->user_agent : 'Mozilla/5.0 Textmotion/'.TM_VERSION.'');
- $this->set_header('host', $this->http_host);
- $this->set_header('accept', '*/*');
- if (function_exists('gzinflate')) {
- $this->set_header('accept-encoding', 'gzip,deflate');
- }
-
- $this->set_header('keep-alive', '300');
- $this->set_header('connection', 'keep-alive');
- foreach ($this->request_headers as $name => $value) {
- $name = preg_replace_callback('/(^|-)(.)/', create_function('$a', 'return $a[1].strtoupper($a[2]);'), $name);
- $this->write("{$name}: $value\r\n");
- }
- $this->write("\r\n");
- }
- /**
- * Prepares variables for being sent.
- * @param array $vars Variables to send
- * @returns string Variables formatted to be sent
- */
- public function join_variables($vars) {
- $buff = array();
- foreach ($vars as $f => $v)
- $buff[] = "$f=".urlencode($v);
- return implode("&", $buff);
- }
- /**
- * Performs a HTTP Post
- * @param string $url URL where to post
- * @param array $vars Variables to send
- * @param boolean $only_headers Set to true if you want only the document headers to be returned.
- * @returns string Document contents or headers
- */
- public function post($url, $vars, $only_headers = false) {
- if (is_array($vars)) {
- $data = $this->join_variables($vars);
- } else {
- $data = $vars;
- }
- $url = preg_replace("/[\r\n].*/", '', $url);
- $this->write("POST $url HTTP/1.1\r\n");
- $this->set_header('content-length', strlen($data));
- $this->set_header('content-type', 'application/x-www-form-urlencoded');
- $this->send_headers();
- $this->write($data);
-
- if ($only_headers) {
- return $this->get_headers();
- }
- $this->read_response();
- return $this->response_document;
- }
- /**
- * Reads only HTTP headers
- * @returns string HTTP headers
- */
- private function get_headers() {
- $buff = '';
- stream_set_timeout($this->_socket, $this->read_timeout);
- do {
- $line = $this->read_line($this->_socket);
- $buff .= $line;
- if ($line == "\r\n")
- return $buff;
- } while (!feof($this->_socket));
- }
-
- // taken from http://us.php.net/manual/en/public function.gzinflate.php#77336
- private function uncompress(&$data) {
- if (substr($data, 0, 3) == "\x1f\x8b\x08") {
- $i = 10;
- $flag = ord(substr($data, 3, 1));
- if ($flag > 0) {
- if ($flag & 4) {
- list($xlen) = unpack('v', substr($data, $i, 2));
- }
- if ($flag & 8) {
- $i = strpos($data, "\0", $i) + 1;
- }
- if ($flag & 16) {
- $i = strpos($data, "\0", $i) + 1;
- }
- if ($flag & 2) {
- $i = $i + 2;
- }
- }
- $data = gzinflate(substr($data, $i, -8));
- }
- }
- private function read_response() {
- if ($this->download()) {
- if ((isset($this->response_headers['content-encoding']) && ($this->response_headers['content-encoding'] == "gzip")) || (isset($this->response_headers['vary']) && strtolower($this->response_headers['vary']) == 'accept-encoding')) {
- $this->uncompress($this->response_document);
- }
- }
- }
- private function download() {
- unset($headers);
- unset($document);
-
- $data = '';
- $buff = '';
- $size = 0;
- $tmp = '';
- $response = 0;
- while ($this->status()) {
-
- $buff = $tmp.$this->read();
- $len = strlen($buff);
- $tmp = '';
-
- for ($i = 0; $i < $len; $i++) {
-
- $chr = $buff{$i};
- $tmp .= $chr;
- if (isset($document)) {
- if (isset($headers['transfer-encoding'])) {
- switch($headers['transfer-encoding'] == 'chunked') {
- case 'chunked':
- if ($size) {
- $tmp = '';
- for (; $size > 0 && $i < $len; $i++, $size--) {
- $tmp .= $buff{$i};
- }
- $document .= $tmp;
- $tmp = '';
- } else {
- if ($chr == "\n" && strlen(trim($tmp))) {
- $size = hexdec(trim($tmp));
- if ($size == 0) {
- $this->response_document = $document;
- return true;
- }
- $tmp = '';
- }
- }
- break;
- default:
- debug('Unsupported transfer encoding '.$headers['transfer-encoding'], 3);
- return false;
- break;
- }
- } else if (isset($headers['content-length'])) {
- if (!$size) {
- $size = $headers['content-length'];
- }
- $tmp = '';
- for (; $size > 0 && $i < $len; $i++, $size--) {
- $tmp .= $buff{$i};
- }
- $document .= $tmp;
- $tmp = '';
- if ($size == 0) {
- $this->response_document = $document;
- return true;
- }
- } else {
- $document = substr($buff, $i);
- while ($this->status()) {
- $document .= $this->read();
- }
- $this->response_document = $document;
- return true;
- }
- } else {
- if ($chr == "\n") {
- if (!isset($headers)) {
- // first response.
- debug('>>> '.$tmp, 3);
- preg_match('/[^\s]+\s(\d+)\s.*/', $tmp, $code);
- $this->response_code = $code[1];
- if (preg_match('/^(200|302|301)$/', $code[1])) {
- $headers = array();
- } else {
- debug('Unknown response', 3);
- return false;
- }
- } else {
- if (trim($tmp)) {
- // storing headers
- preg_match('/^([^:]+):\s?(.+)$/', $tmp, $match);
- $headers[strtolower($match[1])] = trim($match[2]);
- } else {
- $document = '';
- $this->response_headers = $headers;
-
- if (preg_match('/^(302|301)$/', $this->response_code) && !empty($headers['location'])) {
- return false;
- } else if (empty($headers['content-length'])) {
- if (isset($headers['transfer-encoding']) && $headers['transfer-encoding'] != 'chunked') {
- debug('No content-length header.', 3);
- return false;
- }
- }
- }
- }
- $tmp = '';
- }
- }
- }
- }
- }
- /**
- * Standard HTTP get
- * @param string $url URL to get
- * @param boolean $only_headers Set true if you want only the document headers
- * @returns string Document content or headers
- */
- public public function get($url, $only_headers = false, $count = 0) {
- if (!$this->status()) {
- $this->open();
- }
- debug('HTTP GET '.$url, 3);
- if ($count < 3) {
- $url = preg_replace("/[\r\n].*/", '', $url);
- $this->write("GET $url HTTP/1.1\r\n");
- $this->send_headers();
- if ($only_headers) {
- return $this->get_headers();
- }
- $this->read_response();
- if (preg_match('/^(301|302)$/', $this->response_code)) {
- $redir = $this->response_headers['location'];
- if ($redir{0} != '/' && !preg_match('/^http:\/\/([^\/]+)/i', $redir, $match)) {
- $redir = dirname($url).'/'.$redir;
- }
- if (preg_match('/[a-z]+:\/\/([^\/]+)/i', $redir, $match)) {
- if (isset($match[1])) {
- $host = explode(':', $match[1]);
- $this->http_host = $host[0];
- if (isset($host[1])) {
- $this->http_port = $host[1];
- }
- }
- }
-
- debug(sprintf('Redirected to %s...', $redir), 3);
- return $this->get($redir, $only_headers, $count + 1);
- }
- //debug($this->response_headers);
- return $this->response_document;
- }
- }
- /**
- * Runs a public functional test
- */
- public function run_test() {
- header('content-type: text/plain');
- $http = new http_client('localhost', '80');
- if ($http->open()) {
- echo $http->get('/test/foo.txt');
- $http->close();
- }
- }
- }
- // http_client::run_test();
- ?>