You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
5.1 KiB

  1. <?php
  2. namespace p3k\HTTP;
  3. // This implements the same interface as the main class p3k\HTTP
  4. // despite the fact that it looks like just a transport plugin.
  5. // This is so that the main p3k\HTTP object can be replaced with this
  6. // object in the test suite.
  7. class Test implements Transport {
  8. private $_testDataPath;
  9. private $_redirects_remaining;
  10. public function __construct($testDataPath) {
  11. $this->_testDataPath = $testDataPath;
  12. }
  13. protected $_timeout = 4;
  14. protected $_max_redirects = 8;
  15. public function set_max_redirects($max) {
  16. $this->_max_redirects = $max;
  17. }
  18. public function set_timeout($timeout) {
  19. $this->_timeout = $timeout;
  20. }
  21. public function set_transport(Transport $transport) {
  22. }
  23. public function get($url, $headers=[]) {
  24. $this->_redirects_remaining = $this->_max_redirects;
  25. $parts = parse_url($url);
  26. unset($parts['fragment']);
  27. $url = self::_build_url($parts);
  28. return $this->_read_file($url);
  29. }
  30. public function post($url, $body, $headers=[]) {
  31. return $this->_read_file($url);
  32. }
  33. public function head($url, $headers=[]) {
  34. $response = $this->_read_file($url);
  35. return [
  36. 'code' => (int)$response['code'],
  37. 'header' => $response['header'],
  38. 'headers' => $response['headers'],
  39. 'rels' => $response['rels'],
  40. 'error' => '',
  41. 'error_description' => '',
  42. 'url' => $response['url']
  43. ];
  44. }
  45. private function _read_file($url) {
  46. $parts = parse_url($url);
  47. if($parts['path']) {
  48. $parts['path'] = '/'.str_replace('/','_',substr($parts['path'],1));
  49. if(!isset($parts['path']) || $parts['path'] == '/')
  50. $parts['path'] = '/_';
  51. $filepathurl = self::_build_url($parts);
  52. }
  53. $filename = $this->_testDataPath.preg_replace('/https?:\/\//', '', $filepathurl);
  54. if(!file_exists($filename)) {
  55. $filename = $this->_testDataPath.'404.response.txt';
  56. }
  57. $response = file_get_contents($filename);
  58. $split = explode("\r\n\r\n", $response);
  59. if(count($split) < 2) {
  60. throw new \Exception("Invalid file contents in test data, check that newlines are CRLF: $url");
  61. }
  62. $headers = array_shift($split);
  63. $body = implode("\r\n", $split);
  64. if(preg_match('/HTTP\/1\.1 (\d+)/', $headers, $match)) {
  65. $code = $match[1];
  66. }
  67. $headers = preg_replace('/HTTP\/1\.1 \d+ .+/', '', $headers);
  68. $parsedHeaders = self::_parse_headers($headers);
  69. if(array_key_exists('Location', $parsedHeaders)) {
  70. $effectiveUrl = \mf2\resolveUrl($url, $parsedHeaders['Location']);
  71. if($this->_redirects_remaining > 0) {
  72. $this->_redirects_remaining--;
  73. return $this->_read_file($effectiveUrl);
  74. } else {
  75. return [
  76. 'code' => (int)$code,
  77. 'header' => $headers,
  78. 'headers' => $parsedHeaders,
  79. 'rels' => \IndieWeb\http_rels($headers),
  80. 'body' => $body,
  81. 'error' => 'too_many_redirects',
  82. 'error_description' => '',
  83. 'url' => $effectiveUrl
  84. ];
  85. }
  86. } else {
  87. $effectiveUrl = $url;
  88. }
  89. return [
  90. 'code' => (int)$code,
  91. 'header' => $headers,
  92. 'headers' => $parsedHeaders,
  93. 'rels' => \IndieWeb\http_rels($headers),
  94. 'body' => $body,
  95. 'error' => (isset($parsedHeaders['X-Test-Error']) ? $parsedHeaders['X-Test-Error'] : ''),
  96. 'error_description' => '',
  97. 'url' => $effectiveUrl
  98. ];
  99. }
  100. private static function _parse_headers($headers) {
  101. $retVal = [];
  102. $fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $headers));
  103. foreach($fields as $field) {
  104. if(preg_match('/([^:]+): (.+)/m', $field, $match)) {
  105. $match[1] = preg_replace_callback('/(?<=^|[\x09\x20\x2D])./', function($m) {
  106. return strtoupper($m[0]);
  107. }, strtolower(trim($match[1])));
  108. // If there's already a value set for the header name being returned, turn it into an array and add the new value
  109. $match[1] = preg_replace_callback('/(?<=^|[\x09\x20\x2D])./', function($m) {
  110. return strtoupper($m[0]);
  111. }, strtolower(trim($match[1])));
  112. if(isset($retVal[$match[1]])) {
  113. if(!is_array($retVal[$match[1]]))
  114. $retVal[$match[1]] = [$retVal[$match[1]]];
  115. $retVal[$match[1]][] = $match[2];
  116. } else {
  117. $retVal[$match[1]] = trim($match[2]);
  118. }
  119. }
  120. }
  121. return $retVal;
  122. }
  123. private static function _build_url($parsed_url) {
  124. $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
  125. $host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
  126. $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
  127. $user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
  128. $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
  129. $pass = ($user || $pass) ? "$pass@" : '';
  130. $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
  131. $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
  132. $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
  133. return "$scheme$user$pass$host$port$path$query$fragment";
  134. }
  135. }