Skip to content

Commit 213cd0d

Browse files
authored
fix: add header value validation (#26)
1 parent f5c329b commit 213cd0d

File tree

2 files changed

+54
-1
lines changed

2 files changed

+54
-1
lines changed

src/HttpWorker.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,11 @@ private function arrayToHeaderValue(array $headers = []): array
252252
* @var array<array-key, string> $value
253253
*/
254254
foreach ($headers as $key => $value) {
255-
$result[$key] = new HeaderValue(['value' => $value]);
255+
/** @psalm-suppress DocblockTypeContradiction */
256+
$value = \array_filter(\is_array($value) ? $value : [$value], static fn (mixed $v): bool => \is_string($v));
257+
if ($value !== []) {
258+
$result[$key] = new HeaderValue(['value' => $value]);
259+
}
256260
}
257261

258262
return $result;

tests/Unit/HttpWorkerTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,22 @@ public function testRespondWithProtoCodec(): void
119119
$worker->respond(200, 'foo', ['Content-Type' => ['application/x-www-form-urlencoded']]);
120120
}
121121

122+
#[DataProvider('headersDataProvider')]
123+
public function testRespondWithProtoCodecWithHeaders(array $headers, array $expected): void
124+
{
125+
$expectedHeader = new Response(['status' => 200, 'headers' => $expected]);
126+
127+
$worker = $this->createMock(WorkerInterface::class);
128+
$worker->expects($this->once())
129+
->method('respond')
130+
->with(new Payload('foo', $expectedHeader->serializeToString()), Frame::CODEC_PROTO);
131+
132+
(new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(Frame::CODEC_PROTO);
133+
$worker = new HttpWorker($worker);
134+
135+
$worker->respond(200, 'foo', $headers);
136+
}
137+
122138
public function testRespondWithJsonCodec(): void
123139
{
124140
$worker = $this->createMock(WorkerInterface::class);
@@ -262,6 +278,39 @@ public static function emptyRequestDataProvider(): \Traversable
262278
yield [new Payload(null, null)];
263279
}
264280

281+
public static function headersDataProvider(): \Traversable
282+
{
283+
yield [
284+
['Content-Type' => ['application/x-www-form-urlencoded']],
285+
['Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']])]
286+
];
287+
yield [
288+
['Content-Type' => ['application/x-www-form-urlencoded'], 'X-Test' => ['foo', 'bar']],
289+
[
290+
'Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']]),
291+
'X-Test' => new HeaderValue(['value' => ['foo', 'bar']]),
292+
]
293+
];
294+
yield [['Content-Type' => [null]], []];
295+
yield [['Content-Type' => [1]], []];
296+
yield [['Content-Type' => [true]], []];
297+
yield [['Content-Type' => [false]], []];
298+
yield [['Content-Type' => [new \stdClass()]], []];
299+
yield [['Content-Type' => [1.5]], []];
300+
yield [
301+
['X-Test' => ['foo', 'bar'], 'X-Test2' => ['foo', null], 'X-Test3' => [null, 1]],
302+
[
303+
'X-Test' => new HeaderValue(['value' => ['foo', 'bar']]),
304+
'X-Test2' => new HeaderValue(['value' => ['foo']]),
305+
]
306+
];
307+
yield [
308+
['Content-Type' => 'application/x-www-form-urlencoded'],
309+
['Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']])]
310+
];
311+
yield [['Content-Type' => new \stdClass()], []];
312+
}
313+
265314
private static function createProtoRequest(array $values): \RoadRunner\HTTP\DTO\V1\Request
266315
{
267316
$toHeaderValue = static function (string $key, bool $wrap = true) use (&$values): void {

0 commit comments

Comments
 (0)