wiki.techinc.nl/tests/phpunit/unit/includes/Rest/ConditionalHeaderUtilTest.php
daniel 39b721667a REST: Allow weak ETags to match strong ETags.
This changes handling of the If-Match header so a weak ETag coming from
the client may match a strong ETag generated by the handler.

This is needed because ETags may get "weakened" on the way to the client
by proxies and middleware, e.g. Varnish applying compression, see
<https://varnish-cache.org/docs/6.0/users-guide/compression.html>.

This behavior is technically a violation of RFC 7232 section 3.1, but it
should actually be safe: A client sending a weak ETag is still ok with
getting a strong match, and a handler that generates a strong ETag knows
that it can rely on strong semantics holding, even if the tag sent by
the client is weak.

Bug: T310710
Bug: T238849
Change-Id: I2a604f2f46719a5f74ddac1b1fa58c8a36910240
2022-06-27 12:52:42 +00:00

268 lines
5.3 KiB
PHP

<?php
namespace MediaWiki\Tests\Rest;
use MediaWiki\Rest\ConditionalHeaderUtil;
use MediaWiki\Rest\RequestData;
use MediaWiki\Rest\Response;
/**
* @covers \MediaWiki\Rest\ConditionalHeaderUtil
*/
class ConditionalHeaderUtilTest extends \MediaWikiUnitTestCase {
public static function provider() {
return [
'nothing' => [
'GET',
null,
null,
null,
[],
null,
[]
],
'inm true' => [
'GET',
'"a"',
null,
null,
[ 'If-None-Match' => '"b"' ],
null,
[ 'ETag' => [ '"a"' ] ]
],
'inm false' => [
'GET',
'"a"',
null,
null,
[ 'If-None-Match' => '"a"' ],
304,
[ 'ETag' => [ '"a"' ] ]
],
'ims true' => [
'GET',
null,
'Mon, 14 Oct 2019 00:00:01 GMT',
null,
[ 'If-Modified-Since' => 'Mon, 14 Oct 2019 00:00:00 GMT' ],
null,
[ 'Last-Modified' => [ 'Mon, 14 Oct 2019 00:00:01 GMT' ] ]
],
'ims false' => [
'GET',
null,
'Mon, 14 Oct 2019 00:00:00 GMT',
null,
[ 'If-Modified-Since' => 'Mon, 14 Oct 2019 00:00:00 GMT' ],
304,
[ 'Last-Modified' => [ 'Mon, 14 Oct 2019 00:00:00 GMT' ] ]
],
'im true' => [
'GET',
'"a"',
null,
null,
[ 'If-Match' => '"a"' ],
null,
[ 'ETag' => [ '"a"' ] ]
],
'im false' => [
'GET',
'"a"',
null,
null,
[ 'If-Match' => '"b"' ],
412,
[ 'ETag' => [ '"a"' ] ]
],
'ius true' => [
'GET',
null,
'Mon, 14 Oct 2019 00:00:00 GMT',
null,
[ 'If-Unmodified-Since' => 'Mon, 14 Oct 2019 00:00:00 GMT' ],
null,
[ 'Last-Modified' => [ 'Mon, 14 Oct 2019 00:00:00 GMT' ] ]
],
'ius false' => [
'GET',
null,
'Mon, 14 Oct 2019 00:00:01 GMT',
null,
[ 'If-Unmodified-Since' => 'Mon, 14 Oct 2019 00:00:00 GMT' ],
412,
[ 'Last-Modified' => [ 'Mon, 14 Oct 2019 00:00:01 GMT' ] ]
],
'im true, ius false' => [
'GET',
'"a"',
'Mon, 14 Oct 2019 00:00:01 GMT',
null,
[
'If-Match' => '"a"',
'If-Unmodified-Since' => 'Mon, 14 Oct 2019 00:00:00 GMT'
],
null,
[
'Last-Modified' => [ 'Mon, 14 Oct 2019 00:00:01 GMT' ],
'ETag' => [ '"a"' ]
]
],
'inm true, ims false' => [
'GET',
'"a"',
'Mon, 14 Oct 2019 00:00:00 GMT',
null,
[
'If-None-Match' => '"b"',
'If-Modified-Since' => 'Mon, 14 Oct 2019 00:00:00 GMT'
],
null,
[
'Last-Modified' => [ 'Mon, 14 Oct 2019 00:00:00 GMT' ],
'ETag' => [ '"a"' ]
]
],
'im true, inm false' => [
'GET',
'"a"',
null,
null,
[
'If-Match' => '"a"',
'If-None-Match' => '"a"'
],
304,
[ 'ETag' => [ '"a"' ] ]
],
'ius true, inm false' => [
'GET',
'"a"',
'Mon, 14 Oct 2019 00:00:00 GMT',
null,
[
'If-Unmodified-Since' => 'Mon, 14 Oct 2019 00:00:00 GMT',
'If-None-Match' => '"a"'
],
304,
[
'Last-Modified' => [ 'Mon, 14 Oct 2019 00:00:00 GMT' ],
'ETag' => [ '"a"' ]
]
],
'im star true' => [
'GET',
'"a"',
null,
true,
[ 'If-Match' => '*' ],
null,
[ 'ETag' => [ '"a"' ] ]
],
'im star false' => [
'GET',
null,
null,
false,
[ 'If-Match' => '*' ],
412,
[]
],
'inm false post' => [
'POST',
'"a"',
null,
null,
[ 'If-None-Match' => '"a"' ],
412,
[ 'ETag' => [ '"a"' ] ]
],
'im multiple true' => [
'GET',
'"a"',
null,
null,
[ 'If-Match' => '"b", "a"' ],
null,
[ 'ETag' => [ '"a"' ] ]
],
'im multiple false' => [
'GET',
'"a"',
null,
null,
[ 'If-Match' => '"b", "c"' ],
412,
[ 'ETag' => [ '"a"' ] ]
],
// A strong ETag returned by the server may have been "weakened" by
// a proxy or middleware, e.g. when applying compression and setting
// content-encoding (rather than transfer-encoding). We want to still accept
// such ETags, even though that's against the HTTP spec (T238849).
// See ConditionalHeaderUtil::setVarnishETagHack.
'If-Match weak vs strong' => [
'GET',
'"a"',
null,
null,
[ 'If-Match' => 'W/"a"' ],
null,
[ 'ETag' => [ '"a"' ] ]
],
'If-Match strong vs weak' => [
'GET',
'W/"a"',
null,
null,
[ 'If-Match' => '"a"' ],
412,
[ 'ETag' => [ 'W/"a"' ] ]
],
'If-Match weak vs weak' => [
'GET',
'W/"a"',
null,
null,
[ 'If-Match' => 'W/"a"' ],
412,
[ 'ETag' => [ 'W/"a"' ] ]
],
'ims with resource unknown' => [
'GET',
null,
null,
null,
[ 'If-Modified-Since' => 'Mon, 14 Oct 2019 00:00:00 GMT' ],
null,
[]
],
'ius with resource unknown' => [
'GET',
null,
null,
null,
[ 'If-Unmodified-Since' => 'Mon, 14 Oct 2019 00:00:00 GMT' ],
412,
[]
],
];
}
/** @dataProvider provider */
public function testConditionalHeaderUtil( $method, $eTag, $lastModified, $hasRepresentation,
$requestHeaders, $expectedStatus, $expectedResponseHeaders
) {
$util = new ConditionalHeaderUtil;
$util->setValidators( $eTag, $lastModified, $hasRepresentation );
$request = new RequestData( [
'method' => $method,
'headers' => $requestHeaders
] );
$status = $util->checkPreconditions( $request );
$this->assertSame( $expectedStatus, $status );
$response = new Response;
$util->applyResponseHeaders( $response );
$this->assertSame( $expectedResponseHeaders, $response->getHeaders() );
}
}