Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/*** base include file for SimpleTest* @package SimpleTest* @subpackage WebTester* @version $Id: http.php 1398 2006-09-08 19:31:03Z xue $*//**#@+* include other SimpleTest class files*/require_once(dirname(__FILE__) . '/socket.php');require_once(dirname(__FILE__) . '/cookies.php');require_once(dirname(__FILE__) . '/url.php');/**#@-*//*** Creates HTTP headers for the end point of* a HTTP request.* @package SimpleTest* @subpackage WebTester*/class SimpleRoute {protected $_url;/*** Sets the target URL.* @param SimpleUrl $url URL as object.* @access public*/function SimpleRoute($url) {$this->_url = $url;}/*** Resource name.* @return SimpleUrl Current url.* @access protected*/function getUrl() {return $this->_url;}/*** Creates the first line which is the actual request.* @param string $method HTTP request method, usually GET.* @return string Request line content.* @access protected*/function _getRequestLine($method) {return $method . ' ' . $this->_url->getPath() .$this->_url->getEncodedRequest() . ' HTTP/1.0';}/*** Creates the host part of the request.* @return string Host line content.* @access protected*/function _getHostLine() {$line = 'Host: ' . $this->_url->getHost();if ($this->_url->getPort()) {$line .= ':' . $this->_url->getPort();}return $line;}/*** Opens a socket to the route.* @param string $method HTTP request method, usually GET.* @param integer $timeout Connection timeout.* @return SimpleSocket New socket.* @access public*/function &createConnection($method, $timeout) {$default_port = ('https' == $this->_url->getScheme()) ? 443 : 80;$socket = $this->_createSocket($this->_url->getScheme() ? $this->_url->getScheme() : 'http',$this->_url->getHost(),$this->_url->getPort() ? $this->_url->getPort() : $default_port,$timeout);if (! $socket->isError()) {$socket->write($this->_getRequestLine($method) . "\r\n");$socket->write($this->_getHostLine() . "\r\n");$socket->write("Connection: close\r\n");}return $socket;}/*** Factory for socket.* @param string $scheme Protocol to use.* @param string $host Hostname to connect to.* @param integer $port Remote port.* @param integer $timeout Connection timeout.* @return SimpleSocket/SimpleSecureSocket New socket.* @access protected*/function &_createSocket($scheme, $host, $port, $timeout) {if (in_array($scheme, array('https'))) {$socket = new SimpleSecureSocket($host, $port, $timeout);} else {$socket = new SimpleSocket($host, $port, $timeout);}return $socket;}}/*** Creates HTTP headers for the end point of* a HTTP request via a proxy server.* @package SimpleTest* @subpackage WebTester*/class SimpleProxyRoute extends SimpleRoute {protected $_proxy;protected $_username;protected $_password;/*** Stashes the proxy address.* @param SimpleUrl $url URL as object.* @param string $proxy Proxy URL.* @param string $username Username for autentication.* @param string $password Password for autentication.* @access public*/function SimpleProxyRoute($url, $proxy, $username = false, $password = false) {$this->SimpleRoute($url);$this->_proxy = $proxy;$this->_username = $username;$this->_password = $password;}/*** Creates the first line which is the actual request.* @param string $method HTTP request method, usually GET.* @param SimpleUrl $url URL as object.* @return string Request line content.* @access protected*/function _getRequestLine($method) {$url = $this->getUrl();$scheme = $url->getScheme() ? $url->getScheme() : 'http';$port = $url->getPort() ? ':' . $url->getPort() : '';return $method . ' ' . $scheme . '://' . $url->getHost() . $port .$url->getPath() . $url->getEncodedRequest() . ' HTTP/1.0';}/*** Creates the host part of the request.* @param SimpleUrl $url URL as object.* @return string Host line content.* @access protected*/function _getHostLine() {$host = 'Host: ' . $this->_proxy->getHost();$port = $this->_proxy->getPort() ? $this->_proxy->getPort() : 8080;return "$host:$port";}/*** Opens a socket to the route.* @param string $method HTTP request method, usually GET.* @param integer $timeout Connection timeout.* @return SimpleSocket New socket.* @access public*/function &createConnection($method, $timeout) {$socket = $this->_createSocket($this->_proxy->getScheme() ? $this->_proxy->getScheme() : 'http',$this->_proxy->getHost(),$this->_proxy->getPort() ? $this->_proxy->getPort() : 8080,$timeout);if ($socket->isError()) {return $socket;}$socket->write($this->_getRequestLine($method) . "\r\n");$socket->write($this->_getHostLine() . "\r\n");if ($this->_username && $this->_password) {$socket->write('Proxy-Authorization: Basic ' .base64_encode($this->_username . ':' . $this->_password) ."\r\n");}$socket->write("Connection: close\r\n");return $socket;}}/*** HTTP request for a web page. Factory for* HttpResponse object.* @package SimpleTest* @subpackage WebTester*/class SimpleHttpRequest {protected $_route;protected $_encoding;protected $_headers;protected $_cookies;/*** Builds the socket request from the different pieces.* These include proxy information, URL, cookies, headers,* request method and choice of encoding.* @param SimpleRoute $route Request route.* @param SimpleFormEncoding $encoding Content to send with* request.* @access public*/function SimpleHttpRequest($route, $encoding) {$this->_route = $route;$this->_encoding = $encoding;$this->_headers = array();$this->_cookies = array();}/*** Dispatches the content to the route's socket.* @param integer $timeout Connection timeout.* @return SimpleHttpResponse A response which may only have* an error, but hopefully has a* complete web page.* @access public*/function &fetch($timeout) {$socket = $this->_route->createConnection($this->_encoding->getMethod(), $timeout);if (! $socket->isError()) {$this->_dispatchRequest($socket, $this->_encoding);}$response = $this->_createResponse($socket);return $response;}/*** Sends the headers.* @param SimpleSocket $socket Open socket.* @param string $method HTTP request method,* usually GET.* @param SimpleFormEncoding $encoding Content to send with request.* @access private*/function _dispatchRequest($socket, $encoding) {foreach ($this->_headers as $header_line) {$socket->write($header_line . "\r\n");}if (count($this->_cookies) > 0) {$socket->write("Cookie: " . implode(";", $this->_cookies) . "\r\n");}$encoding->writeHeadersTo($socket);$socket->write("\r\n");$encoding->writeTo($socket);}/*** Adds a header line to the request.* @param string $header_line Text of full header line.* @access public*/function addHeaderLine($header_line) {$this->_headers[] = $header_line;}/*** Reads all the relevant cookies from the* cookie jar.* @param SimpleCookieJar $jar Jar to read* @param SimpleUrl $url Url to use for scope.* @access public*/function readCookiesFromJar($jar, $url) {$this->_cookies = $jar->selectAsPairs($url);}/*** Wraps the socket in a response parser.* @param SimpleSocket $socket Responding socket.* @return SimpleHttpResponse Parsed response object.* @access protected*/function &_createResponse($socket) {$response = new SimpleHttpResponse($socket,$this->_route->getUrl(),$this->_encoding);return $response;}}/*** Collection of header lines in the response.* @package SimpleTest* @subpackage WebTester*/class SimpleHttpHeaders {protected $_raw_headers;protected $_response_code;protected $_http_version;protected $_mime_type;protected $_location;protected $_cookies;protected $_authentication;protected $_realm;/*** Parses the incoming header block.* @param string $headers Header block.* @access public*/function SimpleHttpHeaders($headers) {$this->_raw_headers = $headers;$this->_response_code = false;$this->_http_version = false;$this->_mime_type = '';$this->_location = false;$this->_cookies = array();$this->_authentication = false;$this->_realm = false;foreach (split("\r\n", $headers) as $header_line) {$this->_parseHeaderLine($header_line);}}/*** Accessor for parsed HTTP protocol version.* @return integer HTTP error code.* @access public*/function getHttpVersion() {return $this->_http_version;}/*** Accessor for raw header block.* @return string All headers as raw string.* @access public*/function getRaw() {return $this->_raw_headers;}/*** Accessor for parsed HTTP error code.* @return integer HTTP error code.* @access public*/function getResponseCode() {return (integer)$this->_response_code;}/*** Returns the redirected URL or false if* no redirection.* @return string URL or false for none.* @access public*/function getLocation() {return $this->_location;}/*** Test to see if the response is a valid redirect.* @return boolean True if valid redirect.* @access public*/function isRedirect() {return in_array($this->_response_code, array(301, 302, 303, 307)) &&(boolean)$this->getLocation();}/*** Test to see if the response is an authentication* challenge.* @return boolean True if challenge.* @access public*/function isChallenge() {return ($this->_response_code == 401) &&(boolean)$this->_authentication &&(boolean)$this->_realm;}/*** Accessor for MIME type header information.* @return string MIME type.* @access public*/function getMimeType() {return $this->_mime_type;}/*** Accessor for authentication type.* @return string Type.* @access public*/function getAuthentication() {return $this->_authentication;}/*** Accessor for security realm.* @return string Realm.* @access public*/function getRealm() {return $this->_realm;}/*** Writes new cookies to the cookie jar.* @param SimpleCookieJar $jar Jar to write to.* @param SimpleUrl $url Host and path to write under.* @access public*/function writeCookiesToJar($jar, $url) {foreach ($this->_cookies as $cookie) {$jar->setCookie($cookie->getName(),$cookie->getValue(),$url->getHost(),$cookie->getPath(),$cookie->getExpiry());}}/*** Called on each header line to accumulate the held* data within the class.* @param string $header_line One line of header.* @access protected*/function _parseHeaderLine($header_line) {if (preg_match('/HTTP\/(\d+\.\d+)\s+(\d+)/i', $header_line, $matches)) {$this->_http_version = $matches[1];$this->_response_code = $matches[2];}if (preg_match('/Content-type:\s*(.*)/i', $header_line, $matches)) {$this->_mime_type = trim($matches[1]);}if (preg_match('/Location:\s*(.*)/i', $header_line, $matches)) {$this->_location = trim($matches[1]);}if (preg_match('/Set-cookie:(.*)/i', $header_line, $matches)) {$this->_cookies[] = $this->_parseCookie($matches[1]);}if (preg_match('/WWW-Authenticate:\s+(\S+)\s+realm=\"(.*?)\"/i', $header_line, $matches)) {$this->_authentication = $matches[1];$this->_realm = trim($matches[2]);}}/*** Parse the Set-cookie content.* @param string $cookie_line Text after "Set-cookie:"* @return SimpleCookie New cookie object.* @access private*/function _parseCookie($cookie_line) {$parts = split(";", $cookie_line);$cookie = array();preg_match('/\s*(.*?)\s*=(.*)/', array_shift($parts), $cookie);foreach ($parts as $part) {if (preg_match('/\s*(.*?)\s*=(.*)/', $part, $matches)) {$cookie[$matches[1]] = trim($matches[2]);}}return new SimpleCookie($cookie[1],trim($cookie[2]),isset($cookie["path"]) ? $cookie["path"] : "",isset($cookie["expires"]) ? $cookie["expires"] : false);}}/*** Basic HTTP response.* @package SimpleTest* @subpackage WebTester*/class SimpleHttpResponse extends SimpleStickyError {protected $_url;protected $_encoding;protected $_sent;protected $_content;protected $_headers;/*** Constructor. Reads and parses the incoming* content and headers.* @param SimpleSocket $socket Network connection to fetch* response text from.* @param SimpleUrl $url Resource name.* @param mixed $encoding Record of content sent.* @access public*/function SimpleHttpResponse($socket, $url, $encoding) {$this->SimpleStickyError();$this->_url = $url;$this->_encoding = $encoding;$this->_sent = $socket->getSent();$this->_content = false;$raw = $this->_readAll($socket);if ($socket->isError()) {$this->_setError('Error reading socket [' . $socket->getError() . ']');return;}$this->_parse($raw);}/*** Splits up the headers and the rest of the content.* @param string $raw Content to parse.* @access private*/function _parse($raw) {if (! $raw) {$this->_setError('Nothing fetched');$this->_headers = new SimpleHttpHeaders('');} elseif (! strstr($raw, "\r\n\r\n")) {$this->_setError('Could not split headers from content');$this->_headers = new SimpleHttpHeaders($raw);} else {list($headers, $this->_content) = split("\r\n\r\n", $raw, 2);$this->_headers = new SimpleHttpHeaders($headers);}}/*** Original request method.* @return string GET, POST or HEAD.* @access public*/function getMethod() {return $this->_encoding->getMethod();}/*** Resource name.* @return SimpleUrl Current url.* @access public*/function getUrl() {return $this->_url;}/*** Original request data.* @return mixed Sent content.* @access public*/function getRequestData() {return $this->_encoding;}/*** Raw request that was sent down the wire.* @return string Bytes actually sent.* @access public*/function getSent() {return $this->_sent;}/*** Accessor for the content after the last* header line.* @return string All content.* @access public*/function getContent() {return $this->_content;}/*** Accessor for header block. The response is the* combination of this and the content.* @return SimpleHeaders Wrapped header block.* @access public*/function getHeaders() {return $this->_headers;}/*** Accessor for any new cookies.* @return array List of new cookies.* @access public*/function getNewCookies() {return $this->_headers->getNewCookies();}/*** Reads the whole of the socket output into a* single string.* @param SimpleSocket $socket Unread socket.* @return string Raw output if successful* else false.* @access private*/function _readAll($socket) {$all = '';while (! $this->_isLastPacket($next = $socket->read())) {$all .= $next;}return $all;}/*** Test to see if the packet from the socket is the* last one.* @param string $packet Chunk to interpret.* @return boolean True if empty or EOF.* @access private*/function _isLastPacket($packet) {if (is_string($packet)) {return $packet === '';}return ! $packet;}}?>