diff --git a/src/JsonSchema/Uri/Retrievers/Curl.php b/src/JsonSchema/Uri/Retrievers/Curl.php index 311f1d34..6387e253 100644 --- a/src/JsonSchema/Uri/Retrievers/Curl.php +++ b/src/JsonSchema/Uri/Retrievers/Curl.php @@ -76,7 +76,7 @@ private function fetchMessageBody($response) */ protected function fetchContentType($response) { - if (0 < preg_match("/Content-Type:(\V*)/ims", $response, $match)) { + if (0 < preg_match('/^Content-Type:([^;\v]*)/im', $response, $match)) { $this->contentType = trim($match[1]); return true; diff --git a/src/JsonSchema/Uri/Retrievers/FileGetContents.php b/src/JsonSchema/Uri/Retrievers/FileGetContents.php index f6056693..84598c3e 100644 --- a/src/JsonSchema/Uri/Retrievers/FileGetContents.php +++ b/src/JsonSchema/Uri/Retrievers/FileGetContents.php @@ -97,7 +97,7 @@ private function fetchContentType(array $headers): bool */ protected static function getContentTypeMatchInHeader($header) { - if (0 < preg_match("/Content-Type:(\V*)/ims", $header, $match)) { + if (0 < preg_match('/^Content-Type:([^;\v]*)/im', $header, $match)) { return trim($match[1]); } diff --git a/tests/Uri/Retrievers/CurlTest.php b/tests/Uri/Retrievers/CurlTest.php index c2c88067..d47cace6 100644 --- a/tests/Uri/Retrievers/CurlTest.php +++ b/tests/Uri/Retrievers/CurlTest.php @@ -34,6 +34,34 @@ public function testNoContentType(): void self::assertStringEqualsFileCanonicalizing(realpath(__DIR__ . '/../../fixtures/foobar.json'), $result); } + + /** + * @dataProvider contentTypeProvider + */ + public function testFetchContentType(string $response, ?string $expected, bool $matches): void + { + $c = new Curl(); + + $reflector = new \ReflectionObject($c); + $fetchContentType = $reflector->getMethod('fetchContentType'); + if (PHP_VERSION_ID < 80100) { + $fetchContentType->setAccessible(true); + } + + $this->assertSame($matches, $fetchContentType->invoke($c, $response)); + $this->assertSame($expected, $c->getContentType()); + } + + public function contentTypeProvider(): array + { + return [ + 'json without parameters' => ["Content-Type: application/json\r\n\r\n{}", 'application/json', true], + 'json with charset' => ["Content-Type: application/json; charset=utf-8\r\n\r\n{}", 'application/json', true], + 'schema media type with charset' => ["Content-Type: application/schema+json; charset=utf-8\r\n\r\n{}", 'application/schema+json', true], + 'multiple parameters' => ["Content-Type: application/json; charset=utf-8; profile=schema\r\n\r\n{}", 'application/json', true], + 'X-Content-Type is not a content type' => ["HTTP/1.1 200 OK\r\nX-Content-Type: text/plain\r\n\r\n{}", null, false], + ]; + } } } diff --git a/tests/Uri/Retrievers/FileGetContentsTest.php b/tests/Uri/Retrievers/FileGetContentsTest.php index 70effd7c..88d60aa2 100644 --- a/tests/Uri/Retrievers/FileGetContentsTest.php +++ b/tests/Uri/Retrievers/FileGetContentsTest.php @@ -25,7 +25,10 @@ public function testFetchFile(): void $this->assertNotEmpty($result); } - public function testContentType(): void + /** + * @dataProvider contentTypeProvider + */ + public function testFetchContentType(string $header, ?string $expected, bool $matches): void { $res = new FileGetContents(); @@ -35,8 +38,20 @@ public function testContentType(): void $fetchContentType->setAccessible(true); } - $this->assertTrue($fetchContentType->invoke($res, ['Content-Type: application/json'])); - $this->assertFalse($fetchContentType->invoke($res, ['X-Some-Header: whateverValue'])); + $this->assertSame($matches, $fetchContentType->invoke($res, [$header])); + $this->assertSame($expected, $res->getContentType()); + } + + public function contentTypeProvider(): array + { + return [ + 'json without parameters' => ['Content-Type: application/json', 'application/json', true], + 'json with charset' => ['Content-Type: application/json; charset=utf-8', 'application/json', true], + 'schema media type with charset' => ['Content-Type: application/schema+json; charset=utf-8', 'application/schema+json', true], + 'multiple parameters' => ['Content-Type: application/json; charset=utf-8; profile=schema', 'application/json', true], + 'non-content-type header' => ['X-Some-Header: whateverValue', null, false], + 'X-Content-Type is not a content type' => ['X-Content-Type: text/plain', null, false], + ]; } public function testCanHandleHttp301PermanentRedirect(): void