Skip to content

Commit c3fd8f9

Browse files
committed
修复:Workerman HTTP 保持长连接遵循 RFC 标准
1 parent a974625 commit c3fd8f9

File tree

4 files changed

+110
-8
lines changed

4 files changed

+110
-8
lines changed

src/Components/workerman/src/Http/Message/WorkermanResponse.php

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Imi\Server\Http\Message\Response;
88
use Imi\Util\Http\Consts\MediaType;
9+
use Imi\Util\Http\Consts\ResponseHeader;
910
use Imi\Util\Http\Consts\StatusCode;
1011
use Workerman\Connection\TcpConnection;
1112
use Workerman\Worker;
@@ -17,6 +18,11 @@ class WorkermanResponse extends Response
1718
*/
1819
protected ?\Workerman\Protocols\Http\Response $workermanResponse = null;
1920

21+
/**
22+
* Workerman 的 http 请求对象
23+
*/
24+
protected ?WorkermanRequest $request = null;
25+
2026
/**
2127
* Workerman 的 Worker 对象
2228
*/
@@ -37,11 +43,12 @@ class WorkermanResponse extends Response
3743
*/
3844
protected bool $isBodyWritable = true;
3945

40-
public function __construct(Worker $worker, TcpConnection $connection, ?\Workerman\Protocols\Http\Response $response = null)
46+
public function __construct(Worker $worker, TcpConnection $connection, ?\Workerman\Protocols\Http\Response $response = null, ?WorkermanRequest $request = null)
4147
{
4248
$this->workermanResponse = $response;
4349
$this->worker = $worker;
4450
$this->connection = $connection;
51+
$this->request = $request;
4552
parent::__construct();
4653
}
4754

@@ -107,9 +114,18 @@ public function send(): self
107114
if ($this->isBodyWritable())
108115
{
109116
$this->isBodyWritable = false;
110-
$response = $this->workermanResponse;
111-
$response->withBody((string) $this->getBody());
112-
$this->connection->send($response);
117+
if ($this->shouldKeepAlive())
118+
{
119+
$this->connection->send($this->workermanResponse->withBody((string) $this->getBody()));
120+
}
121+
else
122+
{
123+
$this->connection->close($this->workermanResponse->withBody((string) $this->getBody()));
124+
}
125+
}
126+
elseif (!$this->shouldKeepAlive())
127+
{
128+
$this->connection->close();
113129
}
114130

115131
return $this;
@@ -145,9 +161,18 @@ public function sendFile(string $filename, ?string $contentType = null, ?string
145161
if ($this->isBodyWritable())
146162
{
147163
$this->isBodyWritable = false;
148-
$response = $this->workermanResponse;
149-
$response->withFile($filename, $offset, $length);
150-
$this->connection->send($response);
164+
if ($this->shouldKeepAlive())
165+
{
166+
$this->connection->send($this->workermanResponse->withFile($filename, $offset, $length));
167+
}
168+
else
169+
{
170+
$this->connection->close($this->workermanResponse->withFile($filename, $offset, $length));
171+
}
172+
}
173+
elseif (!$this->shouldKeepAlive())
174+
{
175+
$this->connection->close();
151176
}
152177

153178
return $this;
@@ -176,4 +201,25 @@ public function getConnection(): TcpConnection
176201
{
177202
return $this->connection;
178203
}
204+
205+
protected function shouldKeepAlive(): bool
206+
{
207+
if (0 === strcasecmp($this->getHeaderLine(ResponseHeader::CONNECTION), 'close'))
208+
{
209+
return false;
210+
}
211+
if ($request = $this->request)
212+
{
213+
if ($request->getProtocolVersion() >= 1.1)
214+
{
215+
return 0 !== strcasecmp($request->getHeaderLine('connection'), 'close');
216+
}
217+
else
218+
{
219+
return 0 === strcasecmp($request->getHeaderLine('connection'), 'keep-alive');
220+
}
221+
}
222+
223+
return true;
224+
}
179225
}

