From 648227ae624cee26fa7bad3db4d698eecfb7167a Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Fri, 26 Apr 2024 13:01:33 +0300 Subject: [PATCH 1/4] 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 { From 3f1570b7dfddf3e8213e3ca8109ab18a5206f05c Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Fri, 26 Apr 2024 13:07:15 +0300 Subject: [PATCH 2/4] fix: add casting to array --- src/HttpWorker.php | 2 +- tests/Unit/HttpWorkerTest.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/HttpWorker.php b/src/HttpWorker.php index 81e0cf4..8fa0e95 100644 --- a/src/HttpWorker.php +++ b/src/HttpWorker.php @@ -252,7 +252,7 @@ private function arrayToHeaderValue(array $headers = []): array * @var array $value */ foreach ($headers as $key => $value) { - $value = \array_filter($value, static fn (mixed $v): bool => \is_string($v)); + $value = \array_filter((array) $value, static fn (mixed $v): bool => \is_string($v)); if ($value !== []) { $result[$key] = new HeaderValue(['value' => $value]); } diff --git a/tests/Unit/HttpWorkerTest.php b/tests/Unit/HttpWorkerTest.php index 419c3bc..65b3f81 100644 --- a/tests/Unit/HttpWorkerTest.php +++ b/tests/Unit/HttpWorkerTest.php @@ -304,6 +304,10 @@ public static function headersDataProvider(): \Traversable 'X-Test2' => new HeaderValue(['value' => ['foo']]), ] ]; + yield [ + ['Content-Type' => 'application/x-www-form-urlencoded'], + ['Content-Type' => new HeaderValue(['value' => ['application/x-www-form-urlencoded']])] + ]; } private static function createProtoRequest(array $values): \RoadRunner\HTTP\DTO\V1\Request From 2cb4e6d715ee0e6ce2582f30c43bc1599389bf96 Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Fri, 26 Apr 2024 14:10:24 +0300 Subject: [PATCH 3/4] fix: improve casting to array --- src/HttpWorker.php | 2 +- tests/Unit/HttpWorkerTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/HttpWorker.php b/src/HttpWorker.php index 8fa0e95..6c1a5a2 100644 --- a/src/HttpWorker.php +++ b/src/HttpWorker.php @@ -252,7 +252,7 @@ private function arrayToHeaderValue(array $headers = []): array * @var array $value */ foreach ($headers as $key => $value) { - $value = \array_filter((array) $value, static fn (mixed $v): bool => \is_string($v)); + $value = \array_filter(\is_array($value) ? $value : [$value], static fn (mixed $v): bool => \is_string($v)); if ($value !== []) { $result[$key] = new HeaderValue(['value' => $value]); } diff --git a/tests/Unit/HttpWorkerTest.php b/tests/Unit/HttpWorkerTest.php index 65b3f81..6a094c2 100644 --- a/tests/Unit/HttpWorkerTest.php +++ b/tests/Unit/HttpWorkerTest.php @@ -308,6 +308,7 @@ public static function headersDataProvider(): \Traversable ['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 From c4adb0ccdb4d48f766d3b57e7e6eb417902c5854 Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Fri, 26 Apr 2024 14:14:22 +0300 Subject: [PATCH 4/4] fix: Psalm issue --- src/HttpWorker.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HttpWorker.php b/src/HttpWorker.php index 6c1a5a2..e82c1bb 100644 --- a/src/HttpWorker.php +++ b/src/HttpWorker.php @@ -252,6 +252,7 @@ private function arrayToHeaderValue(array $headers = []): array * @var array $value */ foreach ($headers as $key => $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]);