From 648227ae624cee26fa7bad3db4d698eecfb7167a Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Fri, 26 Apr 2024 13:01:33 +0300 Subject: [PATCH] fix: add header value validation --- src/HttpWorker.php | 5 +++- tests/Unit/HttpWorkerTest.php | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/HttpWorker.php b/src/HttpWorker.php index 924e381..81e0cf4 100644 --- a/src/HttpWorker.php +++ b/src/HttpWorker.php @@ -252,7 +252,10 @@ private function arrayToHeaderValue(array $headers = []): array * @var array $value */ foreach ($headers as $key => $value) { - $result[$key] = new HeaderValue(['value' => $value]); + $value = \array_filter($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..419c3bc 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,34 @@ 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']]), + ] + ]; + } + private static function createProtoRequest(array $values): \RoadRunner\HTTP\DTO\V1\Request { $toHeaderValue = static function (string $key, bool $wrap = true) use (&$values): void {