| [ Index ] |
PHP Cross Reference of Limb3 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * base include file for SimpleTest 4 * @package SimpleTest 5 * @subpackage WebTester 6 * @version $Id: http.php 5999 2007-06-18 13:13:08Z pachanga $ 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 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 */ 115 class 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 */ 196 class 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 */ 295 class 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 */ 481 class 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(