class.httpClient.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. <?php
  2. /* Version 0.9, 6th April 2003 - Simon Willison ( http://simon.incutio.com/ )
  3. Manual: http://scripts.incutio.com/httpclient/
  4. */
  5. class HttpClient
  6. {
  7. // Request vars
  8. var $host;
  9. var $port;
  10. var $path;
  11. var $method;
  12. var $postdata = '';
  13. var $cookies = array();
  14. var $referer;
  15. var $accept = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*';
  16. var $accept_encoding = 'gzip';
  17. var $accept_language = 'en-us';
  18. var $user_agent = 'Incutio HttpClient v0.9';
  19. // Options
  20. var $timeout = 20;
  21. var $use_gzip = false;
  22. var $persist_cookies = true; // If true, received cookies are placed in the $this->cookies array ready for the next request
  23. // Note: This currently ignores the cookie path (and time) completely. Time is not important,
  24. // but path could possibly lead to security problems.
  25. var $persist_referers = true; // For each request, sends path of last request as referer
  26. var $debug = false;
  27. var $handle_redirects = true; // Auaomtically redirect if Location or URI header is found
  28. var $max_redirects = 5;
  29. var $headers_only = false; // If true, stops receiving once headers have been read.
  30. // Basic authorization variables
  31. var $username;
  32. var $password;
  33. // Response vars
  34. var $status;
  35. var $headers = array();
  36. var $content = '';
  37. var $errormsg;
  38. // Tracker variables
  39. var $redirect_count = 0;
  40. var $cookie_host = '';
  41. function HttpClient($host, $port=80)
  42. {
  43. $this->host = $host;
  44. $this->port = $port;
  45. }
  46. function get($path, $data = false)
  47. {
  48. $this->path = $path;
  49. $this->method = 'GET';
  50. if ($data) {
  51. $this->path .= '?'.$this->buildQueryString($data);
  52. }
  53. return $this->doRequest();
  54. }
  55. function post($path, $data)
  56. {
  57. $this->path = $path;
  58. $this->method = 'POST';
  59. $this->postdata = $this->buildQueryString($data);
  60. return $this->doRequest();
  61. }
  62. function buildQueryString($data) {
  63. $querystring = '';
  64. if (is_array($data)) {
  65. // Change data in to postable data
  66. foreach ($data as $key => $val) {
  67. if (is_array($val)) {
  68. foreach ($val as $val2) {
  69. $querystring .= urlencode($key).'='.urlencode($val2).'&';
  70. }
  71. } else {
  72. $querystring .= urlencode($key).'='.urlencode($val).'&';
  73. }
  74. }
  75. $querystring = substr($querystring, 0, -1); // Eliminate unnecessary &
  76. } else {
  77. $querystring = $data;
  78. }
  79. return $querystring;
  80. }
  81. function doRequest()
  82. {
  83. // Performs the actual HTTP request, returning true or false depending on outcome
  84. if (!$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) {
  85. // Set error message
  86. switch($errno) {
  87. case -3:
  88. $this->errormsg = 'Socket creation failed (-3)';
  89. case -4:
  90. $this->errormsg = 'DNS lookup failure (-4)';
  91. case -5:
  92. $this->errormsg = 'Connection refused or timed out (-5)';
  93. default:
  94. $this->errormsg = 'Connection failed ('.$errno.')';
  95. $this->errormsg .= ' '.$errstr;
  96. $this->debug($this->errormsg);
  97. }
  98. return false;
  99. }
  100. socket_set_timeout($fp, $this->timeout);
  101. $request = $this->buildRequest();
  102. $this->debug('Request', $request);
  103. fwrite($fp, $request);
  104. // Reset all the variables that should not persist between requests
  105. $this->headers = array();
  106. $this->content = '';
  107. $this->errormsg = '';
  108. // Set a couple of flags
  109. $inHeaders = true;
  110. $atStart = true;
  111. // Now start reading back the response
  112. while (!feof($fp))
  113. {
  114. $line = fgets($fp, 4096);
  115. if ($atStart)
  116. {
  117. // Deal with first line of returned data
  118. $atStart = false;
  119. if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) {
  120. $this->errormsg = "Status code line invalid: ".htmlentities($line);
  121. $this->debug($this->errormsg);
  122. return false;
  123. }
  124. $http_version = $m[1]; // not used
  125. $this->status = $m[2];
  126. $status_string = $m[3]; // not used
  127. $this->debug(trim($line));
  128. continue;
  129. }
  130. if ($inHeaders) {
  131. if (trim($line) == '') {
  132. $inHeaders = false;
  133. $this->debug('Received Headers', $this->headers);
  134. if ($this->headers_only) {
  135. break; // Skip the rest of the input
  136. }
  137. continue;
  138. }
  139. if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) {
  140. // Skip to the next header
  141. continue;
  142. }
  143. $key = strtolower(trim($m[1]));
  144. $val = trim($m[2]);
  145. // Deal with the possibility of multiple headers of same name
  146. if (isset($this->headers[$key])) {
  147. if (is_array($this->headers[$key])) {
  148. $this->headers[$key][] = $val;
  149. } else {
  150. $this->headers[$key] = array($this->headers[$key], $val);
  151. }
  152. } else {
  153. $this->headers[$key] = $val;
  154. }
  155. continue;
  156. }
  157. // We're not in the headers, so append the line to the contents
  158. $this->content .= $line;
  159. }
  160. fclose($fp);
  161. // If data is compressed, uncompress it
  162. if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip')
  163. {
  164. $this->debug('Content is gzip encoded, unzipping it');
  165. $this->content = substr($this->content, 10); // See http://www.php.net/manual/en/function.gzencode.php
  166. $this->content = gzinflate($this->content);
  167. }
  168. // If $persist_cookies, deal with any cookies
  169. if ($this->persist_cookies && isset($this->headers['set-cookie']) && $this->host == $this->cookie_host)
  170. {
  171. $cookies = $this->headers['set-cookie'];
  172. if (!is_array($cookies)) {
  173. $cookies = array($cookies);
  174. }
  175. foreach ($cookies as $cookie)
  176. {
  177. if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) {
  178. $this->cookies[$m[1]] = $m[2];
  179. }
  180. }
  181. // Record domain of cookies for security reasons
  182. $this->cookie_host = $this->host;
  183. }
  184. // If $persist_referers, set the referer ready for the next request
  185. if ($this->persist_referers) {
  186. $this->debug('Persisting referer: '.$this->getRequestURL());
  187. $this->referer = $this->getRequestURL();
  188. }
  189. // Finally, if handle_redirects and a redirect is sent, do that
  190. if ($this->handle_redirects)
  191. {
  192. if (++$this->redirect_count >= $this->max_redirects)
  193. {
  194. $this->errormsg = 'Number of redirects exceeded maximum ('.$this->max_redirects.')';
  195. $this->debug($this->errormsg);
  196. $this->redirect_count = 0;
  197. return false;
  198. }
  199. $location = isset($this->headers['location']) ? $this->headers['location'] : '';
  200. $uri = isset($this->headers['uri']) ? $this->headers['uri'] : '';
  201. if ($location || $uri) {
  202. $url = parse_url($location.$uri);
  203. // This will FAIL if redirect is to a different site
  204. return $this->get($url['path']);
  205. }
  206. }
  207. return true;
  208. }
  209. function buildRequest()
  210. {
  211. $headers = array();
  212. $headers[] = "{$this->method} {$this->path} HTTP/1.0"; // Using 1.1 leads to all manner of problems, such as "chunked" encoding
  213. $headers[] = "Host: {$this->host}";
  214. $headers[] = "User-Agent: {$this->user_agent}";
  215. $headers[] = "Accept: {$this->accept}";
  216. if ($this->use_gzip) {
  217. $headers[] = "Accept-encoding: {$this->accept_encoding}";
  218. }
  219. $headers[] = "Accept-language: {$this->accept_language}";
  220. if ($this->referer) {
  221. $headers[] = "Referer: {$this->referer}";
  222. }
  223. // Cookies
  224. if ($this->cookies) {
  225. $cookie = 'Cookie: ';
  226. foreach ($this->cookies as $key => $value) {
  227. $cookie .= "$key=$value; ";
  228. }
  229. $headers[] = $cookie;
  230. }
  231. // Basic authentication
  232. if ($this->username && $this->password) {
  233. $headers[] = 'Authorization: BASIC '.base64_encode($this->username.':'.$this->password);
  234. }
  235. // If this is a POST, set the content type and length
  236. if ($this->postdata) {
  237. $headers[] = 'Content-Type: application/x-www-form-urlencoded';
  238. $headers[] = 'Content-Length: '.strlen($this->postdata);
  239. }
  240. $request = implode("\r\n", $headers)."\r\n\r\n".$this->postdata;
  241. return $request;
  242. }
  243. function getStatus()
  244. {
  245. return $this->status;
  246. }
  247. function getContent()
  248. {
  249. return $this->content;
  250. }
  251. function getHeaders(){
  252. return $this->headers;
  253. }
  254. function getHeader($header)
  255. {
  256. $header = strtolower($header);
  257. if (isset($this->headers[$header])) {
  258. return $this->headers[$header];
  259. } else {
  260. return false;
  261. }
  262. }
  263. function getError()
  264. {
  265. return $this->errormsg;
  266. }
  267. function getCookies()
  268. {
  269. return $this->cookies;
  270. }
  271. function getRequestURL()
  272. {
  273. $url = 'http://'.$this->host;
  274. if ($this->port != 80) {
  275. $url .= ':'.$this->port;
  276. }
  277. $url .= $this->path;
  278. return $url;
  279. }
  280. // Setter methods
  281. function setUserAgent($string)
  282. {
  283. $this->user_agent = $string;
  284. }
  285. function setAuthorization($username, $password)
  286. {
  287. $this->username = $username;
  288. $this->password = $password;
  289. }
  290. function setCookies($array)
  291. {
  292. $this->cookies = $array;
  293. }
  294. // Option setting methods
  295. function useGzip($boolean)
  296. {
  297. $this->use_gzip = $boolean;
  298. }
  299. function setPersistCookies($boolean)
  300. {
  301. $this->persist_cookies = $boolean;
  302. }
  303. function setPersistReferers($boolean)
  304. {
  305. $this->persist_referers = $boolean;
  306. }
  307. function setHandleRedirects($boolean)
  308. {
  309. $this->handle_redirects = $boolean;
  310. }
  311. function setMaxRedirects($num)
  312. {
  313. $this->max_redirects = $num;
  314. }
  315. function setHeadersOnly($boolean)
  316. {
  317. $this->headers_only = $boolean;
  318. }
  319. function setDebug($boolean)
  320. {
  321. $this->debug = $boolean;
  322. }
  323. // "Quick" static methods
  324. function quickGet($url)
  325. {
  326. $bits = parse_url($url);
  327. $host = $bits['host'];
  328. $port = isset($bits['port']) ? $bits['port'] : 80;
  329. $path = isset($bits['path']) ? $bits['path'] : '/';
  330. if (isset($bits['query'])) {
  331. $path .= '?'.$bits['query'];
  332. }
  333. $client = new HttpClient($host, $port);
  334. if (!$client->get($path)) {
  335. return false;
  336. } else {
  337. return $client->getContent();
  338. }
  339. }
  340. function quickPost($url, $data)
  341. {
  342. $bits = parse_url($url);
  343. $host = $bits['host'];
  344. $port = isset($bits['port']) ? $bits['port'] : 80;
  345. $path = isset($bits['path']) ? $bits['path'] : '/';
  346. $client = new HttpClient($host, $port);
  347. if (!$client->post($path, $data)) {
  348. return false;
  349. } else {
  350. return $client->getContent();
  351. }
  352. }
  353. function debug($msg, $object = false)
  354. {
  355. if ($this->debug) {
  356. print '<div style="border: 1px solid red; padding: 0.5em; margin: 0.5em;"><strong>HttpClient Debug:</strong> '.$msg;
  357. if ($object) {
  358. ob_start();
  359. print_r($object);
  360. $content = htmlentities(ob_get_contents());
  361. ob_end_clean();
  362. print '<pre>'.$content.'</pre>';
  363. }
  364. print '</div>';
  365. }
  366. }
  367. }
  368. ?>