src/Components/workerman/src/Server/Http/Server.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ protected function bindEvents(): void
6767
// @phpstan-ignore-next-line
6868
$request = new WorkermanRequest($worker, $connection, $data);
6969
// @phpstan-ignore-next-line
70-
$response = new WorkermanResponse($worker, $connection, new Response());
70+
$response = new WorkermanResponse($worker, $connection, new Response(), $request);
7171
RequestContext::muiltiSet([
7272
'server' => $this,
7373
'request' => $request,

src/Components/workerman/tests/unit/AppServer/ApiServer/Controller/IndexController.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Imi\Util\Http\Consts\StatusCode;
1717
use Imi\Util\Http\MessageUtil;
1818
use Imi\Util\Stream\MemoryStream;
19+
use Imi\Workerman\Http\Message\WorkermanRequest;
1920

2021
/**
2122
* @Controller(prefix="/")
@@ -355,4 +356,17 @@ public function duplicated1(): void
355356
public function duplicated2(): void
356357
{
357358
}
359+
360+
/**
361+
* @Action
362+
*/
363+
public function connectionId(): array
364+
{
365+
/** @var WorkermanRequest $request */
366+
$request = RequestContext::get('request');
367+
368+
return [
369+
'id' => $request->getConnection()->id,
370+
];
371+
}
358372
}

src/Components/workerman/tests/unit/AppServer/Tests/Http/RequestTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Imi\Util\Http\Consts\MediaType;
88
use Yurun\Util\HttpRequest;
9+
use Yurun\Util\YurunHttp\Http\Psr7\Consts\RequestHeader;
910
use Yurun\Util\YurunHttp\Http\Psr7\UploadedFile;
1011

1112
/**
@@ -255,4 +256,45 @@ public function testMoreUrlParams(): void
255256
'page' => 666,
256257
]), $response->body());
257258
}
259+
260+
public function testKeepAlive(): void
261+
{
262+
// HTTP/1.0 Close
263+
$http = new HttpRequest();
264+
$http->protocolVersion = '1.0';
265+
$data1 = $http->get($this->host . 'connectionId')->json(true)['id'] ?? null;
266+
$data2 = $http->get($this->host . 'connectionId')->json(true)['id'] ?? null;
267+
$this->assertNotNull($data1);
268+
$this->assertNotNull($data2);
269+
$this->assertNotEquals($data1, $data2);
270+
271+
// HTTP/1.0 Keep-Alive
272+
$http = new HttpRequest();
273+
$http->protocolVersion = '1.0';
274+
$http->header(RequestHeader::CONNECTION, 'keep-alive');
275+
$data1 = $http->get($this->host . 'connectionId')->json(true)['id'] ?? null;
276+
$data2 = $http->get($this->host . 'connectionId')->json(true)['id'] ?? null;
277+
$this->assertNotNull($data1);
278+
$this->assertNotNull($data2);
279+
$this->assertEquals($data1, $data2);
280+
281+
// HTTP/1.1 Close
282+
$http = new HttpRequest();
283+
$http->protocolVersion = '1.1';
284+
$http->header(RequestHeader::CONNECTION, 'close');
285+
$data1 = $http->get($this->host . 'connectionId')->json(true)['id'] ?? null;
286+
$data2 = $http->get($this->host . 'connectionId')->json(true)['id'] ?? null;
287+
$this->assertNotNull($data1);
288+
$this->assertNotNull($data2);
289+
$this->assertNotEquals($data1, $data2);
290+
291+
// HTTP/1.1 Keep-Alive
292+
$http = new HttpRequest();
293+
$http->protocolVersion = '1.1';
294+
$data1 = $http->get($this->host . 'connectionId')->json(true)['id'] ?? null;
295+
$data2 = $http->get($this->host . 'connectionId')->json(true)['id'] ?? null;
296+
$this->assertNotNull($data1);
297+
$this->assertNotNull($data2);
298+
$this->assertEquals($data1, $data2);
299+
}
258300
}

0 commit comments

Comments
 (0)