Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
    /**
3
     *	base include file for SimpleTest
4
     *	@package	SimpleTest
5
     *	@subpackage	WebTester
6
     *	@version	$Id: http.php 1398 2006-09-08 19:31:03Z xue $
7
     */
8
 
9
    /**#@+
10
     *	include other SimpleTest class files
11
     */
12
    require_once(dirname(__FILE__) . '/socket.php');
13
    require_once(dirname(__FILE__) . '/cookies.php');
14
    require_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
     */
23
    class SimpleRoute {
24
        protected $_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
     */
115
    class SimpleProxyRoute extends SimpleRoute {
116
        protected $_proxy;
117
        protected $_username;
118
        protected $_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
     */
196
    class SimpleHttpRequest {
197
        protected $_route;
198
        protected $_encoding;
199
        protected $_headers;
200
        protected $_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
     */
295
    class SimpleHttpHeaders {
296
        protected $_raw_headers;
297
        protected $_response_code;
298
        protected $_http_version;
299
        protected $_mime_type;
300
        protected $_location;
301
        protected $_cookies;
302
        protected $_authentication;
303
        protected $_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
     */
481
    class SimpleHttpResponse extends SimpleStickyError {
482
        protected $_url;
483
        protected $_encoding;
484
        protected $_sent;
485
        protected $_content;
486
        protected $_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
?>