3 * Base include file for SimpleTest
5 * @subpackage WebTester
6 * @version $Id: user_agent.php,v 1.38 2004/06/30 20:40:07 lastcraft Exp $
10 * include other SimpleTest class files
12 require_once(dirname(__FILE__
) . '/http.php');
13 require_once(dirname(__FILE__
) . '/authentication.php');
16 define('DEFAULT_MAX_REDIRECTS', 3);
17 define('DEFAULT_CONNECTION_TIMEOUT', 15);
20 * Repository for cookies. This stuff is a
21 * tiny bit browser dependent.
23 * @subpackage WebTester
25 class SimpleCookieJar
{
29 * Constructor. Jar starts empty.
32 function SimpleCookieJar() {
33 $this->_cookies
= array();
37 * Removes expired and temporary cookies as if
38 * the browser was closed and re-opened.
39 * @param string/integer $now Time to test expiry against.
42 function restartSession($date = false
) {
43 $surviving_cookies = array();
44 for ($i = 0; $i < count($this->_cookies
); $i++
) {
45 if (! $this->_cookies
[$i]->getValue()) {
48 if (! $this->_cookies
[$i]->getExpiry()) {
51 if ($date && $this->_cookies
[$i]->isExpired($date)) {
54 $surviving_cookies[] = $this->_cookies
[$i];
56 $this->_cookies
= $surviving_cookies;
60 * Ages all cookies in the cookie jar.
61 * @param integer $interval The old session is moved
62 * into the past by this number
63 * of seconds. Cookies now over
64 * age will be removed.
67 function agePrematurely($interval) {
68 for ($i = 0; $i < count($this->_cookies
); $i++
) {
69 $this->_cookies
[$i]->agePrematurely($interval);
74 * Adds a cookie to the jar. This will overwrite
75 * cookies with matching host, paths and keys.
76 * @param SimpleCookie $cookie New cookie.
79 function setCookie($cookie) {
80 for ($i = 0; $i < count($this->_cookies
); $i++
) {
81 $is_match = $this->_isMatch(
83 $this->_cookies
[$i]->getHost(),
84 $this->_cookies
[$i]->getPath(),
85 $this->_cookies
[$i]->getName());
87 $this->_cookies
[$i] = $cookie;
91 $this->_cookies
[] = $cookie;
95 * Fetches a hash of all valid cookies filtered
96 * by host, path and keyed by name
97 * Any cookies with missing categories will not
98 * be filtered out by that category. Expired
99 * cookies must be cleared by restarting the session.
100 * @param string $host Host name requirement.
101 * @param string $path Path encompassing cookies.
102 * @return hash Valid cookie objects keyed
103 * on the cookie name.
106 function getValidCookies($host = false
, $path = "/") {
107 $valid_cookies = array();
108 foreach ($this->_cookies
as $cookie) {
109 if ($this->_isMatch($cookie, $host, $path, $cookie->getName())) {
110 $valid_cookies[] = $cookie;
113 return $valid_cookies;
117 * Tests cookie for matching against search
119 * @param SimpleTest $cookie Cookie to test.
120 * @param string $host Host must match.
121 * @param string $path Cookie path must be shorter than
123 * @param string $name Name must match.
124 * @return boolean True if matched.
127 function _isMatch($cookie, $host, $path, $name) {
128 if ($cookie->getName() != $name) {
131 if ($host && $cookie->getHost() && !$cookie->isValidHost($host)) {
134 if (! $cookie->isValidPath($path)) {
141 * Adds the current cookies to a request.
142 * @param SimpleHttpRequest $request Request to modify.
143 * @param SimpleUrl $url Cookie selector.
146 function addHeaders(&$request, $url) {
147 $cookies = $this->getValidCookies($url->getHost(), $url->getPath());
148 foreach ($cookies as $cookie) {
149 $request->setCookie($cookie);
155 * Fetches web pages whilst keeping track of
156 * cookies and authentication.
157 * @package SimpleTest
158 * @subpackage WebTester
160 class SimpleUserAgent
{
165 var $_proxy_username;
166 var $_proxy_password;
167 var $_connection_timeout;
168 var $_additional_headers;
171 * Starts with no cookies, realms or proxies.
174 function SimpleUserAgent() {
175 $this->_cookie_jar
= &new SimpleCookieJar();
176 $this->_authenticator
= &new SimpleAuthenticator();
177 $this->setMaximumRedirects(DEFAULT_MAX_REDIRECTS
);
178 $this->_proxy
= false
;
179 $this->_proxy_username
= false
;
180 $this->_proxy_password
= false
;
181 $this->setConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT
);
182 $this->_additional_headers
= array();
186 * Removes expired and temporary cookies as if
187 * the browser was closed and re-opened.
188 * @param string/integer $date Time when session restarted.
189 * If omitted then all persistent
193 function restartSession($date = false
) {
194 $this->_cookie_jar
->restartSession($date);
198 * Adds a header to every fetch.
199 * @param string $header Header line to add to every
200 * request until cleared.
203 function addHeader($header) {
204 $this->_additional_headers
[] = $header;
208 * Ages the cookies by the specified time.
209 * @param integer $interval Amount in seconds.
212 function ageCookies($interval) {
213 $this->_cookie_jar
->agePrematurely($interval);
217 * Sets an additional cookie. If a cookie has
218 * the same name and path it is replaced.
219 * @param string $name Cookie key.
220 * @param string $value Value of cookie.
221 * @param string $host Host upon which the cookie is valid.
222 * @param string $path Cookie path if not host wide.
223 * @param string $expiry Expiry date.
226 function setCookie($name, $value, $host = false
, $path = '/', $expiry = false
) {
227 $cookie = new SimpleCookie($name, $value, $path, $expiry);
229 $cookie->setHost($host);
231 $this->_cookie_jar
->setCookie($cookie);
235 * Reads the most specific cookie value from the
237 * @param string $host Host to search.
238 * @param string $path Applicable path.
239 * @param string $name Name of cookie to read.
240 * @return string False if not present, else the
244 function getCookieValue($host, $path, $name) {
246 foreach ($this->_cookie_jar
->getValidCookies($host, $path) as $cookie) {
247 if ($name == $cookie->getName()) {
248 if (strlen($cookie->getPath()) > strlen($longest_path)) {
249 $value = $cookie->getValue();
250 $longest_path = $cookie->getPath();
254 return (isset($value) ?
$value : false
);
258 * Reads the current cookies within the base URL.
259 * @param string $name Key of cookie to find.
260 * @param SimpleUrl $base Base URL to search from.
261 * @return string Null if there is no base URL, false
262 * if the cookie is not set.
265 function getBaseCookieValue($name, $base) {
269 return $this->getCookieValue($base->getHost(), $base->getPath(), $name);
273 * Sets the socket timeout for opening a connection.
274 * @param integer $timeout Maximum time in seconds.
277 function setConnectionTimeout($timeout) {
278 $this->_connection_timeout
= $timeout;
282 * Sets the maximum number of redirects before
283 * a page will be loaded anyway.
284 * @param integer $max Most hops allowed.
287 function setMaximumRedirects($max) {
288 $this->_max_redirects
= $max;
292 * Sets proxy to use on all requests for when
293 * testing from behind a firewall. Set URL
294 * to false to disable.
295 * @param string $proxy Proxy URL.
296 * @param string $username Proxy username for authentication.
297 * @param string $password Proxy password for authentication.
300 function useProxy($proxy, $username, $password) {
302 $this->_proxy
= false
;
305 if (strncmp($proxy, 'http://', 7) != 0) {
306 $proxy = 'http://'. $proxy;
308 $this->_proxy
= &new SimpleUrl($proxy);
309 $this->_proxy_username
= $username;
310 $this->_proxy_password
= $password;
314 * Test to see if the redirect limit is passed.
315 * @param integer $redirects Count so far.
316 * @return boolean True if over.
319 function _isTooManyRedirects($redirects) {
320 return ($redirects > $this->_max_redirects
);
324 * Sets the identity for the current realm.
325 * @param string $host Host to which realm applies.
326 * @param string $realm Full name of realm.
327 * @param string $username Username for realm.
328 * @param string $password Password for realm.
331 function setIdentity($host, $realm, $username, $password) {
332 $this->_authenticator
->setIdentityForRealm($host, $realm, $username, $password);
336 * Fetches a URL as a response object. Will keep trying if redirected.
337 * It will also collect authentication realm information.
338 * @param string $method GET, POST, etc.
339 * @param string/SimpleUrl $url Target to fetch.
340 * @param hash $parameters Additional parameters for request.
341 * @return SimpleHttpResponse Hopefully the target page.
344 function &fetchResponse($method, $url, $parameters = false
) {
345 if ($method != 'POST') {
346 $url->addRequestParameters($parameters);
349 $response = &$this->_fetchWhileRedirected($method, $url, $parameters);
350 if ($headers = $response->getHeaders()) {
351 if ($headers->isChallenge()) {
352 $this->_authenticator
->addRealm(
354 $headers->getAuthentication(),
355 $headers->getRealm());
362 * Fetches the page until no longer redirected or
363 * until the redirect limit runs out.
364 * @param string $method GET, POST, etc.
365 * @param SimpleUrl $url Target to fetch.
366 * @param hash $parameters Additional parameters for request.
367 * @return SimpleHttpResponse Hopefully the target page.
370 function &_fetchWhileRedirected($method, $url, $parameters) {
373 $response = &$this->_fetch($method, $url, $parameters);
374 if ($response->isError()) {
377 $headers = $response->getHeaders();
378 $location = new SimpleUrl($headers->getLocation());
379 $url = $location->makeAbsolute($url);
380 $this->_addCookiesToJar($url, $headers->getNewCookies());
381 if (! $headers->isRedirect()) {
386 } while (! $this->_isTooManyRedirects(++
$redirects));
391 * Actually make the web request.
392 * @param string $method GET, POST, etc.
393 * @param SimpleUrl $url Target to fetch.
394 * @param hash $parameters Additional parameters for request.
395 * @return SimpleHttpResponse Headers and hopefully content.
398 function &_fetch($method, $url, $parameters) {
400 $parameters = array();
402 $request = &$this->_createRequest($method, $url, $parameters);
403 return $request->fetch($this->_connection_timeout
);
407 * Creates a full page request.
408 * @param string $method Fetching method.
409 * @param SimpleUrl $url Target to fetch as url object.
410 * @param hash $parameters POST/GET parameters.
411 * @return SimpleHttpRequest New request.
414 function &_createRequest($method, $url, $parameters) {
415 $request = &$this->_createHttpRequest($method, $url, $parameters);
416 $this->_addAdditionalHeaders($request);
417 $this->_cookie_jar
->addHeaders($request, $url);
418 $this->_authenticator
->addHeaders($request, $url);
423 * Builds the appropriate HTTP request object.
424 * @param string $method Fetching method.
425 * @param SimpleUrl $url Target to fetch as url object.
426 * @param hash $parameters POST/GET parameters.
427 * @return SimpleHttpRequest New request object.
430 function &_createHttpRequest($method, $url, $parameters) {
431 if ($method == 'POST') {
432 $request = &new SimpleHttpPostRequest(
433 $this->_createRoute($url),
438 $url->addRequestParameters($parameters);
440 return new SimpleHttpRequest($this->_createRoute($url), $method);
444 * Sets up either a direct route or via a proxy.
445 * @param SimpleUrl $url Target to fetch as url object.
446 * @return SimpleRoute Route to take to fetch URL.
449 function &_createRoute($url) {
451 return new SimpleProxyRoute(
454 $this->_proxy_username
,
455 $this->_proxy_password
);
457 return new SimpleRoute($url);
461 * Adds additional manual headers.
462 * @param SimpleHttpRequest $request Outgoing request.
465 function _addAdditionalHeaders(&$request) {
466 foreach ($this->_additional_headers
as $header) {
467 $request->addHeaderLine($header);
472 * Extracts new cookies into the cookie jar.
473 * @param SimpleUrl $url Target to fetch as url object.
474 * @param array $cookies New cookies.
477 function _addCookiesToJar($url, $cookies) {
478 foreach ($cookies as $cookie) {
479 if ($url->getHost()) {
480 $cookie->setHost($url->getHost());
482 $this->_cookie_jar
->setCookie($cookie);