/output/swoole_library/src/core/Coroutine/FastCGI/Proxy.php

https://github.com/swoole/ide-helper · PHP · 205 lines · 163 code · 26 blank · 16 comment · 18 complexity · e508bf64bd1d021514feba4ccc875034 MD5 · raw file

  1. <?php
  2. /**
  3. * This file is part of Swoole.
  4. *
  5. * @link https://www.swoole.com
  6. * @contact team@swoole.com
  7. * @license https://github.com/swoole/library/blob/master/LICENSE
  8. */
  9. declare(strict_types=1);
  10. namespace Swoole\Coroutine\FastCGI;
  11. use InvalidArgumentException;
  12. use Swoole\FastCGI\HttpRequest;
  13. use Swoole\FastCGI\HttpResponse;
  14. use Swoole\Http;
  15. class Proxy
  16. {
  17. /* @var string */
  18. protected $host;
  19. /* @var int */
  20. protected $port;
  21. /* @var float */
  22. protected $timeout = -1;
  23. /* @var string */
  24. protected $documentRoot;
  25. /* @var bool */
  26. protected $https = false;
  27. /* @var string */
  28. protected $index = 'index.php';
  29. /* @var array */
  30. protected $params = [];
  31. /* @var null|callable */
  32. protected $staticFileFilter;
  33. public function __construct(string $url, string $documentRoot = '/')
  34. {
  35. [$this->host, $this->port] = Client::parseUrl($url);
  36. $this->documentRoot = $documentRoot;
  37. $this->staticFileFilter = [$this, 'staticFileFiltrate'];
  38. }
  39. public function withTimeout(float $timeout): self
  40. {
  41. $this->timeout = $timeout;
  42. return $this;
  43. }
  44. public function withHttps(bool $https): self
  45. {
  46. $this->https = $https;
  47. return $this;
  48. }
  49. public function withIndex(string $index): self
  50. {
  51. $this->index = $index;
  52. return $this;
  53. }
  54. public function getParam(string $name): ?string
  55. {
  56. return $this->params[$name] ?? null;
  57. }
  58. public function withParam(string $name, string $value): self
  59. {
  60. $this->params[$name] = $value;
  61. return $this;
  62. }
  63. public function withoutParam(string $name): self
  64. {
  65. unset($this->params[$name]);
  66. return $this;
  67. }
  68. public function getParams(): array
  69. {
  70. return $this->params;
  71. }
  72. public function withParams(array $params): self
  73. {
  74. $this->params = $params;
  75. return $this;
  76. }
  77. public function withAddedParams(array $params): self
  78. {
  79. $this->params = $params + $this->params;
  80. return $this;
  81. }
  82. public function withStaticFileFilter(?callable $filter): self
  83. {
  84. $this->staticFileFilter = $filter;
  85. return $this;
  86. }
  87. public function translateRequest($userRequest): HttpRequest
  88. {
  89. $request = new HttpRequest();
  90. if ($userRequest instanceof \Swoole\Http\Request) {
  91. $server = $userRequest->server;
  92. $headers = $userRequest->header;
  93. $pathInfo = $userRequest->server['path_info'];
  94. $pathInfo = '/' . (ltrim($pathInfo, '/'));
  95. if (strlen($this->index) !== 0) {
  96. $extension = pathinfo($pathInfo, PATHINFO_EXTENSION);
  97. if (empty($extension)) {
  98. $pathInfo = rtrim($pathInfo, '/') . '/' . $this->index;
  99. }
  100. }
  101. $requestUri = $scriptName = $documentUri = $server['request_uri'];
  102. $queryString = $server['query_string'] ?? '';
  103. if (strlen($queryString) !== 0) {
  104. $requestUri .= "?{$server['query_string']}";
  105. }
  106. $request
  107. ->withDocumentRoot($this->documentRoot)
  108. ->withScriptFilename($this->documentRoot . $pathInfo)
  109. ->withScriptName($scriptName)
  110. ->withDocumentUri($documentUri)
  111. ->withServerProtocol($server['server_protocol'])
  112. ->withServerAddr('127.0.0.1')
  113. ->withServerPort($server['server_port'])
  114. ->withRemoteAddr($server['remote_addr'])
  115. ->withRemotePort($server['remote_port'])
  116. ->withMethod($server['request_method'])
  117. ->withRequestUri($requestUri)
  118. ->withQueryString($queryString)
  119. ->withContentType($headers['content-type'] ?? '')
  120. ->withContentLength((int) ($headers['content-length'] ?? 0))
  121. ->withHeaders($headers)
  122. ->withBody($userRequest->rawContent())
  123. ->withAddedParams($this->params);
  124. if ($this->https) {
  125. $request->withParam('HTTPS', '1');
  126. }
  127. } else {
  128. throw new InvalidArgumentException('Not supported on ' . get_class($userRequest));
  129. }
  130. return $request;
  131. }
  132. public function translateResponse(HttpResponse $response, $userResponse): void
  133. {
  134. if ($userResponse instanceof \Swoole\Http\Response) {
  135. $userResponse->status($response->getStatusCode(), $response->getReasonPhrase());
  136. $userResponse->header = $response->getHeaders();
  137. $userResponse->cookie = $response->getSetCookieHeaderLines();
  138. $userResponse->end($response->getBody());
  139. } else {
  140. throw new InvalidArgumentException('Not supported on ' . get_class($userResponse));
  141. }
  142. }
  143. public function pass($userRequest, $userResponse): void
  144. {
  145. if (!($userRequest instanceof HttpRequest)) {
  146. $request = $this->translateRequest($userRequest);
  147. } else {
  148. $request = $userRequest;
  149. }
  150. unset($userRequest);
  151. if ($this->staticFileFilter) {
  152. $filter = $this->staticFileFilter;
  153. if ($filter($request, $userResponse)) {
  154. return;
  155. }
  156. }
  157. $client = new Client($this->host, $this->port);
  158. $response = $client->execute($request, $this->timeout);
  159. $this->translateResponse($response, $userResponse);
  160. }
  161. /* @return bool ['hit' => true, 'miss' => false] */
  162. public function staticFileFiltrate(HttpRequest $request, $userResponse): bool
  163. {
  164. if ($userResponse instanceof \Swoole\Http\Response) {
  165. $extension = pathinfo($request->getScriptFilename(), PATHINFO_EXTENSION);
  166. if ($extension !== 'php') {
  167. $realPath = realpath($request->getScriptFilename());
  168. if (!$realPath || strpos($realPath, $this->documentRoot) !== 0 || !is_file($realPath)) {
  169. $userResponse->status(Http\Status::NOT_FOUND);
  170. } else {
  171. $userResponse->sendfile($realPath);
  172. }
  173. return true;
  174. }
  175. return false;
  176. }
  177. throw new InvalidArgumentException('Not supported on ' . get_class($userResponse));
  178. }
  179. }