user_agent.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <?php
  2. /**
  3. * Base include file for SimpleTest
  4. * @package SimpleTest
  5. * @subpackage WebTester
  6. * @version $Id$
  7. */
  8. /**#@+
  9. * include other SimpleTest class files
  10. */
  11. require_once(dirname(__FILE__) . '/cookies.php');
  12. require_once(dirname(__FILE__) . '/http.php');
  13. require_once(dirname(__FILE__) . '/encoding.php');
  14. require_once(dirname(__FILE__) . '/authentication.php');
  15. /**#@-*/
  16. if (! defined('DEFAULT_MAX_REDIRECTS')) {
  17. define('DEFAULT_MAX_REDIRECTS', 3);
  18. }
  19. if (! defined('DEFAULT_CONNECTION_TIMEOUT')) {
  20. define('DEFAULT_CONNECTION_TIMEOUT', 15);
  21. }
  22. /**
  23. * Fetches web pages whilst keeping track of
  24. * cookies and authentication.
  25. * @package SimpleTest
  26. * @subpackage WebTester
  27. */
  28. class SimpleUserAgent {
  29. private $cookie_jar;
  30. private $cookies_enabled = true;
  31. private $authenticator;
  32. private $max_redirects = DEFAULT_MAX_REDIRECTS;
  33. private $proxy = false;
  34. private $proxy_username = false;
  35. private $proxy_password = false;
  36. private $connection_timeout = DEFAULT_CONNECTION_TIMEOUT;
  37. private $additional_headers = array();
  38. /**
  39. * Starts with no cookies, realms or proxies.
  40. * @access public
  41. */
  42. function __construct() {
  43. $this->cookie_jar = new SimpleCookieJar();
  44. $this->authenticator = new SimpleAuthenticator();
  45. }
  46. /**
  47. * Removes expired and temporary cookies as if
  48. * the browser was closed and re-opened. Authorisation
  49. * has to be obtained again as well.
  50. * @param string/integer $date Time when session restarted.
  51. * If omitted then all persistent
  52. * cookies are kept.
  53. * @access public
  54. */
  55. function restart($date = false) {
  56. $this->cookie_jar->restartSession($date);
  57. $this->authenticator->restartSession();
  58. }
  59. /**
  60. * Adds a header to every fetch.
  61. * @param string $header Header line to add to every
  62. * request until cleared.
  63. * @access public
  64. */
  65. function addHeader($header) {
  66. $this->additional_headers[] = $header;
  67. }
  68. /**
  69. * Ages the cookies by the specified time.
  70. * @param integer $interval Amount in seconds.
  71. * @access public
  72. */
  73. function ageCookies($interval) {
  74. $this->cookie_jar->agePrematurely($interval);
  75. }
  76. /**
  77. * Sets an additional cookie. If a cookie has
  78. * the same name and path it is replaced.
  79. * @param string $name Cookie key.
  80. * @param string $value Value of cookie.
  81. * @param string $host Host upon which the cookie is valid.
  82. * @param string $path Cookie path if not host wide.
  83. * @param string $expiry Expiry date.
  84. * @access public
  85. */
  86. function setCookie($name, $value, $host = false, $path = '/', $expiry = false) {
  87. $this->cookie_jar->setCookie($name, $value, $host, $path, $expiry);
  88. }
  89. /**
  90. * Reads the most specific cookie value from the
  91. * browser cookies.
  92. * @param string $host Host to search.
  93. * @param string $path Applicable path.
  94. * @param string $name Name of cookie to read.
  95. * @return string False if not present, else the
  96. * value as a string.
  97. * @access public
  98. */
  99. function getCookieValue($host, $path, $name) {
  100. return $this->cookie_jar->getCookieValue($host, $path, $name);
  101. }
  102. /**
  103. * Reads the current cookies within the base URL.
  104. * @param string $name Key of cookie to find.
  105. * @param SimpleUrl $base Base URL to search from.
  106. * @return string/boolean Null if there is no base URL, false
  107. * if the cookie is not set.
  108. * @access public
  109. */
  110. function getBaseCookieValue($name, $base) {
  111. if (! $base) {
  112. return null;
  113. }
  114. return $this->getCookieValue($base->getHost(), $base->getPath(), $name);
  115. }
  116. /**
  117. * Switches off cookie sending and recieving.
  118. * @access public
  119. */
  120. function ignoreCookies() {
  121. $this->cookies_enabled = false;
  122. }
  123. /**
  124. * Switches back on the cookie sending and recieving.
  125. * @access public
  126. */
  127. function useCookies() {
  128. $this->cookies_enabled = true;
  129. }
  130. /**
  131. * Sets the socket timeout for opening a connection.
  132. * @param integer $timeout Maximum time in seconds.
  133. * @access public
  134. */
  135. function setConnectionTimeout($timeout) {
  136. $this->connection_timeout = $timeout;
  137. }
  138. /**
  139. * Sets the maximum number of redirects before
  140. * a page will be loaded anyway.
  141. * @param integer $max Most hops allowed.
  142. * @access public
  143. */
  144. function setMaximumRedirects($max) {
  145. $this->max_redirects = $max;
  146. }
  147. /**
  148. * Sets proxy to use on all requests for when
  149. * testing from behind a firewall. Set URL
  150. * to false to disable.
  151. * @param string $proxy Proxy URL.
  152. * @param string $username Proxy username for authentication.
  153. * @param string $password Proxy password for authentication.
  154. * @access public
  155. */
  156. function useProxy($proxy, $username, $password) {
  157. if (! $proxy) {
  158. $this->proxy = false;
  159. return;
  160. }
  161. if ((strncmp($proxy, 'http://', 7) != 0) && (strncmp($proxy, 'https://', 8) != 0)) {
  162. $proxy = 'http://'. $proxy;
  163. }
  164. $this->proxy = new SimpleUrl($proxy);
  165. $this->proxy_username = $username;
  166. $this->proxy_password = $password;
  167. }
  168. /**
  169. * Test to see if the redirect limit is passed.
  170. * @param integer $redirects Count so far.
  171. * @return boolean True if over.
  172. * @access private
  173. */
  174. protected function isTooManyRedirects($redirects) {
  175. return ($redirects > $this->max_redirects);
  176. }
  177. /**
  178. * Sets the identity for the current realm.
  179. * @param string $host Host to which realm applies.
  180. * @param string $realm Full name of realm.
  181. * @param string $username Username for realm.
  182. * @param string $password Password for realm.
  183. * @access public
  184. */
  185. function setIdentity($host, $realm, $username, $password) {
  186. $this->authenticator->setIdentityForRealm($host, $realm, $username, $password);
  187. }
  188. /**
  189. * Fetches a URL as a response object. Will keep trying if redirected.
  190. * It will also collect authentication realm information.
  191. * @param string/SimpleUrl $url Target to fetch.
  192. * @param SimpleEncoding $encoding Additional parameters for request.
  193. * @return SimpleHttpResponse Hopefully the target page.
  194. * @access public
  195. */
  196. function fetchResponse($url, $encoding) {
  197. if ($encoding->getMethod() != 'POST') {
  198. $url->addRequestParameters($encoding);
  199. $encoding->clear();
  200. }
  201. $response = $this->fetchWhileRedirected($url, $encoding);
  202. if ($headers = $response->getHeaders()) {
  203. if ($headers->isChallenge()) {
  204. $this->authenticator->addRealm(
  205. $url,
  206. $headers->getAuthentication(),
  207. $headers->getRealm());
  208. }
  209. }
  210. return $response;
  211. }
  212. /**
  213. * Fetches the page until no longer redirected or
  214. * until the redirect limit runs out.
  215. * @param SimpleUrl $url Target to fetch.
  216. * @param SimpelFormEncoding $encoding Additional parameters for request.
  217. * @return SimpleHttpResponse Hopefully the target page.
  218. * @access private
  219. */
  220. protected function fetchWhileRedirected($url, $encoding) {
  221. $redirects = 0;
  222. do {
  223. $response = $this->fetch($url, $encoding);
  224. if ($response->isError()) {
  225. return $response;
  226. }
  227. $headers = $response->getHeaders();
  228. if ($this->cookies_enabled) {
  229. $headers->writeCookiesToJar($this->cookie_jar, $url);
  230. }
  231. if (! $headers->isRedirect()) {
  232. break;
  233. }
  234. $location = new SimpleUrl($headers->getLocation());
  235. $url = $location->makeAbsolute($url);
  236. $encoding = new SimpleGetEncoding();
  237. } while (! $this->isTooManyRedirects(++$redirects));
  238. return $response;
  239. }
  240. /**
  241. * Actually make the web request.
  242. * @param SimpleUrl $url Target to fetch.
  243. * @param SimpleFormEncoding $encoding Additional parameters for request.
  244. * @return SimpleHttpResponse Headers and hopefully content.
  245. * @access protected
  246. */
  247. protected function fetch($url, $encoding) {
  248. $request = $this->createRequest($url, $encoding);
  249. return $request->fetch($this->connection_timeout);
  250. }
  251. /**
  252. * Creates a full page request.
  253. * @param SimpleUrl $url Target to fetch as url object.
  254. * @param SimpleFormEncoding $encoding POST/GET parameters.
  255. * @return SimpleHttpRequest New request.
  256. * @access private
  257. */
  258. protected function createRequest($url, $encoding) {
  259. $request = $this->createHttpRequest($url, $encoding);
  260. $this->addAdditionalHeaders($request);
  261. if ($this->cookies_enabled) {
  262. $request->readCookiesFromJar($this->cookie_jar, $url);
  263. }
  264. $this->authenticator->addHeaders($request, $url);
  265. return $request;
  266. }
  267. /**
  268. * Builds the appropriate HTTP request object.
  269. * @param SimpleUrl $url Target to fetch as url object.
  270. * @param SimpleFormEncoding $parameters POST/GET parameters.
  271. * @return SimpleHttpRequest New request object.
  272. * @access protected
  273. */
  274. protected function createHttpRequest($url, $encoding) {
  275. return new SimpleHttpRequest($this->createRoute($url), $encoding);
  276. }
  277. /**
  278. * Sets up either a direct route or via a proxy.
  279. * @param SimpleUrl $url Target to fetch as url object.
  280. * @return SimpleRoute Route to take to fetch URL.
  281. * @access protected
  282. */
  283. protected function createRoute($url) {
  284. if ($this->proxy) {
  285. return new SimpleProxyRoute(
  286. $url,
  287. $this->proxy,
  288. $this->proxy_username,
  289. $this->proxy_password);
  290. }
  291. return new SimpleRoute($url);
  292. }
  293. /**
  294. * Adds additional manual headers.
  295. * @param SimpleHttpRequest $request Outgoing request.
  296. * @access private
  297. */
  298. protected function addAdditionalHeaders(&$request) {
  299. foreach ($this->additional_headers as $header) {
  300. $request->addHeaderLine($header);
  301. }
  302. }
  303. }
  304. ?>