PageRenderTime 48ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/HttpServer.class.php

https://gitlab.com/Tiger66639/symbiose
PHP | 319 lines | 236 code | 58 blank | 25 comment | 38 complexity | 9bddfe533987fde02a45fce58d79e960 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause
  1. <?php
  2. namespace lib;
  3. use \Exception;
  4. use Ratchet\MessageComponentInterface;
  5. use Ratchet\ConnectionInterface;
  6. use Ratchet\Http\HttpServerInterface;
  7. use Ratchet\Http\HttpRequestParser;
  8. use Guzzle\Http\Message\RequestInterface;
  9. use Guzzle\Http\Message\Response;
  10. use Guzzle\Http\QueryString;
  11. use Ratchet\Session\Storage\VirtualSessionStorage;
  12. use Ratchet\Session\Serialize\HandlerInterface;
  13. use Symfony\Component\HttpFoundation\Session\Session;
  14. use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
  15. /**
  16. * A builtin HTTP server.
  17. * @since 1.0beta5
  18. * @author emersion
  19. */
  20. class HttpServer implements HttpServerInterface {
  21. protected $_reqParser;
  22. protected $_handler;
  23. protected $sessions = array();
  24. public function __construct(\SessionHandlerInterface $handler, HandlerInterface $serializer = null) {
  25. $this->_reqParser = new HttpRequestParser;
  26. $this->_handler = $handler;
  27. ini_set('session.auto_start', 0);
  28. ini_set('session.cache_limiter', '');
  29. ini_set('session.use_cookies', 0);
  30. if (null === $serializer) {
  31. $serialClass = "Ratchet\\Session\\Serialize\\{$this->toClassCase(ini_get('session.serialize_handler'))}Handler"; // awesome/terrible hack, eh?
  32. if (!class_exists($serialClass)) {
  33. throw new \RuntimeException('Unable to parse session serialize handler');
  34. }
  35. $serializer = new $serialClass;
  36. }
  37. $this->_serializer = $serializer;
  38. }
  39. public function onOpen(ConnectionInterface $from, RequestInterface $request = null) {
  40. if (empty($request)) {
  41. $resp = new Response(400);
  42. $from->send((string)$resp);
  43. $from->close();
  44. return;
  45. }
  46. // Session management
  47. $saveHandler = $this->_handler;
  48. $id = $request->getCookie(ini_get('session.name'));
  49. if (empty($id)) {
  50. $id = sha1(uniqid() . mt_rand());
  51. }
  52. // Crappy workaround for sessions - don't know why they are not saved
  53. // @see https://github.com/ratchetphp/Ratchet/blob/master/src/Ratchet/Session/SessionProvider.php
  54. if (isset($this->sessions[$id])) {
  55. $from->Session = $this->sessions[$id];
  56. } else {
  57. $from->Session = new Session(new VirtualSessionStorage($saveHandler, $id, $this->_serializer));
  58. $this->sessions[$id] = $from->Session;
  59. }
  60. if (ini_get('session.auto_start')) {
  61. $from->Session->start();
  62. }
  63. $this->onRequest($from, $request);
  64. }
  65. public function onMessage(ConnectionInterface $from, $msg) {
  66. try {
  67. if (null === ($request = $this->_reqParser->onMessage($from, $msg))) {
  68. return;
  69. }
  70. } catch (\OverflowException $oe) {
  71. $resp = new Response(413);
  72. $from->send((string)$resp);
  73. $from->close();
  74. return;
  75. }
  76. $this->onRequest($from, $request);
  77. }
  78. protected function onRequest(ConnectionInterface $from, RequestInterface $request) {
  79. $requestPath = $request->getPath();
  80. $body = (string)$request->getBody();
  81. if (!empty($body)) {
  82. $query = QueryString::fromString($body);
  83. $fields = $query->getAll();
  84. $request->addPostFields($fields);
  85. }
  86. // TODO: use only $req->acceptLanguage() in Managers
  87. $_SERVER['HTTP_ACCEPT_LANGUAGE'] = (string) $request->getHeaders()->get('accept-language');
  88. $routes = array(
  89. '/' => 'executeUiBooter',
  90. '/api' => 'executeApiCall',
  91. '/api/group' => 'executeApiCallGroup',
  92. '/sbin/apicall.php' => 'executeApiCall', // @deprecated
  93. '/sbin/apicallgroup.php' => 'executeApiCallGroup', // @deprecated
  94. '/sbin/rawdatacall.php' => 'executeRawDataCall',
  95. '#^/([a-zA-Z0-9-_.]+)\.html$#' => 'executeUiBooter',
  96. '#^/(bin|boot|etc|home|tmp|usr|var)/(.*)$#' => 'executeReadFile',
  97. '/webos.webapp' => 'executeReadManifest',
  98. '/hello' => 'executeSayHello'
  99. );
  100. foreach ($routes as $path => $method) {
  101. $matched = false;
  102. if (substr($path, 0, 1) == '#') { // Regex
  103. if (preg_match($path, $requestPath, $matches)) {
  104. $result = $this->$method($from, $request, $matches);
  105. $matched = true;
  106. }
  107. } else {
  108. if ($path == $requestPath) {
  109. $result = $this->$method($from, $request);
  110. $matched = true;
  111. }
  112. }
  113. if ($matched) {
  114. if (empty($result)) {
  115. $result = '';
  116. }
  117. if ($result instanceof ResponseContent) {
  118. $result = $result->generate();
  119. }
  120. if ($result instanceof HTTPServerResponse) {
  121. if ($result->headersSent()) { // Implicit mode, content already sent
  122. if ($result->getHeader('Connection') != 'keep-alive') {
  123. $from->close();
  124. }
  125. } else {
  126. $result->send();
  127. }
  128. return;
  129. }
  130. $response = null;
  131. if (is_string($result)) {
  132. $response = new Response(200, array(), (string)$result);
  133. } else {
  134. $response = $result;
  135. }
  136. $from->send((string)$response);
  137. $from->close();
  138. return;
  139. }
  140. }
  141. $resp = new Response(404, array('Content-Type' => 'text/plain'), '404 Not Found');
  142. $from->send((string)$resp);
  143. $from->close();
  144. }
  145. public function onError(ConnectionInterface $from, Exception $e) {}
  146. public function onClose(ConnectionInterface $from) {}
  147. protected function getRequest($conn, $req) {
  148. return new HTTPServerRequest($conn, $req);
  149. }
  150. protected function getResponse($conn, $req) {
  151. $res = new HTTPServerResponse($conn);
  152. // Problem with keep-alive: URLs are not routed properly - they are handled by THIS controller
  153. // Must be handled by App instead
  154. if (strtolower($req->getHeaders()->get('connection')) == 'keep-alive' && false) {
  155. $res->addHeader('Connection: keep-alive');
  156. } else {
  157. $res->addHeader('Connection: close');
  158. }
  159. // Send cookie for sessions
  160. if ($conn->Session->isStarted() && $conn->Session->getId() != $req->getCookie(ini_get('session.name'))) {
  161. $cookiesParams = session_get_cookie_params();
  162. $header = ini_get('session.name').'='.$conn->Session->getId().';'
  163. .'Path='.$cookiesParams['path'];
  164. if (!empty($cookiesParams['lifetime'])) {
  165. $header .= ';Max-Age='.$cookiesParams['lifetime'];
  166. }
  167. if (!empty($cookiesParams['domain'])) {
  168. $header .= ';Domain='.$cookiesParams['domain'];
  169. }
  170. if ($cookiesParams['secure']) {
  171. $header .= ';Secure';
  172. }
  173. if ($cookiesParams['httponly']) {
  174. $header .= ';HttpOnly';
  175. }
  176. $res->addHeader('Set-Cookie: ' . $header);
  177. }
  178. return $res;
  179. }
  180. protected function executeUiBooter($conn, $req, $matches = null) {
  181. if (!empty($matches)) {
  182. $req->getQuery()->set('ui', $matches[1]);
  183. }
  184. $request = $this->getRequest($conn, $req);
  185. $response = $this->getResponse($conn, $req);
  186. $bootstrap = new UserInterfaceBooter();
  187. $bootstrap->emulate(null, $request, $response);
  188. $bootstrap->run();
  189. return $bootstrap->httpResponse();
  190. }
  191. protected function executeApiCall($conn, $req) {
  192. $request = $this->getRequest($conn, $req);
  193. $response = $this->getResponse($conn, $req);
  194. $apiCall = new Api;
  195. $apiCall->emulate($req->getPostFields()->getAll(), $request, $response);
  196. $apiCall->run();
  197. return $apiCall->httpResponse();
  198. }
  199. protected function executeApiCallGroup($conn, $req) {
  200. /*$request = new HTTPServerRequest($conn, $req);
  201. $response = new HTTPServerResponse($conn);
  202. $apiGroupCall = new ApiGroup;
  203. $apiGroupCall->emulate($req->getPostFields()->getAll(), $request, $response);
  204. $apiGroupCall->run();
  205. return $apiGroupCall->httpResponse();*/
  206. $fields = $req->getPostFields()->getAll();
  207. if (!isset($fields['data'])) {
  208. $fields['data'] = array();
  209. }
  210. $reqsData = json_decode($fields['data'], true);
  211. $responses = array();
  212. foreach($reqsData as $reqData) {
  213. $subreq = clone $req;
  214. foreach ($subreq->getPostFields() as $name => $value) {
  215. $subreq->removePostField($name);
  216. }
  217. $subreq->addPostFields($reqData);
  218. $responses[] = $this->executeApiCall($conn, $subreq)->content();
  219. }
  220. $resCtn = new ApiGroupResponse;
  221. $resCtn->setResponses($responses);
  222. $res = $this->getResponse($conn, $req);
  223. $res->addHeader('Content-Type: application/json');
  224. $res->setContent($resCtn);
  225. return $res;
  226. }
  227. protected function executeRawDataCall($conn, $req) {
  228. $request = $this->getRequest($conn, $req);
  229. $response = $this->getResponse($conn, $req);
  230. $dataCall = new RawDataCall();
  231. $dataCall->emulate(null, $request, $response);
  232. $dataCall->run();
  233. return $dataCall->httpResponse();
  234. }
  235. protected function executeReadFile($conn, $req, $matches) {
  236. $req->getQuery()->set('path', '/'.$matches[1].'/'.urldecode($matches[2]));
  237. return $this->executeRawDataCall($conn, $req);
  238. }
  239. protected function executeReadManifest($conn, $req) {
  240. $req->getQuery()->set('type', 'firefox');
  241. $request = $this->getRequest($conn, $req);
  242. $response = $this->getResponse($conn, $req);
  243. $manifestCall = new ManifestCall();
  244. $manifestCall->emulate(null, $request, $response);
  245. $manifestCall->run();
  246. return $manifestCall->httpResponse();
  247. }
  248. protected function executeSayHello($conn, $req) {
  249. return 'Hello world!';
  250. }
  251. /**
  252. * @param string $langDef Input to convert
  253. * @return string
  254. * @see https://github.com/ratchetphp/Ratchet/blob/master/src/Ratchet/Session/SessionProvider.php
  255. */
  256. protected function toClassCase($langDef) {
  257. return str_replace(' ', '', ucwords(str_replace('_', ' ', $langDef)));
  258. }
  259. }