0337d704 |
1 | <?php |
2 | /** |
3 | * base include file for SimpleTest |
4 | * @package SimpleTest |
5 | * @subpackage WebTester |
6 | * @version $Id: http.php,v 1.94 2004/06/30 22:13:07 lastcraft Exp $ |
7 | */ |
8 | |
9 | /**#@+ |
10 | * include other SimpleTest class files |
11 | */ |
12 | require_once(dirname(__FILE__) . '/socket.php'); |
13 | require_once(dirname(__FILE__) . '/url.php'); |
14 | /**#@-*/ |
15 | |
16 | /** |
17 | * Cookie data holder. Cookie rules are full of pretty |
18 | * arbitary stuff. I have used... |
19 | * http://wp.netscape.com/newsref/std/cookie_spec.html |
20 | * http://www.cookiecentral.com/faq/ |
21 | * @package SimpleTest |
22 | * @subpackage WebTester |
23 | */ |
24 | class SimpleCookie { |
25 | var $_host; |
26 | var $_name; |
27 | var $_value; |
28 | var $_path; |
29 | var $_expiry; |
30 | var $_is_secure; |
31 | |
32 | /** |
33 | * Constructor. Sets the stored values. |
34 | * @param string $name Cookie key. |
35 | * @param string $value Value of cookie. |
36 | * @param string $path Cookie path if not host wide. |
37 | * @param string $expiry Expiry date as string. |
38 | * @param boolean $is_secure Currently ignored. |
39 | */ |
40 | function SimpleCookie($name, $value = false, $path = false, $expiry = false, $is_secure = false) { |
41 | $this->_host = false; |
42 | $this->_name = $name; |
43 | $this->_value = $value; |
44 | $this->_path = ($path ? $this->_fixPath($path) : "/"); |
45 | $this->_expiry = false; |
46 | if (is_string($expiry)) { |
47 | $this->_expiry = strtotime($expiry); |
48 | } elseif (is_integer($expiry)) { |
49 | $this->_expiry = $expiry; |
50 | } |
51 | $this->_is_secure = $is_secure; |
52 | } |
53 | |
54 | /** |
55 | * Sets the host. The cookie rules determine |
56 | * that the first two parts are taken for |
57 | * certain TLDs and three for others. If the |
58 | * new host does not match these rules then the |
59 | * call will fail. |
60 | * @param string $host New hostname. |
61 | * @return boolean True if hostname is valid. |
62 | * @access public |
63 | */ |
64 | function setHost($host) { |
65 | if ($host = $this->_truncateHost($host)) { |
66 | $this->_host = $host; |
67 | return true; |
68 | } |
69 | return false; |
70 | } |
71 | |
72 | /** |
73 | * Accessor for the truncated host to which this |
74 | * cookie applies. |
75 | * @return string Truncated hostname. |
76 | * @access public |
77 | */ |
78 | function getHost() { |
79 | return $this->_host; |
80 | } |
81 | |
82 | /** |
83 | * Test for a cookie being valid for a host name. |
84 | * @param string $host Host to test against. |
85 | * @return boolean True if the cookie would be valid |
86 | * here. |
87 | */ |
88 | function isValidHost($host) { |
89 | return ($this->_truncateHost($host) === $this->getHost()); |
90 | } |
91 | |
92 | /** |
93 | * Extracts just the domain part that determines a |
94 | * cookie's host validity. |
95 | * @param string $host Host name to truncate. |
96 | * @return string Domain or false on a bad host. |
97 | * @access private |
98 | */ |
99 | function _truncateHost($host) { |
100 | if (preg_match('/[a-z\-]+\.(com|edu|net|org|gov|mil|int)$/i', $host, $matches)) { |
101 | return $matches[0]; |
102 | } elseif (preg_match('/[a-z\-]+\.[a-z\-]+\.[a-z\-]+$/i', $host, $matches)) { |
103 | return $matches[0]; |
104 | } |
105 | return false; |
106 | } |
107 | |
108 | /** |
109 | * Accessor for name. |
110 | * @return string Cookie key. |
111 | * @access public |
112 | */ |
113 | function getName() { |
114 | return $this->_name; |
115 | } |
116 | |
117 | /** |
118 | * Accessor for value. A deleted cookie will |
119 | * have an empty string for this. |
120 | * @return string Cookie value. |
121 | * @access public |
122 | */ |
123 | function getValue() { |
124 | return $this->_value; |
125 | } |
126 | |
127 | /** |
128 | * Accessor for path. |
129 | * @return string Valid cookie path. |
130 | * @access public |
131 | */ |
132 | function getPath() { |
133 | return $this->_path; |
134 | } |
135 | |
136 | /** |
137 | * Tests a path to see if the cookie applies |
138 | * there. The test path must be longer or |
139 | * equal to the cookie path. |
140 | * @param string $path Path to test against. |
141 | * @return boolean True if cookie valid here. |
142 | * @access public |
143 | */ |
144 | function isValidPath($path) { |
145 | return (strncmp( |
146 | $this->_fixPath($path), |
147 | $this->getPath(), |
148 | strlen($this->getPath())) == 0); |
149 | } |
150 | |
151 | /** |
152 | * Accessor for expiry. |
153 | * @return string Expiry string. |
154 | * @access public |
155 | */ |
156 | function getExpiry() { |
157 | if (! $this->_expiry) { |
158 | return false; |
159 | } |
160 | return gmdate("D, d M Y H:i:s", $this->_expiry) . " GMT"; |
161 | } |
162 | |
163 | /** |
164 | * Test to see if cookie is expired against |
165 | * the cookie format time or timestamp. |
166 | * Will give true for a session cookie. |
167 | * @param integer/string $now Time to test against. Result |
168 | * will be false if this time |
169 | * is later than the cookie expiry. |
170 | * Can be either a timestamp integer |
171 | * or a cookie format date. |
172 | * @access public |
173 | */ |
174 | function isExpired($now) { |
175 | if (! $this->_expiry) { |
176 | return true; |
177 | } |
178 | if (is_string($now)) { |
179 | $now = strtotime($now); |
180 | } |
181 | return ($this->_expiry < $now); |
182 | } |
183 | |
184 | /** |
185 | * Ages the cookie by the specified number of |
186 | * seconds. |
187 | * @param integer $interval In seconds. |
188 | * @public |
189 | */ |
190 | function agePrematurely($interval) { |
191 | if ($this->_expiry) { |
192 | $this->_expiry -= $interval; |
193 | } |
194 | } |
195 | |
196 | /** |
197 | * Accessor for the secure flag. |
198 | * @return boolean True if cookie needs SSL. |
199 | * @access public |
200 | */ |
201 | function isSecure() { |
202 | return $this->_is_secure; |
203 | } |
204 | |
205 | /** |
206 | * Adds a trailing and leading slash to the path |
207 | * if missing. |
208 | * @param string $path Path to fix. |
209 | * @access private |
210 | */ |
211 | function _fixPath($path) { |
212 | if (substr($path, 0, 1) != '/') { |
213 | $path = '/' . $path; |
214 | } |
215 | if (substr($path, -1, 1) != '/') { |
216 | $path .= '/'; |
217 | } |
218 | return $path; |
219 | } |
220 | } |
221 | |
222 | /** |
223 | * Creates HTTP headers for the end point of |
224 | * a HTTP request. |
225 | * @package SimpleTest |
226 | * @subpackage WebTester |
227 | */ |
228 | class SimpleRoute { |
229 | var $_url; |
230 | |
231 | /** |
232 | * Sets the target URL. |
233 | * @param SimpleUrl $url URL as object. |
234 | * @access public |
235 | */ |
236 | function SimpleRoute($url) { |
237 | $this->_url = $url; |
238 | } |
239 | |
240 | /** |
241 | * Resource name. |
242 | * @return SimpleUrl Current url. |
243 | * @access protected |
244 | */ |
245 | function getUrl() { |
246 | return $this->_url; |
247 | } |
248 | |
249 | /** |
250 | * Creates the first line which is the actual request. |
251 | * @param string $method HTTP request method, usually GET. |
252 | * @return string Request line content. |
253 | * @access protected |
254 | */ |
255 | function _getRequestLine($method) { |
256 | return $method . ' ' . $this->_url->getPath() . |
257 | $this->_url->getEncodedRequest() . ' HTTP/1.0'; |
258 | } |
259 | |
260 | /** |
261 | * Creates the host part of the request. |
262 | * @return string Host line content. |
263 | * @access protected |
264 | */ |
265 | function _getHostLine() { |
266 | $line = 'Host: ' . $this->_url->getHost(); |
267 | if ($this->_url->getPort()) { |
268 | $line .= ':' . $this->_url->getPort(); |
269 | } |
270 | return $line; |
271 | } |
272 | |
273 | /** |
274 | * Opens a socket to the route. |
275 | * @param string $method HTTP request method, usually GET. |
276 | * @param integer $timeout Connection timeout. |
277 | * @return SimpleSocket New socket. |
278 | * @access public |
279 | */ |
280 | function &createConnection($method, $timeout) { |
281 | $default_port = ('https' == $this->_url->getScheme()) ? 443 : 80; |
282 | $socket = &$this->_createSocket( |
283 | $this->_url->getScheme() ? $this->_url->getScheme() : 'http', |
284 | $this->_url->getHost(), |
285 | $this->_url->getPort() ? $this->_url->getPort() : $default_port, |
286 | $timeout); |
287 | if (! $socket->isError()) { |
288 | $socket->write($this->_getRequestLine($method) . "\r\n"); |
289 | $socket->write($this->_getHostLine() . "\r\n"); |
290 | $socket->write("Connection: close\r\n"); |
291 | } |
292 | return $socket; |
293 | } |
294 | |
295 | /** |
296 | * Factory for socket. |
297 | * @param string $scheme Protocol to use. |
298 | * @param string $host Hostname to connect to. |
299 | * @param integer $port Remote port. |
300 | * @param integer $timeout Connection timeout. |
301 | * @return SimpleSocket/SimpleSecureSocket New socket. |
302 | * @access protected |
303 | */ |
304 | function &_createSocket($scheme, $host, $port, $timeout) { |
305 | if (in_array($scheme, array('https'))) { |
306 | return new SimpleSecureSocket($host, $port, $timeout); |
307 | } |
308 | return new SimpleSocket($host, $port, $timeout); |
309 | } |
310 | } |
311 | |
312 | /** |
313 | * Creates HTTP headers for the end point of |
314 | * a HTTP request via a proxy server. |
315 | * @package SimpleTest |
316 | * @subpackage WebTester |
317 | */ |
318 | class SimpleProxyRoute extends SimpleRoute { |
319 | var $_proxy; |
320 | var $_username; |
321 | var $_password; |
322 | |
323 | /** |
324 | * Stashes the proxy address. |
325 | * @param SimpleUrl $url URL as object. |
326 | * @param string $proxy Proxy URL. |
327 | * @param string $username Username for autentication. |
328 | * @param string $password Password for autentication. |
329 | * @access public |
330 | */ |
331 | function SimpleProxyRoute($url, $proxy, $username = false, $password = false) { |
332 | $this->SimpleRoute($url); |
333 | $this->_proxy = $proxy; |
334 | $this->_username = $username; |
335 | $this->_password = $password; |
336 | } |
337 | |
338 | /** |
339 | * Creates the first line which is the actual request. |
340 | * @param string $method HTTP request method, usually GET. |
341 | * @param SimpleUrl $url URL as object. |
342 | * @return string Request line content. |
343 | * @access protected |
344 | */ |
345 | function _getRequestLine($method) { |
346 | $url = $this->getUrl(); |
347 | $scheme = $url->getScheme() ? $url->getScheme() : 'http'; |
348 | $port = $url->getPort() ? ':' . $url->getPort() : ''; |
349 | return $method . ' ' . $scheme . '://' . $url->getHost() . $port . |
350 | $url->getPath() . $url->getEncodedRequest() . ' HTTP/1.0'; |
351 | } |
352 | |
353 | /** |
354 | * Creates the host part of the request. |
355 | * @param SimpleUrl $url URL as object. |
356 | * @return string Host line content. |
357 | * @access protected |
358 | */ |
359 | function _getHostLine() { |
360 | $host = 'Host: ' . $this->_proxy->getHost(); |
361 | $port = $this->_proxy->getPort() ? $this->_proxy->getPort() : 8080; |
362 | return "$host:$port"; |
363 | } |
364 | |
365 | /** |
366 | * Opens a socket to the route. |
367 | * @param string $method HTTP request method, usually GET. |
368 | * @param integer $timeout Connection timeout. |
369 | * @return SimpleSocket New socket. |
370 | * @access public |
371 | */ |
372 | function &createConnection($method, $timeout) { |
373 | $socket = &$this->_createSocket( |
374 | $this->_proxy->getScheme() ? $this->_proxy->getScheme() : 'http', |
375 | $this->_proxy->getHost(), |
376 | $this->_proxy->getPort() ? $this->_proxy->getPort() : 8080, |
377 | $timeout); |
378 | if (! $socket->isError()) { |
379 | $socket->write($this->_getRequestLine($method) . "\r\n"); |
380 | $socket->write($this->_getHostLine() . "\r\n"); |
381 | if ($this->_username && $this->_password) { |
382 | $socket->write('Proxy-Authorization: Basic ' . |
383 | base64_encode($this->_username . ':' . $this->_password) . |
384 | "\r\n"); |
385 | } |
386 | $socket->write("Connection: close\r\n"); |
387 | } |
388 | return $socket; |
389 | } |
390 | } |
391 | |
392 | /** |
393 | * HTTP request for a web page. Factory for |
394 | * HttpResponse object. |
395 | * @package SimpleTest |
396 | * @subpackage WebTester |
397 | */ |
398 | class SimpleHttpRequest { |
399 | var $_route; |
400 | var $_method; |
401 | var $_content; |
402 | var $_headers; |
403 | var $_cookies; |
404 | |
405 | /** |
406 | * Saves the URL ready for fetching. |
407 | * @param SimpleRoute $route Request route. |
408 | * @param string $method HTTP request method, |
409 | * usually GET. |
410 | * @param string $content Content to send with request. |
411 | * @access public |
412 | */ |
413 | function SimpleHttpRequest(&$route, $method, $content = '') { |
414 | $this->_route = &$route; |
415 | $this->_method = $method; |
416 | $this->_content = $content; |
417 | $this->_headers = array(); |
418 | $this->_cookies = array(); |
419 | } |
420 | |
421 | /** |
422 | * Fetches the content and parses the headers. |
423 | * @param integer $timeout Connection timeout. |
424 | * @return SimpleHttpResponse A response which may only have |
425 | * an error. |
426 | * @access public |
427 | */ |
428 | function &fetch($timeout) { |
429 | $socket = &$this->_route->createConnection($this->_method, $timeout); |
430 | if ($socket->isError()) { |
431 | return $this->_createResponse($socket); |
432 | } |
433 | $this->_dispatchRequest($socket, $this->_method, $this->_content); |
434 | return $this->_createResponse($socket); |
435 | } |
436 | |
437 | /** |
438 | * Sends the headers. |
439 | * @param SimpleSocket $socket Open socket. |
440 | * @param string $method HTTP request method, |
441 | * usually GET. |
442 | * @param string $content Content to send with request. |
443 | * @access protected |
444 | */ |
445 | function _dispatchRequest(&$socket, $method, $content) { |
446 | if ($content) { |
447 | $socket->write("Content-Length: " . strlen($content) . "\r\n"); |
448 | $socket->write("Content-Type: application/x-www-form-urlencoded\r\n"); |
449 | } |
450 | foreach ($this->_headers as $header_line) { |
451 | $socket->write($header_line . "\r\n"); |
452 | } |
453 | if (count($this->_cookies) > 0) { |
454 | $socket->write("Cookie: " . $this->_marshallCookies($this->_cookies) . "\r\n"); |
455 | } |
456 | $socket->write("\r\n"); |
457 | if ($content) { |
458 | $socket->write($content); |
459 | } |
460 | } |
461 | |
462 | /** |
463 | * Adds a header line to the request. |
464 | * @param string $header_line Text of header line. |
465 | * @access public |
466 | */ |
467 | function addHeaderLine($header_line) { |
468 | $this->_headers[] = $header_line; |
469 | } |
470 | |
471 | /** |
472 | * Adds a cookie to the request. |
473 | * @param SimpleCookie $cookie Additional cookie. |
474 | * @access public |
475 | */ |
476 | function setCookie($cookie) { |
477 | $this->_cookies[] = $cookie; |
478 | } |
479 | |
480 | /** |
481 | * Serialises the cookie hash ready for |
482 | * transmission. |
483 | * @param hash $cookies Parsed cookies. |
484 | * @return array Cookies in header form. |
485 | * @access private |
486 | */ |
487 | function _marshallCookies($cookies) { |
488 | $cookie_pairs = array(); |
489 | foreach ($cookies as $cookie) { |
490 | $cookie_pairs[] = $cookie->getName() . "=" . $cookie->getValue(); |
491 | } |
492 | return implode(";", $cookie_pairs); |
493 | } |
494 | |
495 | /** |
496 | * Wraps the socket in a response parser. |
497 | * @param SimpleSocket $socket Responding socket. |
498 | * @return SimpleHttpResponse Parsed response object. |
499 | * @access protected |
500 | */ |
501 | function &_createResponse(&$socket) { |
502 | return new SimpleHttpResponse( |
503 | $socket, |
504 | $this->_method, |
505 | $this->_route->getUrl(), |
506 | $this->_content); |
507 | } |
508 | } |
509 | |
510 | /** |
511 | * Request with data to send. Usually PUT or POST. |
512 | * @package SimpleTest |
513 | * @subpackage WebTester |
514 | */ |
515 | class SimpleHttpPostRequest extends SimpleHttpRequest { |
516 | |
517 | /** |
518 | * Cretaes an HTML form request. |
519 | * @param SimpleRoute $route Request target. |
520 | * @param array $parameters Content to send. |
521 | * @access public |
522 | */ |
523 | function SimpleHttpPostRequest($route, $parameters) { |
524 | $this->SimpleHttpRequest($route, 'POST', $parameters); |
525 | } |
526 | |
527 | /** |
528 | * Sends the headers. |
529 | * @param SimpleSocket $socket Open socket. |
530 | * @param string $method HTTP request method, |
531 | * usually GET. |
532 | * @param string $content Content to send with request. |
533 | * @access protected |
534 | */ |
535 | function _dispatchRequest(&$socket, $method, $content) { |
536 | parent::_dispatchRequest( |
537 | $socket, |
538 | $method, |
539 | SimpleUrl::encodeRequest($content)); |
540 | } |
541 | } |
542 | |
543 | /** |
544 | * Collection of header lines in the response. |
545 | * @package SimpleTest |
546 | * @subpackage WebTester |
547 | */ |
548 | class SimpleHttpHeaders { |
549 | var $_raw_headers; |
550 | var $_response_code; |
551 | var $_http_version; |
552 | var $_mime_type; |
553 | var $_location; |
554 | var $_cookies; |
555 | var $_authentication; |
556 | var $_realm; |
557 | |
558 | /** |
559 | * Parses the incoming header block. |
560 | * @param string $headers Header block. |
561 | * @access public |
562 | */ |
563 | function SimpleHttpHeaders($headers) { |
564 | $this->_raw_headers = $headers; |
565 | $this->_response_code = false; |
566 | $this->_http_version = false; |
567 | $this->_mime_type = ''; |
568 | $this->_location = false; |
569 | $this->_cookies = array(); |
570 | $this->_authentication = false; |
571 | $this->_realm = false; |
575dd9be |
572 | foreach (explode("\r\n", $headers) as $header_line) { |
0337d704 |
573 | $this->_parseHeaderLine($header_line); |
574 | } |
575 | } |
576 | |
577 | /** |
578 | * Accessor for parsed HTTP protocol version. |
579 | * @return integer HTTP error code. |
580 | * @access public |
581 | */ |
582 | function getHttpVersion() { |
583 | return $this->_http_version; |
584 | } |
585 | |
586 | /** |
587 | * Accessor for raw header block. |
588 | * @return string All headers as raw string. |
589 | * @access public |
590 | */ |
591 | function getRaw() { |
592 | return $this->_raw_headers; |
593 | } |
594 | |
595 | /** |
596 | * Accessor for parsed HTTP error code. |
597 | * @return integer HTTP error code. |
598 | * @access public |
599 | */ |
600 | function getResponseCode() { |
601 | return (integer)$this->_response_code; |
602 | } |
603 | |
604 | /** |
605 | * Returns the redirected URL or false if |
606 | * no redirection. |
607 | * @return string URL or false for none. |
608 | * @access public |
609 | */ |
610 | function getLocation() { |
611 | return $this->_location; |
612 | } |
613 | |
614 | /** |
615 | * Test to see if the response is a valid redirect. |
616 | * @return boolean True if valid redirect. |
617 | * @access public |
618 | */ |
619 | function isRedirect() { |
620 | return in_array($this->_response_code, array(301, 302, 303, 307)) && |
621 | (boolean)$this->getLocation(); |
622 | } |
623 | |
624 | /** |
625 | * Test to see if the response is an authentication |
626 | * challenge. |
627 | * @return boolean True if challenge. |
628 | * @access public |
629 | */ |
630 | function isChallenge() { |
631 | return ($this->_response_code == 401) && |
632 | (boolean)$this->_authentication && |
633 | (boolean)$this->_realm; |
634 | } |
635 | |
636 | /** |
637 | * Accessor for MIME type header information. |
638 | * @return string MIME type. |
639 | * @access public |
640 | */ |
641 | function getMimeType() { |
642 | return $this->_mime_type; |
643 | } |
644 | |
645 | /** |
646 | * Accessor for authentication type. |
647 | * @return string Type. |
648 | * @access public |
649 | */ |
650 | function getAuthentication() { |
651 | return $this->_authentication; |
652 | } |
653 | |
654 | /** |
655 | * Accessor for security realm. |
656 | * @return string Realm. |
657 | * @access public |
658 | */ |
659 | function getRealm() { |
660 | return $this->_realm; |
661 | } |
662 | |
663 | /** |
664 | * Accessor for any new cookies. |
665 | * @return array List of new cookies. |
666 | * @access public |
667 | */ |
668 | function getNewCookies() { |
669 | return $this->_cookies; |
670 | } |
671 | |
672 | /** |
673 | * Called on each header line to accumulate the held |
674 | * data within the class. |
675 | * @param string $header_line One line of header. |
676 | * @access protected |
677 | */ |
678 | function _parseHeaderLine($header_line) { |
679 | if (preg_match('/HTTP\/(\d+\.\d+)\s+(.*?)\s/i', $header_line, $matches)) { |
680 | $this->_http_version = $matches[1]; |
681 | $this->_response_code = $matches[2]; |
682 | } |
683 | if (preg_match('/Content-type:\s*(.*)/i', $header_line, $matches)) { |
684 | $this->_mime_type = trim($matches[1]); |
685 | } |
686 | if (preg_match('/Location:\s*(.*)/i', $header_line, $matches)) { |
687 | $this->_location = trim($matches[1]); |
688 | } |
689 | if (preg_match('/Set-cookie:(.*)/i', $header_line, $matches)) { |
690 | $this->_cookies[] = $this->_parseCookie($matches[1]); |
691 | } |
692 | if (preg_match('/WWW-Authenticate:\s+(\S+)\s+realm=\"(.*?)\"/i', $header_line, $matches)) { |
693 | $this->_authentication = $matches[1]; |
694 | $this->_realm = trim($matches[2]); |
695 | } |
696 | } |
697 | |
698 | /** |
699 | * Parse the Set-cookie content. |
700 | * @param string $cookie_line Text after "Set-cookie:" |
701 | * @return SimpleCookie New cookie object. |
702 | * @access private |
703 | */ |
704 | function _parseCookie($cookie_line) { |
575dd9be |
705 | $parts = explode(";", $cookie_line); |
0337d704 |
706 | $cookie = array(); |
707 | preg_match('/\s*(.*?)\s*=(.*)/', array_shift($parts), $cookie); |
708 | foreach ($parts as $part) { |
709 | if (preg_match('/\s*(.*?)\s*=(.*)/', $part, $matches)) { |
710 | $cookie[$matches[1]] = trim($matches[2]); |
711 | } |
712 | } |
713 | return new SimpleCookie( |
714 | $cookie[1], |
715 | trim($cookie[2]), |
716 | isset($cookie["path"]) ? $cookie["path"] : "", |
717 | isset($cookie["expires"]) ? $cookie["expires"] : false); |
718 | } |
719 | } |
720 | |
721 | /** |
722 | * Basic HTTP response. |
723 | * @package SimpleTest |
724 | * @subpackage WebTester |
725 | */ |
726 | class SimpleHttpResponse extends StickyError { |
727 | var $_method; |
728 | var $_url; |
729 | var $_request_data; |
730 | var $_sent; |
731 | var $_content; |
732 | var $_headers; |
733 | |
734 | /** |
735 | * Constructor. Reads and parses the incoming |
736 | * content and headers. |
737 | * @param SimpleSocket $socket Network connection to fetch |
738 | * response text from. |
739 | * @param string $method HTTP request method. |
740 | * @param SimpleUrl $url Resource name. |
741 | * @param mixed $request_data Record of content sent. |
742 | * @access public |
743 | */ |
744 | function SimpleHttpResponse(&$socket, $method, $url, $request_data = '') { |
745 | $this->StickyError(); |
746 | $this->_method = $method; |
747 | $this->_url = $url; |
748 | $this->_request_data = $request_data; |
749 | $this->_sent = $socket->getSent(); |
750 | $this->_content = false; |
751 | $raw = $this->_readAll($socket); |
752 | if ($socket->isError()) { |
753 | $this->_setError('Error reading socket [' . $socket->getError() . ']'); |
754 | return; |
755 | } |
756 | $this->_parse($raw); |
757 | } |
758 | |
759 | /** |
760 | * Splits up the headers and the rest of the content. |
761 | * @param string $raw Content to parse. |
762 | * @access private |
763 | */ |
764 | function _parse($raw) { |
765 | if (! $raw) { |
766 | $this->_setError('Nothing fetched'); |
767 | $this->_headers = &new SimpleHttpHeaders(''); |
768 | } elseif (! strstr($raw, "\r\n\r\n")) { |
769 | $this->_setError('Could not parse headers'); |
770 | $this->_headers = &new SimpleHttpHeaders($raw); |
771 | } else { |
575dd9be |
772 | list($headers, $this->_content) = explode("\r\n\r\n", $raw, 2); |
0337d704 |
773 | $this->_headers = &new SimpleHttpHeaders($headers); |
774 | } |
775 | } |
776 | |
777 | /** |
778 | * Original request method. |
779 | * @return string GET, POST or HEAD. |
780 | * @access public |
781 | */ |
782 | function getMethod() { |
783 | return $this->_method; |
784 | } |
785 | |
786 | /** |
787 | * Resource name. |
788 | * @return SimpleUrl Current url. |
789 | * @access public |
790 | */ |
791 | function getUrl() { |
792 | return $this->_url; |
793 | } |
794 | |
795 | /** |
796 | * Original request data. |
797 | * @return mixed Sent content. |
798 | * @access public |
799 | */ |
800 | function getRequestData() { |
801 | return $this->_request_data; |
802 | } |
803 | |
804 | /** |
805 | * Raw request that was sent down the wire. |
806 | * @return string Bytes actually sent. |
807 | * @access public |
808 | */ |
809 | function getSent() { |
810 | return $this->_sent; |
811 | } |
812 | |
813 | /** |
814 | * Accessor for the content after the last |
815 | * header line. |
816 | * @return string All content. |
817 | * @access public |
818 | */ |
819 | function getContent() { |
820 | return $this->_content; |
821 | } |
822 | |
823 | /** |
824 | * Accessor for header block. The response is the |
825 | * combination of this and the content. |
826 | * @return SimpleHeaders Wrapped header block. |
827 | * @access public |
828 | */ |
829 | function getHeaders() { |
830 | return $this->_headers; |
831 | } |
832 | |
833 | /** |
834 | * Accessor for any new cookies. |
835 | * @return array List of new cookies. |
836 | * @access public |
837 | */ |
838 | function getNewCookies() { |
839 | return $this->_headers->getNewCookies(); |
840 | } |
841 | |
842 | /** |
843 | * Reads the whole of the socket output into a |
844 | * single string. |
845 | * @param SimpleSocket $socket Unread socket. |
846 | * @return string Raw output if successful |
847 | * else false. |
848 | * @access private |
849 | */ |
850 | function _readAll(&$socket) { |
851 | $all = ''; |
852 | while (! $this->_isLastPacket($next = $socket->read())) { |
853 | $all .= $next; |
854 | } |
855 | return $all; |
856 | } |
857 | |
858 | /** |
859 | * Test to see if the packet from the socket is the |
860 | * last one. |
861 | * @param string $packet Chunk to interpret. |
862 | * @return boolean True if empty or EOF. |
863 | * @access private |
864 | */ |
865 | function _isLastPacket($packet) { |
866 | if (is_string($packet)) { |
867 | return $packet === ''; |
868 | } |
869 | return ! $packet; |
870 | } |
871 | } |
575dd9be |
872 | ?> |