Http.php 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <?php
  2. /**
  3. * This file is part of workerman.
  4. *
  5. * Licensed under The MIT License
  6. * For full copyright and license information, please see the MIT-LICENSE.txt
  7. * Redistributions of files must retain the above copyright notice.
  8. *
  9. * @author walkor<walkor@workerman.net>
  10. * @copyright walkor<walkor@workerman.net>
  11. * @link http://www.workerman.net/
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace Workerman\Protocols;
  15. use Workerman\Connection\TcpConnection;
  16. use Workerman\Protocols\Http\Request;
  17. use Workerman\Protocols\Http\Response;
  18. use Workerman\Protocols\Websocket;
  19. use Workerman\Worker;
  20. /**
  21. * Class Http.
  22. * @package Workerman\Protocols
  23. */
  24. class Http
  25. {
  26. /**
  27. * Request class name.
  28. *
  29. * @var string
  30. */
  31. protected static $_requestClass = 'Workerman\Protocols\Http\Request';
  32. /**
  33. * Session name.
  34. *
  35. * @var string
  36. */
  37. protected static $_sessionName = 'PHPSID';
  38. /**
  39. * Upload tmp dir.
  40. *
  41. * @var string
  42. */
  43. protected static $_uploadTmpDir = '';
  44. /**
  45. * Cache.
  46. *
  47. * @var array
  48. */
  49. protected static $_cache = array();
  50. /**
  51. * Open cache.
  52. *
  53. * @var bool.
  54. */
  55. public static $_enableCache = true;
  56. /**
  57. * Get or set session name.
  58. *
  59. * @param null $name
  60. * @return string
  61. */
  62. public static function sessionName($name = null)
  63. {
  64. if ($name !== null && $name !== '') {
  65. static::$_sessionName = (string)$name;
  66. }
  67. return static::$_sessionName;
  68. }
  69. /**
  70. * Get or set the request class name.
  71. *
  72. * @param null $class_name
  73. * @return string
  74. */
  75. public static function requestClass($class_name = null)
  76. {
  77. if ($class_name) {
  78. static::$_requestClass = $class_name;
  79. }
  80. return static::$_requestClass;
  81. }
  82. /**
  83. * Enable or disable Cache.
  84. *
  85. * @param $value
  86. */
  87. public static function enableCache($value)
  88. {
  89. static::$_enableCache = (bool)$value;
  90. }
  91. /**
  92. * Check the integrity of the package.
  93. *
  94. * @param string $recv_buffer
  95. * @param TcpConnection $connection
  96. * @return int
  97. */
  98. public static function input($recv_buffer, TcpConnection $connection)
  99. {
  100. $crlf_pos = \strpos($recv_buffer, "\r\n\r\n");
  101. if (false === $crlf_pos) {
  102. // Judge whether the package length exceeds the limit.
  103. if ($recv_len = \strlen($recv_buffer) >= 16384) {
  104. $connection->send("HTTP/1.1 413 Request Entity Too Large\r\n\r\n");
  105. $connection->consumeRecvBuffer($recv_len);
  106. return 0;
  107. }
  108. return 0;
  109. }
  110. $head_len = $crlf_pos + 4;
  111. $method = \strstr($recv_buffer, ' ', true);
  112. if ($method === 'GET' || $method === 'OPTIONS' || $method === 'HEAD') {
  113. return $head_len;
  114. } else if ($method !== 'POST' && $method !== 'PUT' && $method !== 'DELETE') {
  115. $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n", true);
  116. $connection->consumeRecvBuffer(\strlen($recv_buffer));
  117. return 0;
  118. }
  119. $header = \substr($recv_buffer, 0, $crlf_pos);
  120. if ($pos = \strpos($header, "\r\nContent-Length: ")) {
  121. return $head_len + (int)\substr($header, $pos + 18, 10);
  122. } else if (\preg_match("/\r\ncontent-length: ?(\d+)/i", $header, $match)) {
  123. return $head_len + $match[1];
  124. }
  125. return $method === 'DELETE' ? $head_len : 0;
  126. }
  127. /**
  128. * Http decode.
  129. *
  130. * @param string $recv_buffer
  131. * @param TcpConnection $connection
  132. * @return \Workerman\Protocols\Http\Request
  133. */
  134. public static function decode($recv_buffer, TcpConnection $connection)
  135. {
  136. $cacheable = static::$_enableCache && \strlen($recv_buffer) < 512;
  137. if (true === $cacheable && isset(static::$_cache[$recv_buffer])) {
  138. $request = static::$_cache[$recv_buffer];
  139. $request->connection = $connection;
  140. $connection->__request = $request;
  141. $request->properties = array();
  142. return $request;
  143. }
  144. $request = new static::$_requestClass($recv_buffer);
  145. $request->connection = $connection;
  146. $connection->__request = $request;
  147. if (true === $cacheable) {
  148. static::$_cache[$recv_buffer] = $request;
  149. if (\count(static::$_cache) > 512) {
  150. unset(static::$_cache[key(static::$_cache)]);
  151. }
  152. }
  153. return $request;
  154. }
  155. /**
  156. * Http encode.
  157. *
  158. * @param string|Response $response
  159. * @param TcpConnection $connection
  160. * @return string
  161. */
  162. public static function encode($response, TcpConnection $connection)
  163. {
  164. if (isset($connection->__request)) {
  165. $connection->__request->session = null;
  166. $connection->__request->connection = null;
  167. $connection->__request = null;
  168. }
  169. if (\is_scalar($response) || null === $response) {
  170. $ext_header = '';
  171. if (isset($connection->__header)) {
  172. foreach ($connection->__header as $name => $value) {
  173. if (\is_array($value)) {
  174. foreach ($value as $item) {
  175. $ext_header = "$name: $item\r\n";
  176. }
  177. } else {
  178. $ext_header = "$name: $value\r\n";
  179. }
  180. }
  181. unset($connection->__header);
  182. }
  183. $body_len = \strlen($response);
  184. return "HTTP/1.1 200 OK\r\nServer: workerman\r\n{$ext_header}Connection: keep-alive\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: $body_len\r\n\r\n$response";
  185. }
  186. if (isset($connection->__header)) {
  187. $response->withHeaders($connection->__header);
  188. unset($connection->__header);
  189. }
  190. if (isset($response->file)) {
  191. $file = $response->file;
  192. $body_len = (int)\filesize($file);
  193. $response->header('Content-Length', $body_len);
  194. if ($body_len < 1024 * 1024) {
  195. $connection->send((string)$response . file_get_contents($file, false, null, -1, $body_len), true);
  196. return '';
  197. }
  198. $handler = \fopen($file, 'r');
  199. if (false === $handler) {
  200. $connection->close(new Response(403, null, '403 Forbidden'));
  201. return '';
  202. }
  203. $connection->send((string)$response, true);
  204. static::sendStream($connection, $handler);
  205. return '';
  206. }
  207. return (string)$response;
  208. }
  209. /**
  210. * Send remainder of a stream to client.
  211. *
  212. * @param TcpConnection $connection
  213. * @param $handler
  214. */
  215. protected static function sendStream(TcpConnection $connection, $handler)
  216. {
  217. $connection->bufferFull = false;
  218. // Read file content from disk piece by piece and send to client.
  219. $do_write = function () use ($connection, $handler) {
  220. // Send buffer not full.
  221. while ($connection->bufferFull === false) {
  222. // Read from disk.
  223. $buffer = \fread($handler, 8192);
  224. // Read eof.
  225. if ($buffer === '' || $buffer === false) {
  226. fclose($handler);
  227. $connection->onBufferDrain = null;
  228. return;
  229. }
  230. $connection->send($buffer, true);
  231. }
  232. };
  233. // Send buffer full.
  234. $connection->onBufferFull = function ($connection) {
  235. $connection->bufferFull = true;
  236. };
  237. // Send buffer drain.
  238. $connection->onBufferDrain = function ($connection) use ($do_write) {
  239. $connection->bufferFull = false;
  240. $do_write();
  241. };
  242. $do_write();
  243. }
  244. /**
  245. * Set or get uploadTmpDir.
  246. *
  247. * @return bool|string
  248. */
  249. public static function uploadTmpDir($dir = null)
  250. {
  251. if (null !== $dir) {
  252. static::$_uploadTmpDir = $dir;
  253. }
  254. if (static::$_uploadTmpDir === '') {
  255. if ($upload_tmp_dir = \ini_get('upload_tmp_dir')) {
  256. static::$_uploadTmpDir = $upload_tmp_dir;
  257. } else if ($upload_tmp_dir = \sys_get_temp_dir()) {
  258. static::$_uploadTmpDir = $upload_tmp_dir;
  259. }
  260. }
  261. static::$_uploadTmpDir;
  262. }
  263. }