From 213cd0d5c0fba1548f22a5f5ff333afa88fe24ae Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Fri, 26 Apr 2024 14:16:10 +0300 Subject: [PATCH] fix: add header value validation (#26) --- src/HttpWorker.php | 6 ++++- tests/Unit/HttpWorkerTest.php | 49 +++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/HttpWorker.php b/src/HttpWorker.php index 924e381..e82c1bb 100644 --- a/src/HttpWorker.php +++ b/src/HttpWorker.php @@ -252,7 +252,11 @@ private function arrayToHeaderValue(array $headers = []): array * @var array $value */ foreach ($headers as $key => $value) { - $result[$key] = new HeaderValue(['value' => $value]); + /** @psalm-suppress DocblockTypeContradiction */ + $value = \array_filter(\is_array($value) ? $value : [$value], static fn (mixed $v): bool => \is_string($v)); + if ($value !== []) { + $result[$key] = new HeaderValue(['value' => $value]); + } } return $result; diff --git a/tests/Unit/HttpWorkerTest.php b/tests/Unit/HttpWorkerTest.php index 90b63f2..6a094c2 100644 --- a/tests/Unit/HttpWorkerTest.php +++ b/tests/Unit/HttpWorkerTest.php @@ -119,6 +119,22 @@ public function testRespondWithProtoCodec(): void $worker->respond(200, 'foo', ['Content-Type' => ['application/x-www-form-urlencoded']]); } + #[DataProvider('headersDataProvider')] + public function testRespondWithProtoCodecWithHeaders(array $headers, array $expected): void + { + $expectedHeader = new Response(['status' => 200, 'headers' => $expected]); + + $worker = $this->createMock(WorkerInterface::class); + $worker->expects($this->once()) + ->method('respond') + ->with(new Payload('foo', $expectedHeader->serializeToString()), Frame::CODEC_PROTO); + + (new \ReflectionProperty(HttpWorker::class, 'codec'))->setValue(Frame::CODEC_PROTO); + $worker = new HttpWorker($worker); + + $worker->respond(200, 'foo', $headers); + } + public function testRespondWithJsonCodec(): void { $worker = $this->createMock(WorkerInterface::class); @@ -262,6 +278,39 @@ public static function emptyRequestDataProvider(): \Traversable yield [new Payload(null, null)]; } + public static function headersDataProvider(): \Traversable + { + yield [ + ['Content-Type' => ['application/x-www-form-urlencoded']], + ['Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']])] + ]; + yield [ + ['Content-Type' => ['application/x-www-form-urlencoded'], 'X-Test' => ['foo', 'bar']], + [ + 'Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']]), + 'X-Test' => new HeaderValue(['value' => ['foo', 'bar']]), + ] + ]; + yield [['Content-Type' => [null]], []]; + yield [['Content-Type' => [1]], []]; + yield [['Content-Type' => [true]], []]; + yield [['Content-Type' => [false]], []]; + yield [['Content-Type' => [new \stdClass()]], []]; + yield [['Content-Type' => [1.5]], []]; + yield [ + ['X-Test' => ['foo', 'bar'], 'X-Test2' => ['foo', null], 'X-Test3' => [null, 1]], + [ + 'X-Test' => new HeaderValue(['value' => ['foo', 'bar']]), + 'X-Test2' => new HeaderValue(['value' => ['foo']]), + ] + ]; + yield [ + ['Content-Type' => 'application/x-www-form-urlencoded'], + ['Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']])] + ]; + yield [['Content-Type' => new \stdClass()], []]; + } + private static function createProtoRequest(array $values): \RoadRunner\HTTP\DTO\V1\Request { $toHeaderValue = static function (string $key, bool $wrap = true) use (&$values): void {