PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/Resources/Public/Contrib/aloha/plugins/extra/proxy/adapter/proxy.php

https://bitbucket.org/pixelant/aloha
PHP | 248 lines | 142 code | 30 blank | 76 comment | 29 complexity | acf4dac02c681b87c7563cebfc9b5506 MD5 | raw file
  1. <?php
  2. /**
  3. * Gentics Aloha Editor AJAX Gateway
  4. * Copyright (c) 2010 Gentics Software GmbH
  5. * Licensed unter the terms of http://www.aloha-editor.com/license.html
  6. * aloha-sales@gentics.com
  7. * Author Haymo Meran h.meran@gentics.com
  8. * Author Johannes Schüth j.schuet@gentics.com
  9. * Author Tobias Steiner t.steiner@gentics.com
  10. *
  11. * Testing from the command line:
  12. * function getallheaders(){return array('X-Gentics' => 'X');};
  13. * https url example: https://google.com/adsense
  14. *
  15. */
  16. // for debugging
  17. //$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0';
  18. //$_SERVER['REQUEST_METHOD'] = 'HEAD';
  19. //error_reporting(E_ALL);
  20. $request = array(
  21. 'method' => $_SERVER['REQUEST_METHOD'],
  22. 'protocol' => $_SERVER['SERVER_PROTOCOL'],
  23. 'headers' => getallheaders(),
  24. //TODO: multipart/form-data is not handled by php://input. there
  25. //doesn't seem to be a generic way to get at the raw post data for
  26. //that content-type.
  27. 'payload' => file_get_contents('php://input'),
  28. );
  29. // read url parameter
  30. if (array_key_exists('url', $_GET)) {
  31. $request['url'] = urldecode($_GET['url']);
  32. } else {
  33. header("HTTP/1.0 400 Bad Request");
  34. echo "Aloha Editor AJAX Gateway failed because parameter url is missing.";
  35. exit();
  36. }
  37. // check if link exists
  38. $response = http_request($request);
  39. // Note HEAD does not always work even if specified...
  40. // We use HEAD for Linkchecking so we do a 2nd request.
  41. if (!array_key_exists('method', $response)) {
  42. $response['method'] = false;
  43. }
  44. if (strtoupper($response['method']) == 'HEAD' && (int) $response['status'] >= 400 ) {
  45. $request['method'] = 'GET';
  46. $response = http_request($request);
  47. //since we handle a HEAD, we don't need to proxy any contents
  48. fclose($response['socket']);
  49. $response['socket'] = null;
  50. }
  51. // forward each returned header...
  52. foreach ($response['headers'] as $key => $value) {
  53. if (strtolower($key) == 'content-length') {
  54. //there is no need to specify a content length since we don't do keep
  55. //alive, and this can cause problems for integration (e.g. gzip output,
  56. //which would change the content length)
  57. //Note: overriding with header('Content-length:') will set
  58. //the content-length to zero for some reason
  59. continue;
  60. }
  61. header("$key: $value");
  62. }
  63. header('Connection: close');
  64. // output the contents if any
  65. if (null !== $response['socket']) {
  66. fpassthru($response['socket']);
  67. fclose($response['socket']);
  68. }
  69. exit;
  70. /**
  71. * Query an HTTP(S) URL with the given request parameters and return the
  72. * response headers and status code. The socket is returned as well and
  73. * will point to the begining of the response payload (after all headers
  74. * have been read), and must be closed with fclose().
  75. * @param $url the request URL
  76. * @param $request the request method may optionally be overridden.
  77. * @param $timeout connection and read timeout in seconds
  78. */
  79. function http_request($request, $timeout = 5) {
  80. $url = $request['url'];
  81. // Extract the hostname from url
  82. $parts = parse_url($url);
  83. if (array_key_exists('host', $parts)) {
  84. $remote = $parts['host'];
  85. } else {
  86. return myErrorHandler("url ($url) has no host. Is it relative?");
  87. }
  88. if (array_key_exists('port', $parts)) {
  89. $port = $parts['port'];
  90. } else {
  91. $port = 0;
  92. }
  93. // Beware that RFC2616 (HTTP/1.1) defines header fields as case-insensitive entities.
  94. $request_headers = "";
  95. foreach ($request['headers'] as $name => $value) {
  96. switch (strtolower($name)) {
  97. //omit some headers
  98. case "keep-alive":
  99. case "connection":
  100. case "cookie":
  101. //TODO: we don't handle any compression encodings. compression
  102. //can cause a problem if client communication is already being
  103. //compressed by the server/app that integrates this script
  104. //(which would double compress the content, once from the remote
  105. //server to us, and once from us to the client, but the client
  106. //would de-compress only once).
  107. case "accept-encoding":
  108. break;
  109. // correct the host parameter
  110. case "host":
  111. $host_info = $remote;
  112. if ($port) {
  113. $host_info .= ':' . $port;
  114. }
  115. $request_headers .= "$name: $host_info\r\n";
  116. break;
  117. // forward all other headers
  118. default:
  119. $request_headers .= "$name: $value\r\n";
  120. break;
  121. }
  122. }
  123. //set fsockopen transport scheme, and the default port
  124. switch (strtolower($parts['scheme'])) {
  125. case 'https':
  126. $scheme = 'ssl://';
  127. if ( ! $port ) $port = 443;
  128. break;
  129. case 'http':
  130. $scheme = '';
  131. if ( ! $port ) $port = 80;
  132. break;
  133. default:
  134. //some other transports are available but not really supported
  135. //by this script: http://php.net/manual/en/transports.inet.php
  136. $scheme = $parts['scheme'] . '://';
  137. if ( ! $port ) {
  138. return myErrorHandler("Unknown scheme ($scheme) and no port.");
  139. }
  140. break;
  141. }
  142. //we make the request with socket operations since we don't want to
  143. //depend on the curl extension, and the higher level wrappers don't
  144. //give us usable error information.
  145. $sock = @fsockopen("$scheme$remote", $port, $errno, $errstr, $timeout);
  146. if ( ! $sock ) {
  147. return myErrorHandler("Unable to open URL ($url): $errstr");
  148. }
  149. //the timeout in fsockopen is only for the connection, the following
  150. //is for reading the content
  151. stream_set_timeout($sock, $timeout);
  152. //an absolute url should only be specified for proxy requests
  153. if (array_key_exists('path', $parts)) {
  154. $path_info = $parts['path'];
  155. } else {
  156. $path_info = '/';
  157. }
  158. if (array_key_exists('query', $parts)) $path_info .= '?' . $parts['query'];
  159. if (array_key_exists('fragment', $parts)) $path_info .= '#' . $parts['fragment'];
  160. $out = $request["method"]." ".$path_info." ".$request["protocol"]."\r\n"
  161. . $request_headers
  162. . "Connection: close\r\n\r\n";
  163. fwrite($sock, $out);
  164. fwrite($sock, $request['payload']);
  165. $header_str = stream_get_line($sock, 1024*16, "\r\n\r\n");
  166. $headers = http_parse_headers($header_str);
  167. $status_line = array_shift($headers);
  168. // get http status
  169. preg_match('|HTTP/\d+\.\d+\s+(\d+)\s+.*|i',$status_line,$match);
  170. $status = $match[1];
  171. return array('headers' => $headers, 'socket' => $sock, 'status' => $status);
  172. }
  173. /**
  174. * Parses a string containing multiple HTTP header lines into an array
  175. * of key => values.
  176. * Inspired by HTTP::Daemon (CPAN).
  177. */
  178. function http_parse_headers($header_str) {
  179. $headers = array();
  180. //ignore leading blank lines
  181. $header_str = preg_replace("/^(?:\x0D?\x0A)+/", '', $header_str);
  182. while (preg_match("/^([^\x0A]*?)\x0D?(?:\x0A|\$)/", $header_str, $matches)) {
  183. $header_str = substr($header_str, strlen($matches[0]));
  184. $status_line = $matches[1];
  185. if (empty($headers)) {
  186. // the status line
  187. $headers[] = $status_line;
  188. }
  189. elseif (preg_match('/^([^:\s]+)\s*:\s*(.*)/', $status_line, $matches)) {
  190. if (isset($key)) {
  191. //previous header is finished (was potentially multi-line)
  192. $headers[$key] = $val;
  193. }
  194. list(,$key,$val) = $matches;
  195. }
  196. elseif (preg_match('/^\s+(.*)/', $status_line, $matches)) {
  197. //continue a multi-line header
  198. $val .= " ".$matches[1];
  199. }
  200. else {
  201. //empty (possibly malformed) header signals the end of all headers
  202. break;
  203. }
  204. }
  205. if (isset($key)) {
  206. $headers[$key] = $val;
  207. }
  208. return $headers;
  209. }
  210. function myErrorHandler($msg)
  211. {
  212. // 500 could be misleading...
  213. // Should we return a special Error when a proxy error occurs?
  214. header("HTTP/1.0 500 Internal Error");
  215. echo "Gentics Aloha Editor AJAX Gateway Error: $msg";
  216. exit();
  217. }
  218. //EOF