source: spip-zone/_plugins_/_stable_/simpletest/simpletest/http.php @ 24737

Last change on this file since 24737 was 24737, checked in by marcimat@…, 12 years ago

Plugin pour des tests unitaires avec SPIP utilisant les librairies SimpleTest? (version 1.0.1).

L'api de simpleTest est enrichie pour proposer des tests spécifiques utilisant les fonctions de SPIP. La documentation sur Contrib va suivre.

Une remarque importante : pour que les tests fonctionnent, il ne PEUT pas y avoir le lien symbolique sur les répertoires plugins ou de simpleTests

Le répertoire tests/ (qui contient les premiers tests unitaires dans un format qui n'est pas simpleTest) peut être ajouté à la racine du site, les tests SimpleTests? se lanceront dedans correctement si ce plugin est activé.

File size: 18.8 KB
Line 
1<?php
2/**
3 *  base include file for SimpleTest
4 *  @package    SimpleTest
5 *  @subpackage WebTester
6 *  @version    $Id: http.php 1722 2008-04-07 19:30:56Z lastcraft $
7 */
8
9/**#@+
10 *  include other SimpleTest class files
11 */
12require_once(dirname(__FILE__) . '/socket.php');
13require_once(dirname(__FILE__) . '/cookies.php');
14require_once(dirname(__FILE__) . '/url.php');
15/**#@-*/
16
17/**
18 *    Creates HTTP headers for the end point of
19 *    a HTTP request.
20 *    @package SimpleTest
21 *    @subpackage WebTester
22 */
23class SimpleRoute {
24    var $_url;
25   
26    /**
27     *    Sets the target URL.
28     *    @param SimpleUrl $url   URL as object.
29     *    @access public
30     */
31    function SimpleRoute($url) {
32        $this->_url = $url;
33    }
34   
35    /**
36     *    Resource name.
37     *    @return SimpleUrl        Current url.
38     *    @access protected
39     */
40    function getUrl() {
41        return $this->_url;
42    }
43   
44    /**
45     *    Creates the first line which is the actual request.
46     *    @param string $method   HTTP request method, usually GET.
47     *    @return string          Request line content.
48     *    @access protected
49     */
50    function _getRequestLine($method) {
51        return $method . ' ' . $this->_url->getPath() .
52                $this->_url->getEncodedRequest() . ' HTTP/1.0';
53    }
54   
55    /**
56     *    Creates the host part of the request.
57     *    @return string          Host line content.
58     *    @access protected
59     */
60    function _getHostLine() {
61        $line = 'Host: ' . $this->_url->getHost();
62        if ($this->_url->getPort()) {
63            $line .= ':' . $this->_url->getPort();
64        }
65        return $line;
66    }
67   
68    /**
69     *    Opens a socket to the route.
70     *    @param string $method      HTTP request method, usually GET.
71     *    @param integer $timeout    Connection timeout.
72     *    @return SimpleSocket       New socket.
73     *    @access public
74     */
75    function &createConnection($method, $timeout) {
76        $default_port = ('https' == $this->_url->getScheme()) ? 443 : 80;
77        $socket = &$this->_createSocket(
78                $this->_url->getScheme() ? $this->_url->getScheme() : 'http',
79                $this->_url->getHost(),
80                $this->_url->getPort() ? $this->_url->getPort() : $default_port,
81                $timeout);
82        if (! $socket->isError()) {
83            $socket->write($this->_getRequestLine($method) . "\r\n");
84            $socket->write($this->_getHostLine() . "\r\n");
85            $socket->write("Connection: close\r\n");
86        }
87        return $socket;
88    }
89   
90    /**
91     *    Factory for socket.
92     *    @param string $scheme                   Protocol to use.
93     *    @param string $host                     Hostname to connect to.
94     *    @param integer $port                    Remote port.
95     *    @param integer $timeout                 Connection timeout.
96     *    @return SimpleSocket/SimpleSecureSocket New socket.
97     *    @access protected
98     */
99    function &_createSocket($scheme, $host, $port, $timeout) {
100        if (in_array($scheme, array('https'))) {
101            $socket = &new SimpleSecureSocket($host, $port, $timeout);
102        } else {
103            $socket = &new SimpleSocket($host, $port, $timeout);
104        }
105        return $socket;
106    }
107}
108
109/**
110 *    Creates HTTP headers for the end point of
111 *    a HTTP request via a proxy server.
112 *    @package SimpleTest
113 *    @subpackage WebTester
114 */
115class SimpleProxyRoute extends SimpleRoute {
116    var $_proxy;
117    var $_username;
118    var $_password;
119   
120    /**
121     *    Stashes the proxy address.
122     *    @param SimpleUrl $url     URL as object.
123     *    @param string $proxy      Proxy URL.
124     *    @param string $username   Username for autentication.
125     *    @param string $password   Password for autentication.
126     *    @access public
127     */
128    function SimpleProxyRoute($url, $proxy, $username = false, $password = false) {
129        $this->SimpleRoute($url);
130        $this->_proxy = $proxy;
131        $this->_username = $username;
132        $this->_password = $password;
133    }
134   
135    /**
136     *    Creates the first line which is the actual request.
137     *    @param string $method   HTTP request method, usually GET.
138     *    @param SimpleUrl $url   URL as object.
139     *    @return string          Request line content.
140     *    @access protected
141     */
142    function _getRequestLine($method) {
143        $url = $this->getUrl();
144        $scheme = $url->getScheme() ? $url->getScheme() : 'http';
145        $port = $url->getPort() ? ':' . $url->getPort() : '';
146        return $method . ' ' . $scheme . '://' . $url->getHost() . $port .
147                $url->getPath() . $url->getEncodedRequest() . ' HTTP/1.0';
148    }
149   
150    /**
151     *    Creates the host part of the request.
152     *    @param SimpleUrl $url   URL as object.
153     *    @return string          Host line content.
154     *    @access protected
155     */
156    function _getHostLine() {
157        $host = 'Host: ' . $this->_proxy->getHost();
158        $port = $this->_proxy->getPort() ? $this->_proxy->getPort() : 8080;
159        return "$host:$port";
160    }
161   
162    /**
163     *    Opens a socket to the route.
164     *    @param string $method       HTTP request method, usually GET.
165     *    @param integer $timeout     Connection timeout.
166     *    @return SimpleSocket        New socket.
167     *    @access public
168     */
169    function &createConnection($method, $timeout) {
170        $socket = &$this->_createSocket(
171                $this->_proxy->getScheme() ? $this->_proxy->getScheme() : 'http',
172                $this->_proxy->getHost(),
173                $this->_proxy->getPort() ? $this->_proxy->getPort() : 8080,
174                $timeout);
175        if ($socket->isError()) {
176            return $socket;
177        }
178        $socket->write($this->_getRequestLine($method) . "\r\n");
179        $socket->write($this->_getHostLine() . "\r\n");
180        if ($this->_username && $this->_password) {
181            $socket->write('Proxy-Authorization: Basic ' .
182                    base64_encode($this->_username . ':' . $this->_password) .
183                    "\r\n");
184        }
185        $socket->write("Connection: close\r\n");
186        return $socket;
187    }
188}
189
190/**
191 *    HTTP request for a web page. Factory for
192 *    HttpResponse object.
193 *    @package SimpleTest
194 *    @subpackage WebTester
195 */
196class SimpleHttpRequest {
197    var $_route;
198    var $_encoding;
199    var $_headers;
200    var $_cookies;
201   
202    /**
203     *    Builds the socket request from the different pieces.
204     *    These include proxy information, URL, cookies, headers,
205     *    request method and choice of encoding.
206     *    @param SimpleRoute $route              Request route.
207     *    @param SimpleFormEncoding $encoding    Content to send with
208     *                                           request.
209     *    @access public
210     */
211    function SimpleHttpRequest(&$route, $encoding) {
212        $this->_route = &$route;
213        $this->_encoding = $encoding;
214        $this->_headers = array();
215        $this->_cookies = array();
216    }
217   
218    /**
219     *    Dispatches the content to the route's socket.
220     *    @param integer $timeout      Connection timeout.
221     *    @return SimpleHttpResponse   A response which may only have
222     *                                 an error, but hopefully has a
223     *                                 complete web page.
224     *    @access public
225     */
226    function &fetch($timeout) {
227        $socket = &$this->_route->createConnection($this->_encoding->getMethod(), $timeout);
228        if (! $socket->isError()) {
229            $this->_dispatchRequest($socket, $this->_encoding);
230        }
231        $response = &$this->_createResponse($socket);
232        return $response;
233    }
234   
235    /**
236     *    Sends the headers.
237     *    @param SimpleSocket $socket           Open socket.
238     *    @param string $method                 HTTP request method,
239     *                                          usually GET.
240     *    @param SimpleFormEncoding $encoding   Content to send with request.
241     *    @access private
242     */
243    function _dispatchRequest(&$socket, $encoding) {
244        foreach ($this->_headers as $header_line) {
245            $socket->write($header_line . "\r\n");
246        }
247        if (count($this->_cookies) > 0) {
248            $socket->write("Cookie: " . implode(";", $this->_cookies) . "\r\n");
249        }
250        $encoding->writeHeadersTo($socket);
251        $socket->write("\r\n");
252        $encoding->writeTo($socket);
253    }
254   
255    /**
256     *    Adds a header line to the request.
257     *    @param string $header_line    Text of full header line.
258     *    @access public
259     */
260    function addHeaderLine($header_line) {
261        $this->_headers[] = $header_line;
262    }
263   
264    /**
265     *    Reads all the relevant cookies from the
266     *    cookie jar.
267     *    @param SimpleCookieJar $jar     Jar to read
268     *    @param SimpleUrl $url           Url to use for scope.
269     *    @access public
270     */
271    function readCookiesFromJar($jar, $url) {
272        $this->_cookies = $jar->selectAsPairs($url);
273    }
274   
275    /**
276     *    Wraps the socket in a response parser.
277     *    @param SimpleSocket $socket   Responding socket.
278     *    @return SimpleHttpResponse    Parsed response object.
279     *    @access protected
280     */
281    function &_createResponse(&$socket) {
282        $response = &new SimpleHttpResponse(
283                $socket,
284                $this->_route->getUrl(),
285                $this->_encoding);
286        return $response;
287    }
288}
289
290/**
291 *    Collection of header lines in the response.
292 *    @package SimpleTest
293 *    @subpackage WebTester
294 */
295class SimpleHttpHeaders {
296    var $_raw_headers;
297    var $_response_code;
298    var $_http_version;
299    var $_mime_type;
300    var $_location;
301    var $_cookies;
302    var $_authentication;
303    var $_realm;
304   
305    /**
306     *    Parses the incoming header block.
307     *    @param string $headers     Header block.
308     *    @access public
309     */
310    function SimpleHttpHeaders($headers) {
311        $this->_raw_headers = $headers;
312        $this->_response_code = false;
313        $this->_http_version = false;
314        $this->_mime_type = '';
315        $this->_location = false;
316        $this->_cookies = array();
317        $this->_authentication = false;
318        $this->_realm = false;
319        foreach (split("\r\n", $headers) as $header_line) {
320            $this->_parseHeaderLine($header_line);
321        }
322    }
323   
324    /**
325     *    Accessor for parsed HTTP protocol version.
326     *    @return integer           HTTP error code.
327     *    @access public
328     */
329    function getHttpVersion() {
330        return $this->_http_version;
331    }
332   
333    /**
334     *    Accessor for raw header block.
335     *    @return string        All headers as raw string.
336     *    @access public
337     */
338    function getRaw() {
339        return $this->_raw_headers;
340    }
341   
342    /**
343     *    Accessor for parsed HTTP error code.
344     *    @return integer           HTTP error code.
345     *    @access public
346     */
347    function getResponseCode() {
348        return (integer)$this->_response_code;
349    }
350   
351    /**
352     *    Returns the redirected URL or false if
353     *    no redirection.
354     *    @return string      URL or false for none.
355     *    @access public
356     */
357    function getLocation() {
358        return $this->_location;
359    }
360   
361    /**
362     *    Test to see if the response is a valid redirect.
363     *    @return boolean       True if valid redirect.
364     *    @access public
365     */
366    function isRedirect() {
367        return in_array($this->_response_code, array(301, 302, 303, 307)) &&
368                (boolean)$this->getLocation();
369    }
370   
371    /**
372     *    Test to see if the response is an authentication
373     *    challenge.
374     *    @return boolean       True if challenge.
375     *    @access public
376     */
377    function isChallenge() {
378        return ($this->_response_code == 401) &&
379                (boolean)$this->_authentication &&
380                (boolean)$this->_realm;
381    }
382   
383    /**
384     *    Accessor for MIME type header information.
385     *    @return string           MIME type.
386     *    @access public
387     */
388    function getMimeType() {
389        return $this->_mime_type;
390    }
391   
392    /**
393     *    Accessor for authentication type.
394     *    @return string        Type.
395     *    @access public
396     */
397    function getAuthentication() {
398        return $this->_authentication;
399    }
400   
401    /**
402     *    Accessor for security realm.
403     *    @return string        Realm.
404     *    @access public
405     */
406    function getRealm() {
407        return $this->_realm;
408    }
409   
410    /**
411     *    Writes new cookies to the cookie jar.
412     *    @param SimpleCookieJar $jar   Jar to write to.
413     *    @param SimpleUrl $url         Host and path to write under.
414     *    @access public
415     */
416    function writeCookiesToJar(&$jar, $url) {
417        foreach ($this->_cookies as $cookie) {
418            $jar->setCookie(
419                    $cookie->getName(),
420                    $cookie->getValue(),
421                    $url->getHost(),
422                    $cookie->getPath(),
423                    $cookie->getExpiry());
424        }
425    }
426
427    /**
428     *    Called on each header line to accumulate the held
429     *    data within the class.
430     *    @param string $header_line        One line of header.
431     *    @access protected
432     */
433    function _parseHeaderLine($header_line) {
434        if (preg_match('/HTTP\/(\d+\.\d+)\s+(\d+)/i', $header_line, $matches)) {
435            $this->_http_version = $matches[1];
436            $this->_response_code = $matches[2];
437        }
438        if (preg_match('/Content-type:\s*(.*)/i', $header_line, $matches)) {
439            $this->_mime_type = trim($matches[1]);
440        }
441        if (preg_match('/Location:\s*(.*)/i', $header_line, $matches)) {
442            $this->_location = trim($matches[1]);
443        }
444        if (preg_match('/Set-cookie:(.*)/i', $header_line, $matches)) {
445            $this->_cookies[] = $this->_parseCookie($matches[1]);
446        }
447        if (preg_match('/WWW-Authenticate:\s+(\S+)\s+realm=\"(.*?)\"/i', $header_line, $matches)) {
448            $this->_authentication = $matches[1];
449            $this->_realm = trim($matches[2]);
450        }
451    }
452   
453    /**
454     *    Parse the Set-cookie content.
455     *    @param string $cookie_line    Text after "Set-cookie:"
456     *    @return SimpleCookie          New cookie object.
457     *    @access private
458     */
459    function _parseCookie($cookie_line) {
460        $parts = split(";", $cookie_line);
461        $cookie = array();
462        preg_match('/\s*(.*?)\s*=(.*)/', array_shift($parts), $cookie);
463        foreach ($parts as $part) {
464            if (preg_match('/\s*(.*?)\s*=(.*)/', $part, $matches)) {
465                $cookie[$matches[1]] = trim($matches[2]);
466            }
467        }
468        return new SimpleCookie(
469                $cookie[1],
470                trim($cookie[2]),
471                isset($cookie["path"]) ? $cookie["path"] : "",
472                isset($cookie["expires"]) ? $cookie["expires"] : false);
473    }
474}
475
476/**
477 *    Basic HTTP response.
478 *    @package SimpleTest
479 *    @subpackage WebTester
480 */
481class SimpleHttpResponse extends SimpleStickyError {
482    var $_url;
483    var $_encoding;
484    var $_sent;
485    var $_content;
486    var $_headers;
487   
488    /**
489     *    Constructor. Reads and parses the incoming
490     *    content and headers.
491     *    @param SimpleSocket $socket   Network connection to fetch
492     *                                  response text from.
493     *    @param SimpleUrl $url         Resource name.
494     *    @param mixed $encoding        Record of content sent.
495     *    @access public
496     */
497    function SimpleHttpResponse(&$socket, $url, $encoding) {
498        $this->SimpleStickyError();
499        $this->_url = $url;
500        $this->_encoding = $encoding;
501        $this->_sent = $socket->getSent();
502        $this->_content = false;
503        $raw = $this->_readAll($socket);
504        if ($socket->isError()) {
505            $this->_setError('Error reading socket [' . $socket->getError() . ']');
506            return;
507        }
508        $this->_parse($raw);
509    }
510   
511    /**
512     *    Splits up the headers and the rest of the content.
513     *    @param string $raw    Content to parse.
514     *    @access private
515     */
516    function _parse($raw) {
517        if (! $raw) {
518            $this->_setError('Nothing fetched');
519            $this->_headers = &new SimpleHttpHeaders('');
520        } elseif (! strstr($raw, "\r\n\r\n")) {
521            $this->_setError('Could not split headers from content');
522            $this->_headers = &new SimpleHttpHeaders($raw);
523        } else {
524            list($headers, $this->_content) = split("\r\n\r\n", $raw, 2);
525            $this->_headers = &new SimpleHttpHeaders($headers);
526        }
527    }
528   
529    /**
530     *    Original request method.
531     *    @return string        GET, POST or HEAD.
532     *    @access public
533     */
534    function getMethod() {
535        return $this->_encoding->getMethod();
536    }
537   
538    /**
539     *    Resource name.
540     *    @return SimpleUrl        Current url.
541     *    @access public
542     */
543    function getUrl() {
544        return $this->_url;
545    }
546   
547    /**
548     *    Original request data.
549     *    @return mixed              Sent content.
550     *    @access public
551     */
552    function getRequestData() {
553        return $this->_encoding;
554    }
555   
556    /**
557     *    Raw request that was sent down the wire.
558     *    @return string        Bytes actually sent.
559     *    @access public
560     */
561    function getSent() {
562        return $this->_sent;
563    }
564   
565    /**
566     *    Accessor for the content after the last
567     *    header line.
568     *    @return string           All content.
569     *    @access public
570     */
571    function getContent() {
572        return $this->_content;
573    }
574   
575    /**
576     *    Accessor for header block. The response is the
577     *    combination of this and the content.
578     *    @return SimpleHeaders        Wrapped header block.
579     *    @access public
580     */
581    function getHeaders() {
582        return $this->_headers;
583    }
584   
585    /**
586     *    Accessor for any new cookies.
587     *    @return array       List of new cookies.
588     *    @access public
589     */
590    function getNewCookies() {
591        return $this->_headers->getNewCookies();
592    }
593   
594    /**
595     *    Reads the whole of the socket output into a
596     *    single string.
597     *    @param SimpleSocket $socket  Unread socket.
598     *    @return string               Raw output if successful
599     *                                 else false.
600     *    @access private
601     */
602    function _readAll(&$socket) {
603        $all = '';
604        while (! $this->_isLastPacket($next = $socket->read())) {
605            $all .= $next;
606        }
607        return $all;
608    }
609   
610    /**
611     *    Test to see if the packet from the socket is the
612     *    last one.
613     *    @param string $packet    Chunk to interpret.
614     *    @return boolean          True if empty or EOF.
615     *    @access private
616     */
617    function _isLastPacket($packet) {
618        if (is_string($packet)) {
619            return $packet === '';
620        }
621        return ! $packet;
622    }
623}
624?>
Note: See TracBrowser for help on using the repository browser.