Skip to content

Commit 22b9e56

Browse files
committed
Changed the strlen to mb_strl with 8bit encoding to ensure that the content length is calculated in bytes in all PHP installations. Added test cases to cover non-ascii content (multi-byte characters, null byte, LTR text).
1 parent 72630cd commit 22b9e56

File tree

2 files changed

+40
-5
lines changed

2 files changed

+40
-5
lines changed

lib/internal/Magento/Framework/App/Http.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,11 @@ public function launch()
157157
*/
158158
private function handleHeadRequest()
159159
{
160-
$contentLength = strlen($this->_response->getContent());
160+
// It is possible that some PHP installations have overloaded strlen to use mb_strlen instead.
161+
// This means strlen might return the actual number of characters in a non-ascii string instead
162+
// of the number of bytes. Use mb_strlen explicitly with a single byte character encoding to ensure
163+
// that the content length is calculated in bytes.
164+
$contentLength = mb_strlen($this->_response->getContent(), '8bit');
161165
$this->_response->clearBody();
162166
$this->_response->setHeader('Content-Length', $contentLength);
163167
}

lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,15 @@ function () {
182182
$this->http->launch();
183183
}
184184

185-
public function testLaunchHeadRequest()
185+
186+
/**
187+
* Test that HEAD requests lead to an empty body and a Content-Length header matching the original body size.
188+
* @dataProvider dataProviderForTestLaunchHeadRequest
189+
* @param string $body
190+
* @param int $expectedLength
191+
*/
192+
public function testLaunchHeadRequest($body, $expectedLength)
186193
{
187-
$body = "<html><head></head><body>Test</body></html>";
188-
$contentLength = strlen($body);
189194
$this->setUpLaunch();
190195
$this->requestMock->expects($this->once())->method('isHead')->will($this->returnValue(true));
191196
$this->responseMock->expects($this->once())
@@ -199,7 +204,7 @@ public function testLaunchHeadRequest()
199204
->will($this->returnValue($this->responseMock));
200205
$this->responseMock->expects($this->once())
201206
->method('setHeader')
202-
->with('Content-Length', $contentLength)
207+
->with('Content-Length', $expectedLength)
203208
->will($this->returnValue($this->responseMock));
204209
$this->eventManagerMock->expects($this->once())
205210
->method('dispatch')
@@ -210,6 +215,32 @@ public function testLaunchHeadRequest()
210215
$this->assertSame($this->responseMock, $this->http->launch());
211216
}
212217

218+
/**
219+
* Different test content for responseMock with their expected lengths in bytes.
220+
* @return array
221+
*/
222+
public function dataProviderForTestLaunchHeadRequest()
223+
{
224+
return [
225+
[
226+
'<html><head></head><body>Test</body></html>', // Ascii text
227+
43 // Expected Content-Length
228+
],
229+
[
230+
'<html><head></head><body>部落格</body></html>', // Multi-byte characters
231+
48 // Expected Content-Length
232+
],
233+
[
234+
'<html><head></head><body>'.chr(0).'</body></html>', // Null byte
235+
40 // Expected Content-Length
236+
],
237+
[
238+
'<html><head></head>خرید<body></body></html>', // LTR text
239+
47 // Expected Content-Length
240+
]
241+
];
242+
}
243+
213244
public function testHandleDeveloperModeNotInstalled()
214245
{
215246
$dir = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\ReadInterface::class);

0 commit comments

Comments
 (0)