WebRequest & RequestFromGlobals: get HTTP headers in one way
apache_request_headers() is a vendor-specific function - it got used
when present and alternative code paths were exercised otherwise.
These preserved certain "special" headers, e.g. Content-Type, only
inconsistently.
The function getallheaders() is an alias[1] for apache_request_headers()
on systems where the latter is present. Alternatively, there is a
polyfill (ralouphie/getallheaders) which is already installed in
mediawiki-vendor[2] (by virtue of guzzle).
Using getallheaders() exclusively, will make sure these "special"
headers are consistently available alongside their "regular"[3] peers
and helps MediaWiki code focus on its domain.
The dependency to ralouphie/getallheaders is made explicit in the same
version in which it is currently locked in mediawiki-vendor[4].
This surfaced because the deprecation warning for API POST requests
without a Content-Type header, introduced in bba1a0f, appeared in my
development system (somewhat dated addshore/mediawiki-docker-dev/) even
though the client did a fine job.
Interesting implementation detail: While WebRequest keeps track of
headers using keys in all upper case, REST RequestFromGlobals does so in
all lower case - but both use retrieval logic complementary to their
respective approach however. In case of REST RequestFromGlobals this is
encapsulated inside of HeaderContainer (setting and retrieving), while
WebRequest does all of this by itself. Cf. [5] and [6]
[1]: https://www.php.net/manual/en/function.getallheaders.php
[2]: https://github.com/wikimedia/mediawiki-vendor/tree/8f2967d/ralouphie/getallheaders
[3]: https://www.php.net/manual/en/reserved.variables.server.php#110763
[4]: https://github.com/wikimedia/mediawiki-vendor/blob/8f2967d/composer.lock#L3250
[5]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
[6]: https://www.php.net/manual/en/function.apache-request-headers.php#124236
Bug: T245535
Change-Id: Iba52f152e15928473b729a2588c2462e76e85634
This commit is contained in:
parent
7c74e448aa
commit
ff40270448
5 changed files with 46 additions and 35 deletions
|
|
@ -36,6 +36,7 @@
|
|||
"php": ">=7.2.22",
|
||||
"psr/container": "1.0.0",
|
||||
"psr/log": "1.1.3",
|
||||
"ralouphie/getallheaders": "3.0.3",
|
||||
"wikimedia/assert": "0.5.0",
|
||||
"wikimedia/at-ease": "2.0.0",
|
||||
"wikimedia/base-convert": "2.0.1",
|
||||
|
|
|
|||
|
|
@ -54,22 +54,7 @@ class RequestFromGlobals extends RequestBase {
|
|||
}
|
||||
|
||||
protected function initHeaders() {
|
||||
if ( function_exists( 'apache_request_headers' ) ) {
|
||||
$this->setHeaders( apache_request_headers() );
|
||||
} else {
|
||||
$headers = [];
|
||||
foreach ( $_SERVER as $name => $value ) {
|
||||
if ( substr( $name, 0, 5 ) === 'HTTP_' ) {
|
||||
$name = strtolower( str_replace( '_', '-', substr( $name, 5 ) ) );
|
||||
$headers[$name] = $value;
|
||||
} elseif ( $name === 'CONTENT_LENGTH' ) {
|
||||
$headers['content-length'] = $value;
|
||||
} elseif ( $name === 'CONTENT_TYPE' ) {
|
||||
$headers['content-type'] = $value;
|
||||
}
|
||||
}
|
||||
$this->setHeaders( $headers );
|
||||
}
|
||||
$this->setHeaders( getallheaders() );
|
||||
}
|
||||
|
||||
public function getBody() {
|
||||
|
|
|
|||
|
|
@ -1089,21 +1089,7 @@ class WebRequest {
|
|||
return;
|
||||
}
|
||||
|
||||
$apacheHeaders = function_exists( 'apache_request_headers' ) ? apache_request_headers() : false;
|
||||
if ( $apacheHeaders ) {
|
||||
foreach ( $apacheHeaders as $tempName => $tempValue ) {
|
||||
$this->headers[strtoupper( $tempName )] = $tempValue;
|
||||
}
|
||||
} else {
|
||||
foreach ( $_SERVER as $name => $value ) {
|
||||
if ( substr( $name, 0, 5 ) === 'HTTP_' ) {
|
||||
$name = str_replace( '_', '-', substr( $name, 5 ) );
|
||||
$this->headers[$name] = $value;
|
||||
} elseif ( $name === 'CONTENT_LENGTH' ) {
|
||||
$this->headers['CONTENT-LENGTH'] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->headers = array_change_key_case( getallheaders(), CASE_UPPER );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -77,16 +77,26 @@ class RequestFromGlobalsTest extends MediaWikiTestCase {
|
|||
$this->setServerVars( [
|
||||
'HTTP_HOST' => '[::1]',
|
||||
'CONTENT_LENGTH' => 6,
|
||||
'CONTENT_TYPE' => 'application/json'
|
||||
'CONTENT_TYPE' => 'application/json',
|
||||
'CONTENT_MD5' => 'rL0Y20zC+Fzt72VPzMSk2A==',
|
||||
] );
|
||||
|
||||
$this->assertEquals( $this->reqFromGlobals->getHeaders(), [
|
||||
'host' => [ '[::1]' ],
|
||||
'content-length' => [ 6 ],
|
||||
'content-type' => [ 'application/json' ]
|
||||
'Host' => [ '[::1]' ],
|
||||
'Content-Length' => [ 6 ],
|
||||
'Content-Type' => [ 'application/json' ],
|
||||
'Content-Md5' => [ 'rL0Y20zC+Fzt72VPzMSk2A==' ],
|
||||
] );
|
||||
}
|
||||
|
||||
public function testGetHeaderKeyIsCaseInsensitive() {
|
||||
$cacheControl = 'private, must-revalidate, max-age=0';
|
||||
$this->setServerVars( [ 'HTTP_CACHE_CONTROL' => $cacheControl ] );
|
||||
|
||||
$this->assertSame( $this->reqFromGlobals->getHeader( 'Cache-Control' ), [ $cacheControl ] );
|
||||
$this->assertSame( $this->reqFromGlobals->getHeader( 'cache-control' ), [ $cacheControl ] );
|
||||
}
|
||||
|
||||
public function testGetBody() {
|
||||
$this->setServerVars( [
|
||||
'REQUEST_METHOD' => 'POST',
|
||||
|
|
|
|||
|
|
@ -644,6 +644,35 @@ class WebRequestTest extends MediaWikiTestCase {
|
|||
$this->assertSame( $request->getAcceptLang(), $expectedLanguages, $description );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers WebRequest::getHeader
|
||||
*/
|
||||
public function testGetHeaderCanYieldSpecialCgiHeaders() {
|
||||
$contentType = 'application/json; charset=utf-8';
|
||||
$contentLength = '4711';
|
||||
$contentMd5 = 'rL0Y20zC+Fzt72VPzMSk2A==';
|
||||
$this->setServerVars( [
|
||||
'HTTP_CONTENT_TYPE' => $contentType,
|
||||
'HTTP_CONTENT_LENGTH' => $contentLength,
|
||||
'HTTP_CONTENT_MD5' => $contentMd5,
|
||||
] );
|
||||
$request = new WebRequest();
|
||||
$this->assertSame( $request->getHeader( 'Content-Type' ), $contentType );
|
||||
$this->assertSame( $request->getHeader( 'Content-Length' ), $contentLength );
|
||||
$this->assertSame( $request->getHeader( 'Content-Md5' ), $contentMd5 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers WebRequest::getHeader
|
||||
*/
|
||||
public function testGetHeaderKeyIsCaseInsensitive() {
|
||||
$cacheControl = 'private, must-revalidate, max-age=0';
|
||||
$this->setServerVars( [ 'HTTP_CACHE_CONTROL' => $cacheControl ] );
|
||||
$request = new WebRequest();
|
||||
$this->assertSame( $request->getHeader( 'Cache-Control' ), $cacheControl );
|
||||
$this->assertSame( $request->getHeader( 'cache-control' ), $cacheControl );
|
||||
}
|
||||
|
||||
protected function setServerVars( $vars ) {
|
||||
// Don't remove vars which should be available in all SAPI.
|
||||
if ( !isset( $vars['REQUEST_TIME_FLOAT'] ) ) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue