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