You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
289 lines
8.1 KiB
289 lines
8.1 KiB
<?php
|
|
|
|
/**
|
|
* Slim Framework (https://slimframework.com)
|
|
*
|
|
* @license https://github.com/slimphp/Slim-Psr7/blob/master/LICENSE.md (MIT License)
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Slim\Tests\Psr7;
|
|
|
|
use InvalidArgumentException;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Slim\Psr7\Headers;
|
|
use stdClass;
|
|
|
|
use function base64_encode;
|
|
|
|
class HeadersTest extends TestCase
|
|
{
|
|
public function testCreateFromGlobals()
|
|
{
|
|
$GLOBALS['getallheaders_return'] = [
|
|
'HTTP_ACCEPT' => 'application/json',
|
|
];
|
|
|
|
$headers = Headers::createFromGlobals();
|
|
|
|
unset($GLOBALS['getallheaders_return']);
|
|
|
|
$this->assertEquals(['accept' => ['application/json']], $headers->getHeaders());
|
|
$this->assertEquals(['ACCEPT' => ['application/json']], $headers->getHeaders(true));
|
|
}
|
|
|
|
public function testCreateFromGlobalsUsesEmptyArrayIfGetAllHeadersReturnsFalse()
|
|
{
|
|
$GLOBALS['getallheaders_return'] = false;
|
|
|
|
$headers = Headers::createFromGlobals();
|
|
|
|
unset($GLOBALS['getallheaders_return']);
|
|
|
|
$this->assertEquals([], $headers->getHeaders());
|
|
}
|
|
|
|
public function testAddHeader()
|
|
{
|
|
$headers = new Headers([
|
|
'Accept' => 'application/json',
|
|
]);
|
|
|
|
$headers->addHeader('Accept', 'text/html');
|
|
|
|
$this->assertEquals(['application/json', 'text/html'], $headers->getHeader('Accept'));
|
|
$this->assertEquals(['accept' => ['application/json', 'text/html']], $headers->getHeaders());
|
|
$this->assertEquals(['Accept' => ['application/json', 'text/html']], $headers->getHeaders(true));
|
|
}
|
|
|
|
public function testAddHeaderValueEmptyArray()
|
|
{
|
|
$this->expectException(InvalidArgumentException::class);
|
|
|
|
$headers = new Headers();
|
|
$headers->addHeader('Header', []);
|
|
}
|
|
|
|
public function testRemoveHeader()
|
|
{
|
|
$headers = new Headers([
|
|
'Accept' => 'application/json',
|
|
]);
|
|
|
|
$headers->removeHeader('Accept');
|
|
|
|
$this->assertEquals([], $headers->getHeader('Accept'));
|
|
$this->assertEquals([], $headers->getHeaders());
|
|
}
|
|
|
|
/**
|
|
* @doesNotPerformAssertions
|
|
*/
|
|
public function testRemoveHeaderByIncompatibleStringWithRFC()
|
|
{
|
|
$headers = new Headers();
|
|
$headers->removeHeader('<incompatible with RFC>');
|
|
}
|
|
|
|
public function testGetHeader()
|
|
{
|
|
$headers = new Headers([
|
|
'Accept' => ['application/json', 'text/html'],
|
|
]);
|
|
|
|
$this->assertEquals(['application/json', 'text/html'], $headers->getHeader('accept'));
|
|
$this->assertEquals(['application/json', 'text/html'], $headers->getHeader('Accept'));
|
|
$this->assertEquals(['application/json', 'text/html'], $headers->getHeader('HTTP_ACCEPT'));
|
|
}
|
|
|
|
public function testGetHeaderReturnsValidatedAndTrimedHeaderDefaultValue()
|
|
{
|
|
$headers = new Headers([]);
|
|
|
|
$this->assertEquals(['application/json'], $headers->getHeader('accept', ' application/json'));
|
|
}
|
|
|
|
public function testGetHeaderThrowsExceptionWithInvalidDefaultArgument()
|
|
{
|
|
$this->expectException(InvalidArgumentException::class);
|
|
|
|
$headers = new Headers([]);
|
|
|
|
$headers->getHeader('accept', new stdClass());
|
|
}
|
|
|
|
public function testSetHeader()
|
|
{
|
|
$headers = new Headers([
|
|
'Content-Length' => 0,
|
|
]);
|
|
|
|
$headers->setHeader('Content-Length', 100);
|
|
|
|
$this->assertSame(['100'], $headers->getHeader('Content-Length'));
|
|
$this->assertEquals(['content-length' => ['100']], $headers->getHeaders());
|
|
$this->assertEquals(['Content-Length' => ['100']], $headers->getHeaders(true));
|
|
}
|
|
|
|
public function testSetHeaderPreservesOriginalCaseIfHeaderAlreadyExists()
|
|
{
|
|
$headers = new Headers([
|
|
'CONTENT-LENGTH' => 0,
|
|
]);
|
|
|
|
$headers->setHeader('Content-Length', 100);
|
|
|
|
$this->assertEquals(['content-length' => ['100']], $headers->getHeaders());
|
|
$this->assertEquals(['CONTENT-LENGTH' => ['100']], $headers->getHeaders(true));
|
|
}
|
|
|
|
public function testSetHeaders()
|
|
{
|
|
$headers = new Headers([
|
|
'Content-Length' => 0,
|
|
]);
|
|
|
|
$headers->setHeaders([
|
|
'Accept' => 'application/json',
|
|
]);
|
|
|
|
$this->assertEquals(['accept' => ['application/json']], $headers->getHeaders());
|
|
$this->assertEquals(['Accept' => ['application/json']], $headers->getHeaders(true));
|
|
}
|
|
|
|
public function testHasHeader()
|
|
{
|
|
$headers = new Headers([
|
|
'Accept' => 'application/json',
|
|
]);
|
|
|
|
$this->assertTrue($headers->hasHeader('accept'));
|
|
$this->assertTrue($headers->hasHeader('Accept'));
|
|
$this->assertTrue($headers->hasHeader('HTTP_ACCEPT'));
|
|
}
|
|
|
|
public function testGetHeaders()
|
|
{
|
|
$headers = new Headers([
|
|
'HTTP_ACCEPT' => 'text/html',
|
|
'HTTP_CONTENT_TYPE' => 'application/json',
|
|
]);
|
|
|
|
$expectedNormalizedHeaders = [
|
|
'accept' => ['text/html'],
|
|
'content-type' => ['application/json'],
|
|
];
|
|
|
|
$this->assertEquals($expectedNormalizedHeaders, $headers->getHeaders());
|
|
}
|
|
|
|
public function testGetHeadersPreservesOriginalCase()
|
|
{
|
|
$headers = new Headers([
|
|
'HTTP_ACCEPT' => 'text/html',
|
|
'HTTP_CONTENT_TYPE' => 'application/json',
|
|
]);
|
|
|
|
$expectedOriginalHeaders = [
|
|
'ACCEPT' => ['text/html'],
|
|
'CONTENT-TYPE' => ['application/json'],
|
|
];
|
|
|
|
$this->assertEquals($expectedOriginalHeaders, $headers->getHeaders(true));
|
|
}
|
|
|
|
public function testParseAuthorizationHeader()
|
|
{
|
|
$expectedValue = 'Basic ' . base64_encode('user:password');
|
|
|
|
$headers = new Headers(['Authorization' => $expectedValue]);
|
|
$this->assertEquals([$expectedValue], $headers->getHeader('Authorization'));
|
|
|
|
$headers = new Headers([], ['REDIRECT_HTTP_AUTHORIZATION' => 'cookie']);
|
|
$this->assertEquals(['cookie'], $headers->getHeader('Authorization'));
|
|
|
|
$headers = new Headers([], ['PHP_AUTH_USER' => 'user', 'PHP_AUTH_PW' => 'password']);
|
|
$this->assertEquals([$expectedValue], $headers->getHeader('Authorization'));
|
|
|
|
$headers = new Headers([], ['PHP_AUTH_DIGEST' => 'digest']);
|
|
$this->assertEquals(['digest'], $headers->getHeader('Authorization'));
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideInvalidHeaderNames
|
|
*/
|
|
public function testWithInvalidHeaderName($headerName): void
|
|
{
|
|
$headers = new Headers();
|
|
|
|
$this->expectException(\InvalidArgumentException::class);
|
|
|
|
$headers->setHeader($headerName, 'foo');
|
|
}
|
|
|
|
public static function provideInvalidHeaderNames(): array
|
|
{
|
|
return [
|
|
[[]],
|
|
[false],
|
|
[new \stdClass()],
|
|
["Content-Type\r\n\r\n"],
|
|
["Content-Type\r\n"],
|
|
["Content-Type\n"],
|
|
["\r\nContent-Type"],
|
|
["\nContent-Type"],
|
|
["\n"],
|
|
["\r\n"],
|
|
["\t"],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideInvalidHeaderValues
|
|
*/
|
|
public function testSetInvalidHeaderValue($headerValue)
|
|
{
|
|
$headers = new Headers();
|
|
|
|
$this->expectException(\InvalidArgumentException::class);
|
|
|
|
$headers->setHeader('Content-Type', $headerValue);
|
|
}
|
|
|
|
public static function provideInvalidHeaderValues(): array
|
|
{
|
|
// Explicit tests for newlines as the most common exploit vector.
|
|
$tests = [
|
|
["new\nline"],
|
|
["new\r\nline"],
|
|
["new\rline"],
|
|
["new\r\n line"],
|
|
["newline\n"],
|
|
["\nnewline"],
|
|
["newline\r\n"],
|
|
["\n\rnewline"],
|
|
];
|
|
|
|
for ($i = 0; $i <= 0xff; $i++) {
|
|
if (\chr($i) == "\t") {
|
|
continue;
|
|
}
|
|
if (\chr($i) == " ") {
|
|
continue;
|
|
}
|
|
if ($i >= 0x21 && $i <= 0x7e) {
|
|
continue;
|
|
}
|
|
if ($i >= 0x80) {
|
|
continue;
|
|
}
|
|
|
|
$tests[] = ["foo" . \chr($i) . "bar"];
|
|
$tests[] = ["foo" . \chr($i)];
|
|
}
|
|
|
|
return $tests;
|
|
}
|
|
}
|