| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631 |
- <?php
- /**
- * This file is part of workerman.
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the MIT-LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @author walkor<walkor@workerman.net>
- * @copyright walkor<walkor@workerman.net>
- * @link http://www.workerman.net/
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
- */
- namespace Workerman\Protocols\Http;
- use Workerman\Connection\TcpConnection;
- use Workerman\Protocols\Http\Session;
- use Workerman\Protocols\Http;
- use Workerman\Worker;
- /**
- * Class Request
- * @package Workerman\Protocols\Http
- */
- class Request
- {
- /**
- * Connection.
- *
- * @var TcpConnection
- */
- public $connection = null;
- /**
- * Session instance.
- *
- * @var Session
- */
- public $session = null;
- /**
- * Properties.
- *
- * @var array
- */
- public $properties = array();
- /**
- * Http buffer.
- *
- * @var string
- */
- protected $_buffer = null;
- /**
- * Request data.
- *
- * @var array
- */
- protected $_data = null;
- /**
- * Header cache.
- *
- * @var array
- */
- protected static $_headerCache = array();
- /**
- * Get cache.
- *
- * @var array
- */
- protected static $_getCache = array();
- /**
- * Post cache.
- *
- * @var array
- */
- protected static $_postCache = array();
- /**
- * Enable cache.
- *
- * @var bool
- */
- protected static $_enableCache = true;
- /**
- * Request constructor.
- *
- * @param $buffer
- */
- public function __construct($buffer)
- {
- $this->_buffer = $buffer;
- }
- /**
- * $_GET.
- *
- * @param null $name
- * @param null $default
- * @return mixed|null
- */
- public function get($name = null, $default = null)
- {
- if (!isset($this->_data['get'])) {
- $this->parseGet();
- }
- if (null === $name) {
- return $this->_data['get'];
- }
- return isset($this->_data['get'][$name]) ? $this->_data['get'][$name] : $default;
- }
- /**
- * $_POST.
- *
- * @param $name
- * @param null $default
- * @return mixed|null
- */
- public function post($name = null, $default = null)
- {
- if (!isset($this->_data['post'])) {
- $this->parsePost();
- }
- if (null === $name) {
- return $this->_data['post'];
- }
- return isset($this->_data['post'][$name]) ? $this->_data['post'][$name] : $default;
- }
- /**
- * Get header item by name.
- *
- * @param null $name
- * @param null $default
- * @return string|null
- */
- public function header($name = null, $default = null)
- {
- if (!isset($this->_data['headers'])) {
- $this->parseHeaders();
- }
- if (null === $name) {
- return $this->_data['headers'];
- }
- $name = \strtolower($name);
- return isset($this->_data['headers'][$name]) ? $this->_data['headers'][$name] : $default;
- }
- /**
- * Get cookie item by name.
- *
- * @param null $name
- * @param null $default
- * @return string|null
- */
- public function cookie($name = null, $default = null)
- {
- if (!isset($this->_data['cookie'])) {
- \parse_str(\str_replace('; ', '&', $this->header('cookie')), $this->_data['cookie']);
- }
- if ($name === null) {
- return $this->_data['cookie'];
- }
- return isset($this->_data['cookie'][$name]) ? $this->_data['cookie'][$name] : $default;
- }
- /**
- * Get upload files.
- *
- * @param null $name
- * @return array|null
- */
- public function file($name = null)
- {
- if (!isset($this->_data['files'])) {
- $this->parsePost();
- }
- if (null === $name) {
- return $this->_data['files'];
- }
- return isset($this->_data['files'][$name]) ? $this->_data['files'][$name] : null;
- }
- /**
- * Get method.
- *
- * @return string
- */
- public function method()
- {
- if (!isset($this->_data['method'])) {
- $this->parseHeadFirstLine();
- }
- return $this->_data['method'];
- }
- /**
- * Get http protocol version.
- *
- * @return string.
- */
- public function protocolVersion()
- {
- if (!isset($this->_data['protocolVersion'])) {
- $this->parseProtocolVersion();
- }
- return $this->_data['protocolVersion'];
- }
- /**
- * Get host.
- *
- * @param bool $without_port
- * @return string
- */
- public function host($without_port = false)
- {
- $host = $this->header('host');
- if ($without_port && $pos = \strpos($host, ':')) {
- return \substr($host, 0, $pos);
- }
- return $host;
- }
- /**
- * Get uri.
- *
- * @return mixed
- */
- public function uri()
- {
- if (!isset($this->_data['uri'])) {
- $this->parseHeadFirstLine();
- }
- return $this->_data['uri'];
- }
- /**
- * Get path.
- *
- * @return mixed
- */
- public function path()
- {
- if (!isset($this->_data['path'])) {
- $this->_data['path'] = \parse_url($this->uri(), PHP_URL_PATH);
- }
- return $this->_data['path'];
- }
- /**
- * Get query string.
- *
- * @return mixed
- */
- public function queryString()
- {
- if (!isset($this->_data['query_string'])) {
- $this->_data['query_string'] = \parse_url($this->uri(), PHP_URL_QUERY);
- }
- return $this->_data['query_string'];
- }
- /**
- * Get session.
- *
- * @return bool|\Workerman\Protocols\Http\Session
- */
- public function session()
- {
- if ($this->session === null) {
- $session_id = $this->sessionId();
- if ($session_id === false) {
- return false;
- }
- $this->session = new Session($session_id);
- }
- return $this->session;
- }
- /**
- * Get session id.
- *
- * @return bool|mixed
- */
- public function sessionId()
- {
- if (!isset($this->_data['sid'])) {
- $session_name = Http::sessionName();
- $sid = $this->cookie($session_name);
- if ($sid === '' || $sid === null) {
- if ($this->connection === null) {
- Worker::safeEcho('Request->session() fail, header already send');
- return false;
- }
- $sid = static::createSessionId();
- $cookie_params = \session_get_cookie_params();
- $this->connection->__header['Set-Cookie'] = array($session_name . '=' . $sid
- . (empty($cookie_params['domain']) ? '' : '; Domain=' . $cookie_params['domain'])
- . (empty($cookie_params['lifetime']) ? '' : '; Max-Age=' . ($cookie_params['lifetime'] + \time()))
- . (empty($cookie_params['path']) ? '' : '; Path=' . $cookie_params['path'])
- . (empty($cookie_params['samesite']) ? '' : '; SameSite=' . $cookie_params['samesite'])
- . (!$cookie_params['secure'] ? '' : '; Secure')
- . (!$cookie_params['httponly'] ? '' : '; HttpOnly'));
- }
- $this->_data['sid'] = $sid;
- }
- return $this->_data['sid'];
- }
- /**
- * Get http raw head.
- *
- * @return string
- */
- public function rawHead()
- {
- if (!isset($this->_data['head'])) {
- $this->_data['head'] = \strstr($this->_buffer, "\r\n\r\n", true);
- }
- return $this->_data['head'];
- }
- /**
- * Get http raw body.
- *
- * @return string
- */
- public function rawBody()
- {
- return \substr($this->_buffer, \strpos($this->_buffer, "\r\n\r\n") + 4);
- }
- /**
- * Get raw buffer.
- *
- * @return string
- */
- public function rawBuffer()
- {
- return $this->_buffer;
- }
- /**
- * Enable or disable cache.
- *
- * @param $value
- */
- public static function enableCache($value)
- {
- static::$_enableCache = (bool)$value;
- }
- /**
- * Parse first line of http header buffer.
- *
- * @return void
- */
- protected function parseHeadFirstLine()
- {
- $first_line = \strstr($this->_buffer, "\r\n", true);
- $tmp = \explode(' ', $first_line, 3);
- $this->_data['method'] = $tmp[0];
- $this->_data['uri'] = isset($tmp[1]) ? $tmp[1] : '/';
- }
- /**
- * Parse protocol version.
- *
- * @return void
- */
- protected function parseProtocolVersion()
- {
- $first_line = \strstr($this->_buffer, "\r\n", true);
- $protoco_version = substr(\strstr($first_line, 'HTTP/'), 5);
- $this->_data['protocolVersion'] = $protoco_version ? $protoco_version : '1.0';
- }
- /**
- * Parse headers.
- *
- * @return void
- */
- protected function parseHeaders()
- {
- $this->_data['headers'] = array();
- $raw_head = $this->rawHead();
- $head_buffer = \substr($raw_head, \strpos($raw_head, "\r\n") + 2);
- $cacheable = static::$_enableCache && !isset($head_buffer[2048]);
- if ($cacheable && isset(static::$_headerCache[$head_buffer])) {
- $this->_data['headers'] = static::$_headerCache[$head_buffer];
- return;
- }
- $head_data = \explode("\r\n", $head_buffer);
- foreach ($head_data as $content) {
- if (false !== \strpos($content, ':')) {
- list($key, $value) = \explode(':', $content, 2);
- $this->_data['headers'][\strtolower($key)] = \ltrim($value);
- } else {
- $this->_data['headers'][\strtolower($content)] = '';
- }
- }
- if ($cacheable) {
- static::$_headerCache[$head_buffer] = $this->_data['headers'];
- if (\count(static::$_headerCache) > 128) {
- unset(static::$_headerCache[key(static::$_headerCache)]);
- }
- }
- }
- /**
- * Parse head.
- *
- * @return void
- */
- protected function parseGet()
- {
- $query_string = $this->queryString();
- $this->_data['get'] = array();
- if ($query_string === '') {
- return;
- }
- $cacheable = static::$_enableCache && !isset($query_string[1024]);
- if ($cacheable && isset(static::$_getCache[$query_string])) {
- $this->_data['get'] = static::$_getCache[$query_string];
- return;
- }
- \parse_str($query_string, $this->_data['get']);
- if ($cacheable) {
- static::$_getCache[$query_string] = $this->_data['get'];
- if (\count(static::$_getCache) > 256) {
- unset(static::$_getCache[key(static::$_getCache)]);
- }
- }
- }
- /**
- * Parse post.
- *
- * @return void
- */
- protected function parsePost()
- {
- $body_buffer = $this->rawBody();
- $this->_data['post'] = $this->_data['files'] = array();
- if ($body_buffer === '') {
- return;
- }
- $cacheable = static::$_enableCache && !isset($body_buffer[1024]);
- if ($cacheable && isset(static::$_postCache[$body_buffer])) {
- $this->_data['post'] = static::$_postCache[$body_buffer];
- return;
- }
- $content_type = $this->header('content-type', '');
- if (\preg_match('/boundary="?(\S+)"?/', $content_type, $match)) {
- $http_post_boundary = '--' . $match[1];
- $this->parseUploadFiles($http_post_boundary);
- return;
- }
- if (\preg_match('/\bjson\b/i', $content_type)) {
- $this->_data['post'] = (array) json_decode($body_buffer, true);
- } else {
- \parse_str($body_buffer, $this->_data['post']);
- }
- if ($cacheable) {
- static::$_postCache[$body_buffer] = $this->_data['post'];
- if (\count(static::$_postCache) > 256) {
- unset(static::$_postCache[key(static::$_postCache)]);
- }
- }
- }
- /**
- * Parse upload files.
- *
- * @param $http_post_boundary
- * @return void
- */
- protected function parseUploadFiles($http_post_boundary)
- {
- $http_body = $this->rawBody();
- $http_body = \substr($http_body, 0, \strlen($http_body) - (\strlen($http_post_boundary) + 4));
- $boundary_data_array = \explode($http_post_boundary . "\r\n", $http_body);
- if ($boundary_data_array[0] === '') {
- unset($boundary_data_array[0]);
- }
- $key = -1;
- $files = array();
- foreach ($boundary_data_array as $boundary_data_buffer) {
- list($boundary_header_buffer, $boundary_value) = \explode("\r\n\r\n", $boundary_data_buffer, 2);
- // Remove \r\n from the end of buffer.
- $boundary_value = \substr($boundary_value, 0, -2);
- $key++;
- foreach (\explode("\r\n", $boundary_header_buffer) as $item) {
- list($header_key, $header_value) = \explode(": ", $item);
- $header_key = \strtolower($header_key);
- switch ($header_key) {
- case "content-disposition":
- // Is file data.
- if (\preg_match('/name="(.*?)"; filename="(.*?)"/i', $header_value, $match)) {
- $error = 0;
- $tmp_file = '';
- $size = \strlen($boundary_value);
- $tmp_upload_dir = HTTP::uploadTmpDir();
- if (!$tmp_upload_dir) {
- $error = UPLOAD_ERR_NO_TMP_DIR;
- } else {
- $tmp_file = \tempnam($tmp_upload_dir, 'workerman.upload.');
- if ($tmp_file === false || false == \file_put_contents($tmp_file, $boundary_value)) {
- $error = UPLOAD_ERR_CANT_WRITE;
- }
- }
- // Parse upload files.
- $files[$key] = array(
- 'key' => $match[1],
- 'name' => $match[2],
- 'tmp_name' => $tmp_file,
- 'size' => $size,
- 'error' => $error
- );
- break;
- } // Is post field.
- else {
- // Parse $_POST.
- if (\preg_match('/name="(.*?)"$/', $header_value, $match)) {
- $this->_data['post'][$match[1]] = $boundary_value;
- }
- }
- break;
- case "content-type":
- // add file_type
- $files[$key]['type'] = \trim($header_value);
- break;
- }
- }
- }
- foreach ($files as $file) {
- $key = $file['key'];
- unset($file['key']);
- $this->_data['files'][$key] = $file;
- }
- }
- /**
- * Create session id.
- *
- * @return string
- */
- protected static function createSessionId()
- {
- return \bin2hex(\pack('d', \microtime(true)) . \pack('N', \mt_rand()));
- }
- /**
- * Setter.
- *
- * @param $name
- * @param $value
- * @return void
- */
- public function __set($name, $value)
- {
- $this->properties[$name] = $value;
- }
- /**
- * Getter.
- *
- * @param $name
- * @return mixed|null
- */
- public function __get($name)
- {
- return isset($this->properties[$name]) ? $this->properties[$name] : null;
- }
- /**
- * Isset.
- *
- * @param $name
- * @return bool
- */
- public function __isset($name)
- {
- return isset($this->properties[$name]);
- }
- /**
- * Unset.
- *
- * @param $name
- * @return void
- */
- public function __unset($name)
- {
- unset($this->properties[$name]);
- }
- /**
- * __toString.
- */
- public function __toString()
- {
- return $this->_buffer;
- }
- /**
- * __destruct.
- *
- * @return void
- */
- public function __destruct()
- {
- if (isset($this->_data['files'])) {
- \clearstatcache();
- foreach ($this->_data['files'] as $item) {
- if (\is_file($item['tmp_name'])) {
- \unlink($item['tmp_name']);
- }
- }
- }
- }
- }
|