fix:更新已知bug,优化代码

This commit is contained in:
Ying
2022-11-28 19:11:12 +08:00
parent f6aee95cfc
commit 9445b206a2
1378 changed files with 53759 additions and 20789 deletions

View File

@@ -16,6 +16,7 @@ namespace Workerman\Connection;
/**
* ConnectionInterface.
*/
#[\AllowDynamicProperties]
abstract class ConnectionInterface
{
/**

View File

@@ -13,6 +13,9 @@
*/
namespace Workerman\Events;
use Throwable;
use Workerman\Worker;
/**
* select eventloop
*/
@@ -211,6 +214,7 @@ class Select implements EventInterface
*/
protected function tick()
{
$tasks_to_insert = [];
while (!$this->_scheduler->isEmpty()) {
$scheduler_data = $this->_scheduler->top();
$timer_id = $scheduler_data['data'];
@@ -228,14 +232,28 @@ class Select implements EventInterface
$task_data = $this->_eventTimer[$timer_id];
if ($task_data[2] === self::EV_TIMER) {
$next_run_time = $time_now + $task_data[3];
$this->_scheduler->insert($timer_id, -$next_run_time);
$tasks_to_insert[] = [$timer_id, -$next_run_time];
}
try {
\call_user_func_array($task_data[0], $task_data[1]);
} catch (Throwable $e) {
Worker::stopAll(250, $e);
}
\call_user_func_array($task_data[0], $task_data[1]);
if (isset($this->_eventTimer[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) {
$this->del($timer_id, self::EV_TIMER_ONCE);
}
continue;
} else {
break;
}
}
foreach ($tasks_to_insert as $item) {
$this->_scheduler->insert($item[0], $item[1]);
}
if (!$this->_scheduler->isEmpty()) {
$scheduler_data = $this->_scheduler->top();
$next_run_time = -$scheduler_data['priority'];
$time_now = \microtime(true);
$this->_selectTimeout = \max((int) (($next_run_time - $time_now) * 1000000), 0);
return;
}
$this->_selectTimeout = 100000000;
@@ -275,10 +293,8 @@ class Select implements EventInterface
} else {
$this->_selectTimeout >= 1 && usleep($this->_selectTimeout);
$ret = false;
}
if (!$this->_scheduler->isEmpty()) {
$this->tick();
}

View File

@@ -1,584 +0,0 @@
<?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 爬山虎<blogdaren@163.com>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Workerman\Protocols\FastCGI;
use Workerman\Worker;
use Workerman\Protocols\Fcgi;
class Request
{
/**
* allowed request methods
*
* @var array
*/
const ALLOWED_REQUEST_METHODS = ['GET', 'POST', 'PUT', 'HEAD', 'DELETE'];
/**
* allowed FastCGI roles
*
* @var array
*/
const ALLOWED_ROLES = [
Fcgi::FCGI_RESPONDER,
Fcgi::FCGI_AUTHORIZER,
Fcgi::FCGI_FILTER,
];
/**
* allowed content type
*
* @var array
*/
const ALLOWED_CONTENT_TYPES = [
self::MIME_URL_ENCODED_FORM_DATA,
self::MIME_MULTI_PART_FORM_DATA,
self::MIME_JSON_DATA,
];
/**
* the MIME type of url encoded form data
*
* @var string
*/
const MIME_URL_ENCODED_FORM_DATA = 'application/x-www-form-urlencoded';
/**
* the MIME type of multi part form data
*
* @var string
*/
const MIME_MULTI_PART_FORM_DATA = 'multipart/form-data; boundary=__X_FASTCGI_CLIENT_BOUNDARY__';
/**
* the MIME type of json data
*
* @var string
*/
const MIME_JSON_DATA = 'application/json';
/**
* FastCGI script to be executed
*
* @var string
*/
public $script = '';
/**
* content MIME Type
*
* @var string
*/
public $contentType = self::MIME_URL_ENCODED_FORM_DATA;
/**
* content data
*
* @var string
*/
public $content = '';
/**
* content length
*
* @var int
*/
public $contentLength = 0;
/**
* request uri
*
* @var string
*/
public $requestUri = '';
/**
* request method
*
* @var string
*/
public $requestMethod = 'GET';
/**
* query string
*
* @var string
*/
public $queryString = '';
/**
* gateway inteface
*
* @var string
*/
public $gatewayInterface = 'FastCGI/1.0';
/**
* server software
*
* @var string
*/
public $serverSoftware = 'FastCGI-Client';
/**
* server name
*
* @var string
*/
public $serverName = 'localhost';
/**
* request id
*
* @var int
*/
public $requestId = 0;
/**
* proxy counter for request id
*
* @var int
*/
static protected $_idCounter = 1;
/**
* custom params
*
* @var array
*/
public $customParams = [];
/**
* indicates FastCGI server to keep connection alive or not after finishing one request
*
* @var boolean
*/
public $keepAlive = true;
/**
* indicates FastCGI server to play the specific role
*
* @var string
*/
public $role = Fcgi::FCGI_RESPONDER;
/**
* @brief __construct
*
* @param string $script
* @param string|array $content
*
* @return void
*/
public function __construct($script = '', $content = '')
{
$this->setScript($script);
$this->setContent($content);
(self::$_idCounter >= (1 << 16)) && self::$_idCounter = 0;
$this->requestId = self::$_idCounter++;
}
/**
* @brief get request id
*
* @return int
*/
public function getRequestId()
{
return $this->requestId;
}
/**
* @brief set the role
*
* @param int $role
*
* @return object
*/
public function setRole($role = Fcgi::FCGI_RESPONDER)
{
if(!is_int($role) || !in_array($role, static::ALLOWED_ROLES))
{
$role = Fcgi::FCGI_RESPONDER;
}
$this->role = $role;
return $this;
}
/**
* @brief get the role
*
* @return int
*/
public function getRole()
{
return $this->role;
}
/**
* @brief set connection alive status
*
* @param boolean $status
*
* @return object
*/
public function setKeepAlive($status = true)
{
$this->keepAlive = !is_bool($status) ? true : $status;
return $this;
}
/**
* @brief get connection alive status
*
* @return boolean
*/
public function getKeepAlive()
{
return $this->keepAlive;
}
/**
* @brief get server software
*
* @return string
*/
public function getServerSoftware()
{
return $this->serverSoftware;
}
/**
* @brief set server software
*
* @param string $software
*
* @return object
*/
public function setServerSoftware($software)
{
if(!empty($software) && \is_string($software))
{
$this->serverSoftware = $software;
}
return $this;
}
/**
* @brief get server name
*
* @return string
*/
public function getServerName()
{
return $this->serverName;
}
/**
* @brief set server name
*
* @param string $name
*
* @return object
*/
public function setServerName($name)
{
if(!empty($name) && \is_string($name))
{
$this->serverName = $name;
}
return $this;
}
/**
* @brief get content type
*
* @return string
*/
public function getContentType()
{
return $this->contentType;
}
/**
* @brief set content type
*
* @param string $type
*
* @return object
*/
public function setContentType($type)
{
if(!\is_string($type) || !in_array($type, static::ALLOWED_CONTENT_TYPES))
{
$type = static::MIME_URL_ENCODED_FORM_DATA;
}
$this->contentType = $type;
return $this;
}
/**
* @brief get content
*
* @return string
*/
public function getContent()
{
return $this->content;
}
/**
* @brief set content
*
* @param string|array $content
*
* @return object
*/
public function setContent($content)
{
if(\is_string($content) || \is_array($content))
{
$this->content = !\is_string($content) ? http_build_query($content) : $content;
$this->contentLength = \strlen($this->content);
}
return $this;
}
/**
* @brief get content length
*
* @return int
*/
public function getContentLength()
{
return $this->contentLength;
}
/**
* @brief get gateway interface
*
* @return string
*/
public function getGatewayInterface()
{
return $this->gatewayInterface;
}
/**
* @brief set FastCGI script
*
* @param string $filename
*
* @return object
*/
public function setScript($filename)
{
if(!empty($filename) && \is_string($filename))
{
$this->script = $filename;
}
return $this;
}
/**
* @brief get FastCGI script
*
* @return string
*/
public function getScript()
{
return $this->script;
}
/**
* @brief set custom params
*
* @param array $pair
*
* @return object
*/
public function setCustomParams($pair)
{
if(!\is_array($pair)) return $this;
foreach($pair as $k => $v)
{
if(!\is_string($v)) continue;
$this->customParams[$k] = $v;
}
return $this;
}
/**
* @brief append custom params
*
* @param array $pair
*
* @return object
*/
public function appendCustomParams($pair)
{
if(\is_array($pair))
{
$this->customParams = \array_merge($this->customParams, $pair);
}
return $this;
}
/**
* @brief reset custom params
*
* @return object
*/
public function resetCustomParams()
{
$this->customParams = [];
return $this;
}
/**
* @brief set query string
*
* @param string|array $string
*
* @return object
*/
public function setQueryString($data = '')
{
if(\is_string($data) || \is_array($data))
{
$this->queryString = !\is_string($data) ? http_build_query($data) : $data;
}
return $this;
}
/**
* @brief get query string
*
* @return string
*/
public function getQueryString()
{
return $this->queryString;
}
/**
* @brief get custom params
*
* @return array
*/
public function getCustomParams()
{
return $this->customParams;
}
/**
* @brief get all params
*
* @return array
*/
public function getParams()
{
return \array_merge($this->customParams, $this->getDefaultParams());
}
/**
* @brief get default params
*
* @return array
*/
public function getDefaultParams()
{
return [
'GATEWAY_INTERFACE' => $this->getGatewayInterface(),
'SCRIPT_FILENAME' => $this->getScript(),
'REQUEST_METHOD' => $this->getRequestMethod(),
'REQUEST_URI' => $this->getRequestUri(),
'QUERY_STRING' => $this->getQueryString(),
'CONTENT_TYPE' => $this->getContentType(),
'CONTENT_LENGTH' => $this->getContentLength(),
'SERVER_NAME' => $this->getServerName(),
'SERVER_SOFTWARE' => $this->getServerSoftware(),
];
}
/**
* @brief set request method
*
* @param string $method
*
* @return object
*/
public function setRequestMethod($method = 'GET')
{
if(!\is_string($method) || !in_array(strtoupper($method), static::ALLOWED_REQUEST_METHODS))
{
$method = 'GET';
}
$this->requestMethod = strtoupper($method);
return $this;
}
/**
* @brief get request method
*
* @return string
*/
public function getRequestMethod()
{
return $this->requestMethod;
}
/**
* @brief get request uri
*
* @return string
*/
public function getRequestUri()
{
return $this->requestUri;
}
/**
* @brief set request uri
*
* @param string $uri
*
* @return object
*/
public function setRequestUri($uri)
{
if(\is_string($uri))
{
$this->requestUri = $uri;
}
return $this;
}
}

View File

@@ -1,233 +0,0 @@
<?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 爬山虎<blogdaren@163.com>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Workerman\Protocols\FastCGI;
class Response
{
/**
* success status
*
* @var int
*/
const STATUS_OK = 200;
/**
* invalid status
*
* @var int
*/
const STATUS_INVALID = -1;
/**
* the request id from response
*
* @var int
*/
protected $_requestId;
/**
* the stdout from response
*
* @var string
*/
protected $_stdout = '';
/**
* the stderr from response
*
* @var string
*/
protected $_stderr = '';
/**
* the origin header from response
*
* @var string
*/
protected $_header = '';
/**
* the origin body from response
*
* @var string
*/
protected $_body = '';
/**
* @brief __construct
*
* @param int $request_id
*
* @return void
*/
public function __construct($request_id = 0)
{
$this->setRequestId($request_id);
}
/**
* @brief set request id
*
* @return int
*/
public function setRequestId($id = 0)
{
$this->_requestId = (\is_int($id) && $id > 0) ? $id : -1;
return $this;
}
/**
* @brief set stdout
*
* @param string $stdout
*
* @return object
*/
public function setStdout($stdout = '')
{
if(\is_string($stdout))
{
$this->_stdout = $stdout;
}
return $this;
}
/**
* @brief get stdout
*
* @return string
*/
public function getStdout()
{
return $this->_stdout;
}
/**
* @brief set stderr
*
* @param string $stderr
*
* @return object
*/
public function setStderr($stderr = '')
{
if(\is_string($stderr))
{
$this->_stderr = $stderr;
}
return $this;
}
/**
* @brief get stderr
*
* @return void
*/
public function getStderr()
{
return $this->_stderr;
}
/**
* @brief get header
*
* @return string
*/
public function getHeader()
{
return $this->_header;
}
/**
* @brief get body
*
* @return string
*/
public function getBody()
{
return $this->_body;
}
/**
* @brief get request id
*
* @return int
*/
public function getRequestId()
{
return $this->_requestId;
}
/**
* @brief format response output
*
* @return array
*/
public function formatOutput()
{
$status = static::STATUS_INVALID;
$header = [];
$body = '';
$crlf_pos = \strpos($this->getStdout(), "\r\n\r\n");
if(false !== $crlf_pos)
{
$status = static::STATUS_OK;
$head = \substr($this->getStdout(), 0, $crlf_pos);
$body = \substr($this->getStdout(), $crlf_pos + 4);
$this->_header = \substr($this->getStdout(), 0, $crlf_pos + 4);
$this->_body = $body;
$header_lines = \explode(PHP_EOL, $head);
foreach($header_lines as $line)
{
if(preg_match('/([\w-]+):\s*(.*)$/', $line, $matches))
{
$name = \trim($matches[1]);
$value = \trim($matches[2]);
if('status' === strtolower($name))
{
$pos = strpos($value, ' ') ;
$status = false !== $pos ? \substr($value, 0, $pos) : static::STATUS_OK;
continue;
}
if(!array_key_exists($name, $header))
{
$header[$name] = $value;
continue;
}
!\is_array($header[$name]) && $header[$name] = [$header[$name]];
$header[$name][] = $value;
}
}
}
$output = [
'requestId' => $this->getRequestId(),
'status' => $status,
'stderr' => $this->getStderr(),
'header' => $header,
'body' => $body,
];
return $output;
}
}

View File

@@ -1,541 +0,0 @@
<?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 爬山虎<blogdaren@163.com>
* @protocol http://www.mit.edu/~yandros/doc/specs/fcgi-spec.html
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Workerman\Protocols;
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\FastCGI\Request;
use Workerman\Protocols\FastCGI\Response;
class Fcgi
{
/**
* the version of fcgi protocol
*
* @var int
*/
const FCGI_VERSION_1 = 1;
/**
* the fixed length of FCGI_Header: sizeof(FCGI_Header) === 8
*
* typedef struct {
* unsigned char version;
* unsigned char type;
* unsigned char requestIdB1;
* unsigned char requestIdB0;
* unsigned char contentLengthB1;
* unsigned char contentLengthB0;
* unsigned char paddingLength;
* unsigned char reserved;
* } FCGI_Header;
*
* @var int
*/
const FCGI_HEADER_LEN = 8;
/**
* the max length of payload
*
* @var int
*/
const FCGI_MAX_PAYLOAD_LEN = 65535;
/**
* the reserved bit FCGI_Header
*
* @var string
*/
const FCGI_RESERVED = '';
/**
* the padding bit FCGI_Header
*
* @var string
*/
const FCGI_PADDING = '';
/**
* the record type of FCGI_BEGIN_REQUEST
*
* @var int
*/
const FCGI_BEGIN_REQUEST = 1;
/**
* the record type of FCGI_ABORT_REQUEST
*
* @var int
*/
const FCGI_ABORT_REQUEST = 2;
/**
* the record type of FCGI_END_REQUEST
*
* @var int
*/
const FCGI_END_REQUEST = 3;
/**
* the record type of FCGI_PARAMS
*
* @var int
*/
const FCGI_PARAMS = 4;
/**
* -------------------------------------
* the pseudo record type of FCGI_PARAMS
* -------------------------------------
*
* @var int
*/
const FCGI_PARAMS_END = 4 << 3;
/**
* the record type of FCGI_STDIN
*
* @var int
*/
const FCGI_STDIN = 5;
/**
* -------------------------------------
* the pseudo record type of FCGI_STDIN
* -------------------------------------
*
* @var int
*/
const FCGI_STDIN_END = 5 << 4;
/**
* the record type of FCGI_STDOUT
*
* @var int
*/
const FCGI_STDOUT = 6;
/**
* the record type of FCGI_STDERR
*
* @var int
*/
const FCGI_STDERR = 7;
/**
* the record type of FCGI_DATA
*
* @var int
*/
const FCGI_DATA = 8;
/**
* the record type of FCGI_GET_VALUES
*
* @var int
*/
const FCGI_GET_VALUES = 9;
/**
* the record type of FCGI_GET_VALUES_RESULT
*
* @var int
*/
const FCGI_GET_VALUES_RESULT = 10;
/**
* the record type of FCGI_UNKNOWN_TYPE
*
* @var int
*/
const FCGI_UNKNOWN_TYPE = 11;
/**
* the role type of FCGI_RESPONDER
*
* @var int
*/
const FCGI_RESPONDER = 1;
/**
* the role type of FCGI_AUTHORIZER
*
* @var int
*/
const FCGI_AUTHORIZER = 2;
/**
* the role type of FCGI_FILTER
*
* @var int
*/
const FCGI_FILTER = 3;
/**
* the protocol status of FCGI_REQUEST_COMPLETE
*
* @var int
*/
const FCGI_REQUEST_COMPLETE = 0;
/**
* the protocol status of FCGI_CANT_MPX_CONN
*
* @var int
*/
const FCGI_CANT_MPX_CONN = 1;
/**
* the protocol status of FCGI_OVERLOADED
*
* @var int
*/
const FCGI_OVERLOADED = 2;
/**
* the protocol status of FCGI_UNKNOWN_ROLE
*
* @var int
*/
const FCGI_UNKNOWN_ROLE = 3;
/**
* the request object
*
* @var object
*/
static private $_request = NULL;
/**
* check the integrity of the package
*
* @param string $buffer
* @param TcpConnection $connection
*
* @return int
*/
public static function input($buffer, TcpConnection $connection)
{
$recv_len = \strlen($buffer);
if($recv_len < static::FCGI_HEADER_LEN) return 0;
if(!isset($connection->packetLength)) $connection->packetLength = 0;
$data = \unpack("Cversion/Ctype/nrequestId/ncontentLength/CpaddingLength/Creserved", $buffer);
if(false === $data) return 0;
$chunk_len = static::FCGI_HEADER_LEN + $data['contentLength'] + $data['paddingLength'];
if($recv_len < $chunk_len) return 0;
if(static::FCGI_END_REQUEST != $data['type'])
{
$connection->packetLength += $chunk_len;
$next_chunk_len = static::input(\substr($buffer, $chunk_len), $connection);
if(0 == $next_chunk_len)
{
//important!! don't forget to reset to zero byte!!
$connection->packetLength = 0;
return 0;
}
}
else
{
$connection->packetLength += $chunk_len;
}
//check package length exceeds the max package length or not
if($connection->packetLength > $connection->maxPackageSize)
{
$msg = "Exception: recv error package. package_length = {$connection->packetLength} ";
$msg .= "exceeds the limit {$connection->maxPackageSize}" . PHP_EOL;
Worker::safeEcho($msg);
$connection->close();
return 0;
}
return $connection->packetLength;
}
/**
* @brief decode package
*
* @param string $buffer
* @param TcpConnection $connection
*
* @return array
*/
public static function decode($buffer, TcpConnection $connection)
{
$offset = 0;
$stdout = $stderr = '';
do
{
$header_buffer = \substr($buffer, $offset, static::FCGI_HEADER_LEN);
$data = \unpack("Cversion/Ctype/nrequestId/ncontentLength/CpaddingLength/Creserved", $header_buffer);
//we are not going to throw new \Exception("Failed to unpack header data from the binary buffer.");
//but just break out of the loop to avoid bring much unnecessary TCP connections with TIME_WAIT status
if(false === $data)
{
$stderr = "Failed to unpack header data from the binary buffer";
Worker::safeEcho($stderr);
$connection->close();
break;
}
$chunk_len = static::FCGI_HEADER_LEN + $data['contentLength'] + $data['paddingLength'];
$body_buffer = \substr($buffer, $offset + static::FCGI_HEADER_LEN, $chunk_len - static::FCGI_HEADER_LEN);
switch($data['type'])
{
case static::FCGI_STDOUT:
$payload = \unpack("a{$data['contentLength']}contentData/a{$data['paddingLength']}paddingData", $body_buffer);
$stdout .= $payload['contentData'];
break;
case static::FCGI_STDERR:
$payload = \unpack("a{$data['contentLength']}contentData/a{$data['paddingLength']}paddingData", $body_buffer);
$stderr .= $payload['contentData'];
break;
case static::FCGI_END_REQUEST:
$payload = \unpack("NappStatus/CprotocolStatus/a3reserved", $body_buffer);
$result = static::checkProtocolStatus($payload['protocolStatus']);
if(0 <> $result['code'])
{
$stderr = $result['msg'];
Worker::safeEcho($stderr);
$connection->close();
}
break;
default:
//not support yet
$payload = '';
break;
}
$offset += $chunk_len;
}while($offset < $connection->packetLength);
//important!! don't forget to reset to zero byte!!
$connection->packetLength = 0;
//build response
$response = new Response();
$output = $response->setRequestId($data['requestId'] ?? -1)
->setStdout($stdout)
->setStderr($stderr)
->formatOutput();
//trigger user callback as onResponse
if(!empty($connection->onResponse) && is_callable($connection->onResponse))
{
try {
\call_user_func($connection->onResponse, $connection, $response);
} catch (\Exception $e) {
$msg = "Exception: onResponse: " . $e->getMessage();
Worker::safeEcho($msg);
$connection->close();
} catch (\Error $e) {
$msg = "Exception: onResponse: " . $e->getMessage();
Worker::safeEcho($msg);
$connection->close();
}
}
return $output;
}
/**
* @brief encode package
*
* @param Request $request
* @param TcpConnection $connection
*
* @return string
*/
public static function encode(Request $request, TcpConnection $connection)
{
if(!$request instanceof Request) return '';
static::$_request = $request;
$packet = '';
$packet .= static::createPacket(static::FCGI_BEGIN_REQUEST);
$packet .= static::createPacket(static::FCGI_PARAMS);
$packet .= static::createPacket(static::FCGI_PARAMS_END);
$packet .= static::createPacket(static::FCGI_STDIN);
$packet .= static::createPacket(static::FCGI_STDIN_END);
$connection->maxSendBufferSize = TcpConnection::$defaultMaxSendBufferSize * 10;
$packet_len = \strlen($packet);
if($packet_len > $connection->maxSendBufferSize)
{
$msg = "Exception: send error package. package_length = {$packet_len} ";
$msg .= "exceeds the limit {$connection->maxSendBufferSize}" . PHP_EOL;
Worker::safeEcho($msg);
$connection->close();
return '';
}
return $packet;
}
/**
* @brief pack payload
*
* @param string $type
*
* @return string
*/
static private function packPayload($type = '')
{
$payload = '';
switch($type)
{
case static::FCGI_BEGIN_REQUEST:
$payload = \pack(
"nCa5",
static::$_request->getRole(),
static::$_request->getKeepAlive(),
static::FCGI_RESERVED
);
break;
case static::FCGI_PARAMS:
case static::FCGI_PARAMS_END:
$payload = '';
$params = (static::FCGI_PARAMS == $type) ? static::$_request->getParams() : [];
foreach($params as $name => $value)
{
$name_len = \strlen($name);
$value_len = \strlen($value);
$format = [
$name_len > 127 ? 'N' : 'C',
$value_len > 127 ? 'N' : 'C',
"a{$name_len}",
"a{$value_len}",
];
$format = implode ('', $format);
$payload .= \pack(
$format,
$name_len > 127 ? ($name_len | 0x80000000) : $name_len,
$value_len > 127 ? ($value_len | 0x80000000) : $value_len,
$name,
$value
);
}
break;
case static::FCGI_STDIN:
case static::FCGI_ABORT_REQUEST:
case static::FCGI_DATA:
$payload = \pack("a" . static::$_request->getContentLength(), static::$_request->getContent());
break;
case static::FCGI_STDIN_END:
$payload = '';
break;
case static::FCGI_UNKNOWN_TYPE:
$payload = \pack("Ca7", static::FCGI_UNKNOWN_TYPE, static::FCGI_RESERVED);
break;
default:
$payload = '';
break;
}
return $payload;
}
/**
* @brief create request packet
*
* @param string $type
*
* @return string
*/
static public function createPacket($type = '')
{
$packet = '';
$offset = 0;
$payload = static::packPayload($type);
$total_len = \strlen($payload);
//don't forget to reset pseudo record type to normal
$type == static::FCGI_PARAMS_END && $type = static::FCGI_PARAMS;
$type == static::FCGI_STDIN_END && $type = static::FCGI_STDIN;
//maybe need to split payload into many chunks
do
{
$chunk = \substr($payload, $offset, static::FCGI_MAX_PAYLOAD_LEN);
$chunk_len = \strlen($chunk);
$remainder = \abs($chunk_len % 8);
$padding_len = $remainder > 0 ? 8 - $remainder : 0;
$header = \pack(
"CCnnCC",
static::FCGI_VERSION_1,
$type,
static::$_request->getRequestId(),
$chunk_len,
$padding_len,
static::FCGI_RESERVED
);
$padding = \pack("a{$padding_len}", static::FCGI_PADDING);
$packet .= $header . $chunk . $padding;
$offset += $chunk_len;
}while($offset < $total_len);
return $packet;
}
/**
* @brief check the protocol status from FCGI_END_REQUEST body
*
* @param int $status
*
* @return array
*/
static public function checkProtocolStatus($status = 0)
{
switch($status)
{
case static::FCGI_REQUEST_COMPLETE:
$msg = 'Accepted: request completed ok';
break;
case static::FCGI_CANT_MPX_CONN:
$msg = 'Rejected: FastCGI server does not support concurrent processing';
break;
case static::FCGI_OVERLOADED:
$msg = 'Rejected: FastCGI server run out of resources or reached the limit';
break;
case static::FCGI_UNKNOWN_ROLE:
$msg = 'Rejected: FastCGI server not support the specified role';
break;
default:
$msg = 'Rejected: FastCGI server does not know what happened';
break;
}
return [
'code' => $status,
'msg' => $msg,
];
}
}

View File

@@ -94,60 +94,58 @@ class Http
*/
public static function input($recv_buffer, TcpConnection $connection)
{
static $input = array();
static $input = [];
if (!isset($recv_buffer[512]) && isset($input[$recv_buffer])) {
return $input[$recv_buffer];
}
$crlf_pos = \strpos($recv_buffer, "\r\n\r\n");
if (false === $crlf_pos) {
// Judge whether the package length exceeds the limit.
if ($recv_len = \strlen($recv_buffer) >= 16384) {
if (\strlen($recv_buffer) >= 16384) {
$connection->close("HTTP/1.1 413 Request Entity Too Large\r\n\r\n", true);
return 0;
}
return 0;
}
$head_len = $crlf_pos + 4;
$length = $crlf_pos + 4;
$method = \strstr($recv_buffer, ' ', true);
if ($method === 'GET' || $method === 'OPTIONS' || $method === 'HEAD' || $method === 'DELETE') {
if (!isset($recv_buffer[512])) {
$input[$recv_buffer] = $head_len;
if (\count($input) > 512) {
unset($input[key($input)]);
}
}
return $head_len;
} else if ($method !== 'POST' && $method !== 'PUT' && $method !== 'PATCH') {
if (!\in_array($method, ['GET', 'POST', 'OPTIONS', 'HEAD', 'DELETE', 'PUT', 'PATCH'])) {
$connection->close("HTTP/1.1 400 Bad Request\r\n\r\n", true);
return 0;
}
$header = \substr($recv_buffer, 0, $crlf_pos);
$length = false;
if ($pos = \strpos($header, "\r\nContent-Length: ")) {
$length = $head_len + (int)\substr($header, $pos + 18, 10);
$length = $length + (int)\substr($header, $pos + 18, 10);
$has_content_length = true;
} else if (\preg_match("/\r\ncontent-length: ?(\d+)/i", $header, $match)) {
$length = $head_len + $match[1];
$length = $length + $match[1];
$has_content_length = true;
} else {
$has_content_length = false;
if (false !== stripos($header, "\r\nTransfer-Encoding:")) {
$connection->close("HTTP/1.1 400 Bad Request\r\n\r\n", true);
return 0;
}
}
if ($length !== false) {
if (!isset($recv_buffer[512])) {
$input[$recv_buffer] = $length;
if (\count($input) > 512) {
unset($input[key($input)]);
}
}
if ($has_content_length) {
if ($length > $connection->maxPackageSize) {
$connection->close("HTTP/1.1 413 Request Entity Too Large\r\n\r\n", true);
return 0;
}
return $length;
}
$connection->close("HTTP/1.1 400 Bad Request\r\n\r\n", true);
return 0;
if (!isset($recv_buffer[512])) {
$input[$recv_buffer] = $length;
if (\count($input) > 512) {
unset($input[key($input)]);
}
}
return $length;
}
/**
@@ -221,6 +219,7 @@ class Http
$file = $response->file['file'];
$offset = $response->file['offset'];
$length = $response->file['length'];
clearstatcache();
$file_size = (int)\filesize($file);
$body_len = $length > 0 ? $length : $file_size - $offset;
$response->withHeaders(array(

View File

@@ -520,6 +520,9 @@ class Request
{
$file = [];
$boundary = "\r\n$boundary";
if (\strlen($this->_buffer) < $section_start_offset) {
return 0;
}
$section_end_offset = \strpos($this->_buffer, $boundary, $section_start_offset);
if (!$section_end_offset) {
return 0;
@@ -596,7 +599,7 @@ class Request
*/
protected static function createSessionId()
{
return \bin2hex(\pack('d', \microtime(true)) . \pack('N', \mt_rand()));
return \bin2hex(\pack('d', \microtime(true)) . random_bytes(8));
}
/**

View File

@@ -410,7 +410,7 @@ class Session
public function __destruct()
{
$this->save();
if (\rand(1, static::$gcProbability[1]) <= static::$gcProbability[0]) {
if (\random_int(1, static::$gcProbability[1]) <= static::$gcProbability[0]) {
$this->gc();
}
}

View File

@@ -87,7 +87,7 @@ class FileSessionHandler implements SessionHandlerInterface
*/
public function write($session_id, $session_data)
{
$temp_file = static::$_sessionSavePath.uniqid(mt_rand(), true);
$temp_file = static::$_sessionSavePath . uniqid(bin2hex(random_bytes(8)), true);
if (!\file_put_contents($temp_file, $session_data)) {
return false;
}

View File

@@ -355,7 +355,7 @@ class Ws
$port = $connection->getRemotePort();
$host = $port === 80 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port;
// Handshake header.
$connection->websocketSecKey = \base64_encode(\md5(\mt_rand(), true));
$connection->websocketSecKey = \base64_encode(random_bytes(16));
$user_header = isset($connection->headers) ? $connection->headers :
(isset($connection->wsHttpHeader) ? $connection->wsHttpHeader : null);
$user_header_str = '';

View File

@@ -26,6 +26,7 @@ use \Exception;
* Worker class
* A container for listening ports
*/
#[\AllowDynamicProperties]
class Worker
{
/**
@@ -33,7 +34,7 @@ class Worker
*
* @var string
*/
const VERSION = '4.0.42';
const VERSION = '4.1.4';
/**
* Status starting.
@@ -183,7 +184,7 @@ class Worker
public $onBufferDrain = null;
/**
* Emitted when worker processes stoped.
* Emitted when worker processes stopped.
*
* @var callable
*/
@@ -548,11 +549,13 @@ class Worker
{
static::checkSapiEnv();
static::init();
static::lock();
static::parseCommand();
static::daemonize();
static::initWorkers();
static::installSignal();
static::saveMasterPid();
static::lock(\LOCK_UN);
static::displayUI();
static::forkWorkers();
static::resetStd();
@@ -629,24 +632,25 @@ class Worker
*
* @return void
*/
protected static function lock()
protected static function lock($flag = \LOCK_EX)
{
$fd = \fopen(static::$_startFile, 'r');
if ($fd && !flock($fd, LOCK_EX)) {
static::log('Workerman['.static::$_startFile.'] already running.');
exit;
static $fd;
if (\DIRECTORY_SEPARATOR !== '/') {
return;
}
$lock_file = static::$pidFile . '.lock';
$fd = $fd ?: \fopen($lock_file, 'a+');
if ($fd) {
flock($fd, $flag);
if ($flag === \LOCK_UN) {
fclose($fd);
$fd = null;
clearstatcache();
if (\is_file($lock_file)) {
unlink($lock_file);
}
}
}
}
/**
* Unlock.
*
* @return void
*/
protected static function unlock()
{
$fd = \fopen(static::$_startFile, 'r');
$fd && flock($fd, \LOCK_UN);
}
/**
@@ -1693,11 +1697,9 @@ class Worker
// onWorkerExit
if ($worker->onWorkerExit) {
try {
call_user_func($worker->onWorkerExit, $worker, $status, $pid);
} catch (\Exception $e) {
static::log("worker[{$worker->name}] onWorkerExit $e");
} catch (\Error $e) {
static::log("worker[{$worker->name}] onWorkerExit $e");
($worker->onWorkerExit)($worker, $status, $pid);
} catch (\Throwable $exception) {
static::log("worker[{$worker->name}] onWorkerExit $exception");
}
}

View File

@@ -24,7 +24,7 @@
"source": "https://github.com/walkor/workerman"
},
"require": {
"php": ">=5.4"
"php": ">=7.0"
},
"suggest": {
"ext-event": "For better performance. "