RequestInterface: add hasBody()

hasBody() provides an easy way to check whether the request contains
payload data. This ensures this check is performed consistently and in
accordance with RFC 9110.

Change-Id: If15f2cb6239c4c8170e70a5bec8a5f0821361275
This commit is contained in:
daniel 2024-02-27 13:28:00 +01:00 committed by Daniel Kinzler
parent 026d8d02a2
commit 36062df690
5 changed files with 128 additions and 0 deletions

View file

@ -128,4 +128,34 @@ abstract class RequestBase implements RequestInterface {
}
return $ct;
}
/**
* Return true if the client provided a content-length header or a
* transfer-encoding header.
*
* @see https://www.rfc-editor.org/rfc/rfc9110.html#name-content-length
*
* @return bool
*/
public function hasBody(): bool {
// From RFC9110, section 8.6: A user agent SHOULD send Content-Length
// in a request when the method defines a meaning for enclosed content
// and it is not sending Transfer-Encoding. [...]
// A user agent SHOULD NOT send a Content-Length header field when the
// request message does not contain content and the method semantics do
// not anticipate such data.
if ( $this->getHeaderLine( 'content-length' ) !== '' ) {
// If a content length is set, there is a body
return true;
}
if ( $this->getHeaderLine( 'transfer-encoding' ) !== '' ) {
// If a transfer encoding is set, there is a body
return true;
}
return false;
}
}

View file

@ -102,4 +102,21 @@ class RequestData extends RequestBase {
public function getPostParams() {
return $this->postParams;
}
public function hasBody(): bool {
if ( parent::hasBody() ) {
return true;
}
if ( $this->parsedBody !== null ) {
return true;
}
if ( $this->getBody()->getContents() !== '' ) {
return true;
}
return false;
}
}

View file

@ -268,4 +268,12 @@ interface RequestInterface {
public function setParsedBody( ?array $data );
public function getBodyType(): ?string;
/**
* Determines whether the request has body data associated with it.
* Note that this method may return true even if the body is empty.
*
* @return bool
*/
public function hasBody(): bool;
}

View file

@ -64,4 +64,34 @@ class RequestBaseTest extends \MediaWikiUnitTestCase {
$rb->setHeaders( [ 'Content-type' => 'application/json' ] );
$this->assertSame( [ 'application/json' ], $rb->getHeaders()[ 'Content-type' ] );
}
public static function provideHasBody() {
yield 'nothing'
=> [ [], false ];
yield 'content-length: 1'
=> [ [ 'content-length' => '1' ], true ];
yield 'content-length: 0'
=> [ [ 'content-length' => '0' ], true ];
yield 'content-length empty'
=> [ [ 'content-length' => '' ], false ];
yield 'transfer-encoding: chunked'
=> [ [ 'transfer-encoding' => 'chunked' ], true ];
yield 'transfer-encoding empty'
=> [ [ 'transfer-encoding' => '' ], false ];
}
/**
* @dataProvider provideHasBody
*/
public function testHasBody( $headers, $expected ) {
$rb = $this->getMockForAbstractClass( RequestBase::class, [ 'cookiePrefix' ] );
$rb->setHeaders( $headers );
$this->assertSame( $expected, $rb->hasBody() );
}
}

View file

@ -221,4 +221,47 @@ class RequestDataTest extends \MediaWikiUnitTestCase {
$this->assertSame( $expectedResult, $request->getBodyType() );
}
public static function provideHasBody() {
yield 'nothing'
=> [ [], false ];
yield 'content-length: 1'
=> [ [ 'content-length' => '1' ], true ];
yield 'content-length: 0'
=> [ [ 'content-length' => '0' ], true ];
yield 'content-length empty'
=> [ [ 'content-length' => '' ], false ];
yield 'transfer-encoding: chunked'
=> [ [ 'transfer-encoding' => 'chunked' ], true ];
yield 'transfer-encoding empty'
=> [ [ 'transfer-encoding' => '' ], false ];
}
/**
* @dataProvider provideHasBody
*/
public function testHasBodyBasedOnHeader( $headers, $expected ) {
$request = new RequestData( [
'headers' => $headers
] );
$this->assertSame( $expected, $request->hasBody() );
}
public function testHasBodyWithContent() {
$request = new RequestData( [
'bodyContents' => 'test test test'
] );
$this->assertTrue( $request->hasBody() );
}
public function testHasBodyWithParsedBody() {
$request = new RequestData( [
'parsedBody' => [ 'foo' => 'bar' ]
] );
$this->assertTrue( $request->hasBody() );
}
}