Files
kiri-core/HttpServer/Client/Http2.php
T

349 lines
7.4 KiB
PHP
Raw Normal View History

2020-09-11 19:37:32 +08:00
<?php
2020-10-29 18:17:25 +08:00
declare(strict_types=1);
2020-09-11 19:37:32 +08:00
namespace HttpServer\Client;
use Exception;
2020-11-01 05:10:38 +08:00
use HttpServer\Http\Context;
2020-09-11 19:37:32 +08:00
use Snowflake\Abstracts\Component;
2021-04-28 19:16:34 +08:00
use Snowflake\Channel;
2020-12-24 11:12:23 +08:00
use Snowflake\Core\Json;
2021-03-31 11:05:21 +08:00
use Snowflake\Core\Xml;
2021-04-28 19:29:07 +08:00
use Snowflake\Event;
2021-03-31 01:33:41 +08:00
use Snowflake\Snowflake;
2020-11-14 04:46:52 +08:00
use Swoole\Coroutine\Http2\Client as H2Client;
2021-04-28 19:05:15 +08:00
use Swoole\Http2\Request;
2021-03-31 11:03:30 +08:00
use Swoole\Http2\Response;
2020-09-11 19:37:32 +08:00
/**
* Class Http2
* @package HttpServer\Client
*/
class Http2 extends Component
{
2021-03-31 01:33:41 +08:00
2021-04-28 19:16:34 +08:00
private array $_clients = [];
private Channel $channel;
/**
* @throws Exception
*/
public function init()
{
$this->channel = Snowflake::getApp('channel');
2021-04-28 19:29:07 +08:00
Event::on(Event::SYSTEM_RESOURCE_RELEASES, [$this, 'releases']);
Event::on(Event::SYSTEM_RESOURCE_CLEAN, [$this, 'clean']);
}
/**
* @throws Exception
*/
public function releases()
{
foreach ($this->_clients as $name => $client) {
/** @var H2Client $client */
$client->close();
$this->channel->push($client, 'http2.' . $name);
}
$this->_clients = [];
}
/**
* 清空
*/
public function clean()
{
foreach ($this->_clients as $client) {
/** @var H2Client $client */
$client->close();
}
$this->_clients = [];
2021-04-28 19:16:34 +08:00
}
2021-04-01 11:34:47 +08:00
/**
* @param bool $isRecv
* @return Http2
*/
public function setIsRecv(bool $isRecv): static
{
Context::setContext('http2isRecv', $isRecv);
return $this;
}
2021-04-01 11:43:24 +08:00
/**
* @param int $timeout
* @return Http2
*/
public function setTimeout(int $timeout): static
{
Context::setContext('http2timeout', $timeout);
return $this;
}
2021-03-31 01:33:41 +08:00
/**
* @param array $headers
2021-04-01 11:34:47 +08:00
* @return Http2
2021-03-31 01:33:41 +08:00
*/
2021-04-01 11:34:47 +08:00
public function setHeader(array $headers): static
2021-03-31 01:33:41 +08:00
{
Context::setContext('http2Headers', $headers);
2021-04-01 11:34:47 +08:00
return $this;
2021-03-31 01:33:41 +08:00
}
2020-09-11 19:37:32 +08:00
/**
* @param $domain
* @param $path
* @param array $params
* @param int $timeout
2020-12-17 14:09:14 +08:00
* @return Result
2020-09-11 19:37:32 +08:00
* @throws Exception
*/
2020-12-17 14:09:14 +08:00
public function get($domain, $path, $params = [], $timeout = -1): Result
2020-09-11 19:37:32 +08:00
{
2021-03-31 01:33:41 +08:00
$request = $this->dispatch($domain, $path, 'GET', $params, $timeout);
return new Result(['code' => 0, 'data' => $request]);
2020-09-11 19:37:32 +08:00
}
/**
* @param $domain
* @param $path
* @param array $params
* @param int $timeout
2020-12-17 14:09:14 +08:00
* @return Result
2020-09-11 19:37:32 +08:00
* @throws Exception
*/
2020-12-17 14:09:14 +08:00
public function post($domain, $path, $params = [], $timeout = -1): Result
2020-09-11 19:37:32 +08:00
{
2021-03-31 01:33:41 +08:00
$request = $this->dispatch($domain, $path, 'POST', $params, $timeout);
return new Result(['code' => 0, 'data' => $request]);
}
/**
* @param $domain
* @param $path
* @param array $params
* @param int $timeout
* @return Result
* @throws Exception
*/
public function upload($domain, $path, $params = [], $timeout = -1): Result
{
$request = $this->dispatch($domain, $path, 'POST', $params, $timeout, true);
return new Result(['code' => 0, 'data' => $request]);
2020-09-11 19:37:32 +08:00
}
2020-09-12 02:53:27 +08:00
/**
* @param $domain
* @param $path
* @param array $params
* @param int $timeout
2020-12-17 14:09:14 +08:00
* @return Result
2020-09-12 02:53:27 +08:00
* @throws Exception
*/
2020-12-17 14:09:14 +08:00
public function delete($domain, $path, $params = [], $timeout = -1): Result
2020-09-12 02:53:27 +08:00
{
2021-03-31 01:33:41 +08:00
$request = $this->dispatch($domain, $path, 'DELETE', $params, $timeout);
return new Result(['code' => 0, 'data' => $request]);
}
/**
* @param $domain
* @param $path
* @param $method
* @param array $params
* @param int $timeout
* @param bool $isUpload
* @return mixed
* @throws Exception
*/
private function dispatch($domain, $path, $method, $params = [], $timeout = -1, $isUpload = false): mixed
{
2021-04-28 19:16:34 +08:00
[$domain, $isSsl] = $this->clear($domain);
2021-03-31 01:33:41 +08:00
2021-04-28 19:05:15 +08:00
$request = $this->getRequest($path, $method, $params, $isUpload);
$request->headers = array_merge($request->headers, [
'Host' => $domain
]);
2021-04-28 19:16:34 +08:00
defer(function () use ($domain, $path, $request, $method) {
$this->channel->push($request, 'request.' . $method . $path);
});
return $this->doRequest($request, $domain, $isSsl, $timeout);
}
2021-03-31 01:33:41 +08:00
2021-04-28 19:16:34 +08:00
/**
* @param $domain
* @return array
*/
private function clear($domain): array
{
if (str_starts_with($domain, 'https://')) {
return [str_replace('https://', '', $domain), true];
} else {
return [str_replace('http://', '', $domain), false];
}
}
/**
* @param Request $request
* @param $domain
* @param $ssl
* @param $timeout
* @return mixed
* @throws Exception
*/
private function doRequest(Request $request, $domain, $ssl, $timeout): mixed
{
$client = $this->getClient($domain, $ssl, $timeout);
defer(function () use ($client, $domain) {
$this->channel->push($client, 'http2.' . $domain);
2021-03-31 01:33:41 +08:00
});
2021-04-28 19:16:34 +08:00
$client->send($request);
2021-04-01 11:34:47 +08:00
if (Context::getContext('http2isRecv') === false) {
return null;
}
return $this->recv($client);
}
2021-03-31 11:03:30 +08:00
2021-04-01 11:34:47 +08:00
/**
* @param $client
* @return mixed
* @throws Exception
*/
private function recv($client): mixed
{
2021-03-31 11:03:30 +08:00
/** @var Response $response */
2021-04-01 11:43:24 +08:00
if (!Context::hasContext('http2timeout')) {
$response = $client->recv();
} else {
$response = $client->recv((int)Context::getContext('http2timeout'));
}
2021-03-31 11:28:03 +08:00
if ($response === false || $response->statusCode > 200) {
2021-03-31 14:12:07 +08:00
throw new Exception($client->errMsg, $client->errCode);
2021-03-31 11:03:30 +08:00
}
2021-03-31 11:05:21 +08:00
$header = $response->headers['content-type'];
if (str_starts_with($header, 'application/json;')) {
return Json::decode($response->data);
} else if (str_starts_with($header, 'application/xml;')) {
return Xml::toArray($response->data);
} else {
return $response->data;
}
2020-09-12 02:53:27 +08:00
}
/**
* @param $domain
* @param $path
* @param array $params
* @param int $timeout
* @return mixed
* @throws Exception
*/
2020-12-17 14:09:14 +08:00
public function put($domain, $path, $params = [], $timeout = -1): Result
2020-09-12 02:53:27 +08:00
{
2021-03-31 01:33:41 +08:00
$request = $this->dispatch($domain, $path, 'PUT', $params, $timeout);
return new Result(['code' => 0, 'data' => $request]);
2020-09-12 02:53:27 +08:00
}
2020-09-11 19:37:32 +08:00
/**
* @param $path
* @param $method
* @param $params
2021-03-31 01:33:41 +08:00
* @param bool $isUpload
2020-09-11 19:37:32 +08:00
* @return Request
2021-03-31 11:01:50 +08:00
* @throws Exception
2020-09-11 19:37:32 +08:00
*/
2021-04-28 19:05:15 +08:00
public function getRequest($path, $method, $params, $isUpload = false): Request
2020-09-11 19:37:32 +08:00
{
2021-03-31 10:51:40 +08:00
if (!str_starts_with($path, '/')) {
$path = '/' . $path;
}
2021-03-31 01:33:41 +08:00
$channel = Snowflake::app()->getChannel();
$request = $channel->pop('request.' . $method . $path, function () use ($path, $method) {
$request = new Request();
$request->method = $method;
$request->path = $path;
return $request;
});
if ($method === 'GET') {
$request->path .= '?' . http_build_query($params);
2020-09-11 19:37:32 +08:00
} else {
2021-03-31 01:33:41 +08:00
$request->data = !is_string($params) && !$isUpload ? Json::encode($params) : $params;
}
2021-03-31 11:30:23 +08:00
$request->headers = Context::getContext('http2Headers');
2021-03-31 01:33:41 +08:00
return $request;
2020-09-11 19:37:32 +08:00
}
/**
* @param $domain
2021-03-31 11:01:50 +08:00
* @param bool $isSsl
2020-09-11 19:37:32 +08:00
* @param int $timeout
* @return H2Client
2021-03-31 11:13:23 +08:00
* @throws Exception
2020-09-11 19:37:32 +08:00
*/
2021-03-31 11:01:50 +08:00
private function getClient($domain, $isSsl = false, $timeout = -1): H2Client
2020-09-11 19:37:32 +08:00
{
2021-04-28 19:16:34 +08:00
if (isset($this->_clients[$domain])) {
return $this->_clients[$domain];
}
2021-03-31 01:33:41 +08:00
$pool = Snowflake::app()->getChannel();
/** @var H2Client $client */
2021-03-31 11:01:50 +08:00
$client = $pool->pop('http2.' . $domain, function () use ($domain, $isSsl, $timeout) {
2021-04-28 19:29:07 +08:00
return $this->newRequest($domain, $isSsl, $timeout);
2021-03-31 01:33:41 +08:00
});
2021-04-28 19:16:34 +08:00
if ((!$client->connected || !$client->ping()) && !$client->connect()) {
2021-03-31 11:15:34 +08:00
throw new Exception($client->errMsg, $client->errCode);
2020-09-11 19:37:32 +08:00
}
2021-04-28 19:16:34 +08:00
return $this->_clients[$domain] = $client;
2020-09-11 19:37:32 +08:00
}
2021-04-28 19:29:07 +08:00
/**
* @param $domain
* @param $isSsl
* @param $timeout
* @return H2Client
*/
public function newRequest($domain, $isSsl, $timeout): H2Client
{
$domain = rtrim($domain, '/');
if (str_contains($domain, ':')) {
[$domain, $port] = explode(':', $domain);
} else {
$port = $isSsl === true ? 443 : 80;
}
$client = new H2Client($domain, (int)$port, $isSsl);
$client->set(['timeout' => $timeout, 'ssl_host_name' => $domain]);
return $client;
}
2020-09-11 19:37:32 +08:00
}