From ce15d2e79740061e49d39d4d9d6890358aaeab7c Mon Sep 17 00:00:00 2001 From: Yurun Date: Mon, 2 Dec 2019 11:20:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=E5=90=91?= =?UTF-8?q?=20Http2=20StreamId=20=E5=86=99=E5=85=A5=E5=A4=9A=E6=AC=A1?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=B8=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/YurunHttp/Attributes.php | 5 ++ src/YurunHttp/Handler/Swoole.php | 8 +--- src/YurunHttp/Http2/IHttp2Client.php | 23 ++++++++- src/YurunHttp/Http2/SwooleClient.php | 29 +++++++++++- tests/unit/Http2/SwooleHttp2Test.php | 71 ++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 9 deletions(-) diff --git a/src/YurunHttp/Attributes.php b/src/YurunHttp/Attributes.php index 71a8178..eaa5f88 100644 --- a/src/YurunHttp/Attributes.php +++ b/src/YurunHttp/Attributes.php @@ -163,6 +163,11 @@ abstract class Attributes */ const HTTP2_NOT_RECV = 'http2_not_recv'; + /** + * 启用 Http2 pipeline + */ + const HTTP2_PIPELINE = 'http2_pipeline'; + /** * 重定向计数 */ diff --git a/src/YurunHttp/Handler/Swoole.php b/src/YurunHttp/Handler/Swoole.php index 441fb03..306a3bc 100644 --- a/src/YurunHttp/Handler/Swoole.php +++ b/src/YurunHttp/Handler/Swoole.php @@ -149,6 +149,7 @@ public function buildRequest($request, $connection, &$http2Request) if($isHttp2) { $http2Request->headers = $headers; + $http2Request->pipeline = $request->getAttribute(Attributes::HTTP2_PIPELINE, false); } else { @@ -311,12 +312,7 @@ private function parseCookies(&$request, $connection, $http2Request) $cookies = $this->cookieManager->getRequestCookies($request->getUri()); if($http2Request) { - $cookie = []; - foreach($cookies as $name => $value) - { - $cookie[] = $name . '=' . urlencode($value); - } - $request = $request->withHeader('cookie', implode(',', $cookie)); + $http2Request->cookies = $cookies; } else { diff --git a/src/YurunHttp/Http2/IHttp2Client.php b/src/YurunHttp/Http2/IHttp2Client.php index 38f81ff..364c609 100644 --- a/src/YurunHttp/Http2/IHttp2Client.php +++ b/src/YurunHttp/Http2/IHttp2Client.php @@ -37,10 +37,29 @@ public function close(); * 成功返回streamId,失败返回false * * @param \Yurun\Util\YurunHttp\Http\Request $request - * @param bool $dropRecvResponse + * @param bool $pipeline 默认send方法在发送请求之后,会结束当前的Http2 Stream,启用PIPELINE后,底层会保持stream流,可以多次调用write方法,向服务器发送数据帧,请参考write方法。 + * @param bool $dropRecvResponse 丢弃接收到的响应数据 * @return int|bool */ - public function send($request, $dropRecvResponse = false); + public function send($request, $pipeline = false, $dropRecvResponse = false); + + /** + * 向一个流写入数据帧 + * + * @param int $streamId + * @param string $data + * @param boolean $end 是否关闭流 + * @return bool + */ + public function write($streamId, $data, $end = false); + + /** + * 关闭一个流 + * + * @param int $streamId + * @return bool + */ + public function end($streamId); /** * 接收数据 diff --git a/src/YurunHttp/Http2/SwooleClient.php b/src/YurunHttp/Http2/SwooleClient.php index 2d71159..3c2065f 100644 --- a/src/YurunHttp/Http2/SwooleClient.php +++ b/src/YurunHttp/Http2/SwooleClient.php @@ -3,6 +3,7 @@ use Swoole\Coroutine; use Swoole\Coroutine\Channel; +use Yurun\Util\YurunHttp\Attributes; use Yurun\Util\YurunHttp\Http\Psr7\Uri; class SwooleClient implements IHttp2Client @@ -128,10 +129,11 @@ public function close() * 成功返回streamId,失败返回false * * @param \Yurun\Util\YurunHttp\Http\Request $request + * @param bool $pipeline 默认send方法在发送请求之后,会结束当前的Http2 Stream,启用PIPELINE后,底层会保持stream流,可以多次调用write方法,向服务器发送数据帧,请参考write方法。 * @param bool $dropRecvResponse 丢弃接收到的响应数据 * @return int|bool */ - public function send($request, $dropRecvResponse = false) + public function send($request, $pipeline = false, $dropRecvResponse = false) { if('2.0' !== $request->getProtocolVersion()) { @@ -142,6 +144,7 @@ public function send($request, $dropRecvResponse = false) { throw new \RuntimeException(sprintf('Current http2 connection instance just support %s://%s:%s, does not support %s', $this->ssl ? 'https' : 'http', $this->host, $this->port, $uri->__toString())); } + $request = $request->withAttribute(Attributes::HTTP2_PIPELINE, $pipeline); $this->handler->buildRequest($request, $this->http2Client, $http2Request); $streamId = $this->http2Client->send($http2Request); if(!$streamId) @@ -155,6 +158,30 @@ public function send($request, $dropRecvResponse = false) return $streamId; } + /** + * 向一个流写入数据帧 + * + * @param int $streamId + * @param string $data + * @param boolean $end 是否关闭流 + * @return bool + */ + public function write($streamId, $data, $end = false) + { + return $this->http2Client->write($streamId, $data, $end); + } + + /** + * 关闭一个流 + * + * @param int $streamId + * @return bool + */ + public function end($streamId) + { + return $this->http2Client->write($streamId, '', true); + } + /** * 接收数据 * diff --git a/tests/unit/Http2/SwooleHttp2Test.php b/tests/unit/Http2/SwooleHttp2Test.php index 133d131..588234a 100644 --- a/tests/unit/Http2/SwooleHttp2Test.php +++ b/tests/unit/Http2/SwooleHttp2Test.php @@ -98,4 +98,75 @@ public function testMuiltCo() }); } + public function testPipeline1() + { + $this->call(function(){ + $uri = new Uri($this->http2Host); + $client = new SwooleClient($uri->getHost(), Uri::getServerPort($uri), 'https' === $uri->getScheme()); + + $this->assertTrue($client->connect()); + + $http = new HttpRequest; + $http->protocolVersion = '2.0'; + $http->timeout = 3000; + + $date = strtotime('2017-03-24 17:12:14'); + $data = json_encode([ + 'date' => $date, + ]); + + $request = $http->buildRequest($this->http2Host, substr($data, 0, 2)); + $streamId = $client->send($request, true); + $this->assertGreaterThan(0, $streamId); + $this->assertTrue($client->write($streamId, substr($data, 2), true)); + // $this->assertTrue($client->end($streamId)); + + $response = $client->recv($streamId); + $data = $response->json(true); + + $this->assertEquals($date, isset($data['date']) ? $data['date'] : null); + $this->assertGreaterThan(1, isset($data['fd']) ? $data['fd'] : null); + $this->assertEquals('yurun', $response->getHeaderLine('trailer')); + // Swoole 4.4.12 BUG,暂时无法获取 + // $this->assertEquals('niubi', $response->getHeaderLine('yurun')); + $client->close(); + }); + } + + public function testPipeline2() + { + $this->call(function(){ + $uri = new Uri($this->http2Host); + $client = new SwooleClient($uri->getHost(), Uri::getServerPort($uri), 'https' === $uri->getScheme()); + + $this->assertTrue($client->connect()); + + $http = new HttpRequest; + $http->protocolVersion = '2.0'; + $http->timeout = 3000; + + $date = strtotime('2017-03-24 17:12:14'); + $data = json_encode([ + 'date' => $date, + ]); + + $request = $http->buildRequest($this->http2Host, substr($data, 0, 2)); + $streamId = $client->send($request, true); + $this->assertGreaterThan(0, $streamId); + $this->assertTrue($client->write($streamId, substr($data, 2))); + $this->assertTrue($client->end($streamId)); + + $response = $client->recv($streamId); + $data = $response->json(true); + + $this->assertEquals($date, isset($data['date']) ? $data['date'] : null); + $this->assertGreaterThan(1, isset($data['fd']) ? $data['fd'] : null); + $this->assertEquals('yurun', $response->getHeaderLine('trailer')); + // Swoole 4.4.12 BUG,暂时无法获取 + // $this->assertEquals('niubi', $response->getHeaderLine('yurun')); + + $client->close(); + }); + } + } \ No newline at end of file