diff --git a/app/etc/di.xml b/app/etc/di.xml index 19543375aad58..66843198a09e0 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -1739,7 +1739,7 @@ \Magento\Framework\App\Action\HttpOptionsActionInterface \Magento\Framework\App\Action\HttpGetActionInterface - \Magento\Framework\App\Action\HttpHeadActionInterface + \Magento\Framework\App\Action\HttpGetActionInterface \Magento\Framework\App\Action\HttpPostActionInterface \Magento\Framework\App\Action\HttpPutActionInterface \Magento\Framework\App\Action\HttpPatchActionInterface diff --git a/lib/internal/Magento/Framework/App/Action/HttpHeadActionInterface.php b/lib/internal/Magento/Framework/App/Action/HttpHeadActionInterface.php index d2f9b70913c1f..389bd8089967b 100644 --- a/lib/internal/Magento/Framework/App/Action/HttpHeadActionInterface.php +++ b/lib/internal/Magento/Framework/App/Action/HttpHeadActionInterface.php @@ -12,6 +12,8 @@ /** * Marker for actions processing HEAD requests. + * + * @deprecated Both GET and HEAD requests map to HttpGetActionInterface */ interface HttpHeadActionInterface extends ActionInterface { diff --git a/lib/internal/Magento/Framework/App/Http.php b/lib/internal/Magento/Framework/App/Http.php index 3c6dee49f97b4..a2d7d136e12d7 100644 --- a/lib/internal/Magento/Framework/App/Http.php +++ b/lib/internal/Magento/Framework/App/Http.php @@ -142,12 +142,26 @@ public function launch() } else { throw new \InvalidArgumentException('Invalid return type'); } + if ($this->_request->isHead() && $this->_response->getHttpResponseCode() == 200) { + $this->handleHeadRequest(); + } // This event gives possibility to launch something before sending output (allow cookie setting) $eventParams = ['request' => $this->_request, 'response' => $this->_response]; $this->_eventManager->dispatch('controller_front_send_response_before', $eventParams); return $this->_response; } + /** + * Handle HEAD requests by adding the Content-Length header and removing the body from the response. + * @return void + */ + private function handleHeadRequest() + { + $contentLength = strlen($this->_response->getContent()); + $this->_response->clearBody(); + $this->_response->setHeader('Content-Length', $contentLength); + } + /** * {@inheritdoc} */ diff --git a/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php b/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php index a299e04e152cc..6606dce5b2ab1 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php @@ -92,7 +92,7 @@ protected function setUp() 'pathInfoProcessor' => $pathInfoProcessorMock, 'objectManager' => $objectManagerMock ]) - ->setMethods(['getFrontName']) + ->setMethods(['getFrontName', 'isHead']) ->getMock(); $this->areaListMock = $this->getMockBuilder(\Magento\Framework\App\AreaList::class) ->disableOriginalConstructor() @@ -155,6 +155,7 @@ private function setUpLaunch() public function testLaunchSuccess() { $this->setUpLaunch(); + $this->requestMock->expects($this->once())->method('isHead')->will($this->returnValue(false)); $this->eventManagerMock->expects($this->once()) ->method('dispatch') ->with( @@ -181,6 +182,34 @@ function () { $this->http->launch(); } + public function testLaunchHeadRequest() + { + $body = "Test"; + $contentLength = strlen($body); + $this->setUpLaunch(); + $this->requestMock->expects($this->once())->method('isHead')->will($this->returnValue(true)); + $this->responseMock->expects($this->once()) + ->method('getHttpResponseCode') + ->will($this->returnValue(200)); + $this->responseMock->expects($this->once()) + ->method('getContent') + ->will($this->returnValue($body)); + $this->responseMock->expects($this->once()) + ->method('clearBody') + ->will($this->returnValue($this->responseMock)); + $this->responseMock->expects($this->once()) + ->method('setHeader') + ->with('Content-Length', $contentLength) + ->will($this->returnValue($this->responseMock)); + $this->eventManagerMock->expects($this->once()) + ->method('dispatch') + ->with( + 'controller_front_send_response_before', + ['request' => $this->requestMock, 'response' => $this->responseMock] + ); + $this->assertSame($this->responseMock, $this->http->launch()); + } + public function testHandleDeveloperModeNotInstalled() { $dir = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\ReadInterface::class); diff --git a/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php b/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php index d945791282a2d..1ea47991d6df6 100644 --- a/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php +++ b/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php @@ -9,6 +9,11 @@ class HttpTest extends \PHPUnit\Framework\TestCase { + /** + * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + */ + private $request; + /** * @var \Magento\Framework\HTTP\PhpEnvironment\Response|\PHPUnit_Framework_MockObject_MockObject */ @@ -29,12 +34,16 @@ class HttpTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { + $this->request = $this->createPartialMock( + \Magento\Framework\App\Request\Http::class, + ['isHead'] + ); $this->response = $this->createPartialMock( \Magento\Framework\HTTP\PhpEnvironment\Response::class, ['setHeader', 'sendHeaders', 'setHeaders'] ); $this->mime = $this->createMock(\Magento\Framework\File\Mime::class); - $this->object = new Http($this->response, $this->mime); + $this->object = new Http($this->request, $this->response, $this->mime); } /** @@ -57,6 +66,9 @@ public function testSend(): void ->method('getMimeType') ->with($file) ->will($this->returnValue($contentType)); + $this->request->expects($this->once()) + ->method('isHead') + ->will($this->returnValue(false)); $this->expectOutputString(file_get_contents($file)); $this->object->send($file); @@ -83,6 +95,9 @@ public function testSendWithOptions(): void ->method('getMimeType') ->with($file) ->will($this->returnValue($contentType)); + $this->request->expects($this->once()) + ->method('isHead') + ->will($this->returnValue(false)); $this->expectOutputString(file_get_contents($file)); $this->object->send(['filepath' => $file, 'headers' => $headers]); @@ -106,4 +121,32 @@ public function testSendNoFileExistException(): void { $this->object->send('nonexistent.file'); } + + /** + * @return void + */ + public function testSendHeadRequest(): void + { + $file = __DIR__ . '/../../_files/javascript.js'; + $contentType = 'content/type'; + + $this->response->expects($this->at(0)) + ->method('setHeader') + ->with('Content-length', filesize($file)); + $this->response->expects($this->at(1)) + ->method('setHeader') + ->with('Content-Type', $contentType); + $this->response->expects($this->once()) + ->method('sendHeaders'); + $this->mime->expects($this->once()) + ->method('getMimeType') + ->with($file) + ->will($this->returnValue($contentType)); + $this->request->expects($this->once()) + ->method('isHead') + ->will($this->returnValue(true)); + + $this->object->send($file); + $this->assertEquals(false, $this->hasOutput()); + } } diff --git a/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php b/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php index aa527866eff55..74cc347fd2fa2 100644 --- a/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php +++ b/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php @@ -8,6 +8,11 @@ class Http { + /** + * @var \Magento\Framework\App\Request\Http + */ + private $request; + /** * @var \Magento\Framework\HTTP\PhpEnvironment\Response */ @@ -19,13 +24,16 @@ class Http private $mime; /** + * @param \Magento\Framework\App\Request\Http $request * @param \Magento\Framework\App\Response\Http $response * @param \Magento\Framework\File\Mime $mime */ public function __construct( + \Magento\Framework\App\Request\Http $request, \Magento\Framework\HTTP\PhpEnvironment\Response $response, \Magento\Framework\File\Mime $mime ) { + $this->request = $request; $this->response = $response; $this->mime = $mime; } @@ -55,6 +63,11 @@ public function send($options = null) $this->response->sendHeaders(); + if ($this->request->isHead()) { + // Do not send the body on HEAD requests. + return; + } + $handle = fopen($filepath, 'r'); if ($handle) { while (($buffer = fgets($handle, 4096)) !== false) {