Skip to content

Commit

Permalink
Merge pull request #9 from clue/mime-types
Browse files Browse the repository at this point in the history
Use correct MIME types as for common file extensions
  • Loading branch information
clue authored Feb 14, 2021
2 parents ab7fd26 + 06caaba commit 6253ad5
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 14 deletions.
41 changes: 38 additions & 3 deletions src/FilesystemHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,41 @@ class FilesystemHandler
{
private $root;

/**
* Mapping between file extension and MIME type to send in `Content-Type` response header
*
* @var array<string,string>
*/
private $mimetypes = array(
'atom' => 'application/atom+xml',
'bz2' => 'application/x-bzip2',
'css' => 'text/css',
'gif' => 'image/gif',
'gz' => 'application/gzip',
'htm' => 'text/html',
'html' => 'text/html',
'ico' => 'image/x-icon',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'js' => 'text/javascript',
'json' => 'application/json',
'pdf' => 'application/pdf',
'png' => 'image/png',
'rss' => 'application/rss+xml',
'svg' => 'image/svg+xml',
'tar' => 'application/x-tar',
'xml' => 'application/xml',
'zip' => 'application/zip',
);

/**
* Assign default MIME type to send in `Content-Type` response header (same as nginx/Apache)
*
* @var string
* @see self::$mimetypes
*/
private $defaultMimetype = 'text/plain';

public function __construct(string $root)
{
$this->root = $root;
Expand Down Expand Up @@ -67,11 +102,11 @@ public function __invoke(ServerRequestInterface $request)
);
}

// Assign default MIME type here (same as nginx/Apache).
// Should use mime database in the future with fallback to given default.
// Assign MIME type based on file extension (same as nginx/Apache) or fall back to given default otherwise.
// Browers are pretty good at figuring out the correct type if no charset attribute is given.
$ext = \strtolower(\substr($path, \strrpos($path, '.') + 1));
$headers = [
'Content-Type' => 'text/plain'
'Content-Type' => $this->mimetypes[$ext] ?? $this->defaultMimetype
];

$stat = @\stat($path);
Expand Down
18 changes: 17 additions & 1 deletion tests/FilesystemHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,23 @@

class FilesystemHandlerTest extends TestCase
{
public function testInvokeWithValidPathToLicenseWillReturnResponseWithFileContents()
public function testInvokeWithValidPathToComposerJsonWillReturnResponseWithFileContentsAndContentType()
{
$handler = new FilesystemHandler(dirname(__DIR__));

$request = new ServerRequest('GET', '/source/composer.json');
$request = $request->withAttribute('path', 'composer.json');

$response = $handler($request);

/** @var ResponseInterface $response */
$this->assertInstanceOf(ResponseInterface::class, $response);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals('application/json', $response->getHeaderLine('Content-Type'));
$this->assertEquals(file_get_contents(__DIR__ . '/../composer.json'), (string) $response->getBody());
}

public function testInvokeWithValidPathToLicenseWillReturnResponseWithFileContentsAndDefaultContentType()
{
$handler = new FilesystemHandler(dirname(__DIR__));

Expand Down
21 changes: 11 additions & 10 deletions tests/acceptance.sh
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,16 @@ out=$(curl -v $base/users 2>&1); match "HTTP/.* 404"
out=$(curl -v $base/users/ 2>&1); match "HTTP/.* 404"
out=$(curl -v $base/users/a/b 2>&1); match "HTTP/.* 404"

out=$(curl -v $base/LICENSE 2>&1); match "HTTP/.* 200" && match -iP "Content-Type: text/plain[\r\n]"
out=$(curl -v $base/source 2>&1); match -i "Location: /source/" && match -iP "Content-Type: text/html[\r\n]"
out=$(curl -v $base/source/ 2>&1); match "HTTP/.* 200"
out=$(curl -v $base/source/LICENSE 2>&1); match "HTTP/.* 200" && match -iP "Content-Type: text/plain[\r\n]"
out=$(curl -v $base/source/LICENSE/ 2>&1); match -i "Location: ../LICENSE"
out=$(curl -v $base/source/LICENSE// 2>&1); match "HTTP/.* 404"
out=$(curl -v $base/source//LICENSE 2>&1); match "HTTP/.* 404"
out=$(curl -v $base/source/tests 2>&1); match -i "Location: tests/"
out=$(curl -v $base/source/invalid 2>&1); match "HTTP/.* 404"
out=$(curl -v $base/source/bin%00ary 2>&1); match "HTTP/.* 40[40]" # expects 404, but not processed with nginx (400) and Apache (404)
out=$(curl -v $base/LICENSE 2>&1); match "HTTP/.* 200" && match -iP "Content-Type: text/plain[\r\n]"
out=$(curl -v $base/source 2>&1); match -i "Location: /source/" && match -iP "Content-Type: text/html[\r\n]"
out=$(curl -v $base/source/ 2>&1); match "HTTP/.* 200"
out=$(curl -v $base/source/composer.json 2>&1); match "HTTP/.* 200" && match -iP "Content-Type: application/json[\r\n]"
out=$(curl -v $base/source/LICENSE 2>&1); match "HTTP/.* 200" && match -iP "Content-Type: text/plain[\r\n]"
out=$(curl -v $base/source/LICENSE/ 2>&1); match -i "Location: ../LICENSE"
out=$(curl -v $base/source/LICENSE// 2>&1); match "HTTP/.* 404"
out=$(curl -v $base/source//LICENSE 2>&1); match "HTTP/.* 404"
out=$(curl -v $base/source/tests 2>&1); match -i "Location: tests/"
out=$(curl -v $base/source/invalid 2>&1); match "HTTP/.* 404"
out=$(curl -v $base/source/bin%00ary 2>&1); match "HTTP/.* 40[40]" # expects 404, but not processed with nginx (400) and Apache (404)

echo "OK ($n)"

0 comments on commit 6253ad5

Please sign in to comment.