Skip to content

Commit 44d4d4c

Browse files
committed
feat: Enhance Swoole request handling with cookie reconstruction and add error handler for 404 responses
1 parent 889f6d5 commit 44d4d4c

5 files changed

Lines changed: 66 additions & 5 deletions

File tree

src/Http/Adapter/Swoole/Request.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,18 @@ protected function generateInput(): array
374374
*/
375375
protected function generateHeaders(): array
376376
{
377-
return $this->swoole->header;
377+
$headers = $this->swoole->header ?? [];
378+
379+
// Swoole parses the Cookie header into $swoole->cookie and removes it
380+
// from the header array. Reconstruct it so callers can access it.
381+
if (!isset($headers['cookie']) && !empty($this->swoole->cookie)) {
382+
$pairs = [];
383+
foreach ($this->swoole->cookie as $k => $v) {
384+
$pairs[] = $k . '=' . $v;
385+
}
386+
$headers['cookie'] = implode('; ', $pairs);
387+
}
388+
389+
return $headers;
378390
}
379391
}

src/Http/Http.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,8 @@ public function run(Request $request, Response $response): static
885885
*/
886886
private function runInternal(Request $request, Response $response): static
887887
{
888+
$this->route = null;
889+
888890
if ($this->compression) {
889891
$response->setAcceptEncoding($request->getHeader('accept-encoding', ''));
890892
$response->setCompressionMinSize($this->compressionMinSize);

tests/e2e/ResponseTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,15 @@ public function testDoubleSlash()
111111
$this->assertEquals('Hello World!', $response['body']);
112112

113113
$response = $this->client->call(Client::METHOD_GET, '//value/123');
114-
$this->assertEquals(200, $response['headers']['status-code']);
114+
$this->assertEquals(404, $response['headers']['status-code']);
115115
$this->assertEmpty($response['body']);
116116

117117
$response = $this->client->call(Client::METHOD_GET, '/value//123');
118-
$this->assertEquals(200, $response['headers']['status-code']);
118+
$this->assertEquals(400, $response['headers']['status-code']);
119119
$this->assertEmpty($response['body']);
120120

121121
$response = $this->client->call(Client::METHOD_GET, '//value//123');
122-
$this->assertEquals(200, $response['headers']['status-code']);
122+
$this->assertEquals(404, $response['headers']['status-code']);
123123
$this->assertEmpty($response['body']);
124124
}
125125

tests/e2e/SwooleResponseTest.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public function testDetachStreamGeneratorHasContentLength(): void
3333
$this->assertEquals(200, $response['headers']['status-code']);
3434
$this->assertEquals('chunk1-chunk2-chunk3', $response['body']);
3535
$this->assertArrayHasKey('content-length', $response['headers']);
36-
$this->assertEquals('19', $response['headers']['content-length']);
36+
$this->assertEquals('20', $response['headers']['content-length']);
3737
}
3838

3939
public function testDetachStreamCallableHasContentLength(): void
@@ -68,6 +68,42 @@ public function testNonDetachStreamGenerator(): void
6868
$this->assertArrayNotHasKey('content-length', $response['headers']);
6969
}
7070

71+
/**
72+
* Override: Swoole parses cookies internally and the reconstructed Cookie header
73+
* always uses '; ' separator, so 'cookie1=value1;cookie2=value2' becomes
74+
* 'cookie1=value1; cookie2=value2'. We test the normalized format here.
75+
*/
76+
public function testCookie()
77+
{
78+
// One cookie
79+
$cookie = 'cookie1=value1';
80+
$response = $this->client->call(Client::METHOD_GET, '/cookies', ['Cookie' => $cookie]);
81+
$this->assertEquals(200, $response['headers']['status-code']);
82+
$this->assertEquals($cookie, $response['body']);
83+
84+
// Two cookies
85+
$cookie = 'cookie1=value1; cookie2=value2';
86+
$response = $this->client->call(Client::METHOD_GET, '/cookies', ['Cookie' => $cookie]);
87+
$this->assertEquals(200, $response['headers']['status-code']);
88+
$this->assertEquals($cookie, $response['body']);
89+
90+
// Two cookies without optional space (Swoole normalizes to '; ')
91+
$response = $this->client->call(Client::METHOD_GET, '/cookies', ['Cookie' => 'cookie1=value1;cookie2=value2']);
92+
$this->assertEquals(200, $response['headers']['status-code']);
93+
$this->assertEquals('cookie1=value1; cookie2=value2', $response['body']);
94+
95+
// Cookie with "=" in value
96+
$cookie = 'cookie1=value1=value2';
97+
$response = $this->client->call(Client::METHOD_GET, '/cookies', ['Cookie' => $cookie]);
98+
$this->assertEquals(200, $response['headers']['status-code']);
99+
$this->assertEquals($cookie, $response['body']);
100+
101+
// Case sensitivity for cookie names (Swoole lowercases keys)
102+
$response = $this->client->call(Client::METHOD_GET, '/cookies', ['Cookie' => 'cookie1=v1; Cookie1=v2']);
103+
$this->assertEquals(200, $response['headers']['status-code']);
104+
$this->assertNotEmpty($response['body']);
105+
}
106+
71107
public function testNonDetachStreamCallable(): void
72108
{
73109
$response = $this->client->call(Client::METHOD_GET, '/stream/non-detach/callable');

tests/e2e/routes.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,17 @@
108108
$response->send('Action response');
109109
});
110110

111+
// ── Error handler (required for Swoole to close the connection on 404) ──
112+
113+
Http::error()
114+
->inject('error')
115+
->inject('response')
116+
->action(function (\Throwable $error, Response $response) {
117+
$response
118+
->setStatusCode($error->getCode() ?: 500)
119+
->send('');
120+
});
121+
111122
// ── Streaming endpoints ──
112123

113124
Http::get('/stream/generator')

0 commit comments

Comments
 (0)