From 2ef203c5067a5fed5a517ee79e91455ef0493a27 Mon Sep 17 00:00:00 2001 From: Keller Martin <43446570+kellerjmrtn@users.noreply.github.com> Date: Fri, 9 Aug 2024 00:19:17 -0400 Subject: [PATCH] Send Entire Request, Including Query Params, To Prerender (#40) * Send Entire Request, Including Query Params, To Prerender * Update To Include New Config Value & Maintain Backwards Compatibility * Fix Typo --------- Co-authored-by: Keller Martin --- README.md | 14 +++++++++++ config/prerender.php | 14 +++++++++++ src/PrerenderMiddleware.php | 42 +++++++++++++++++++++++-------- tests/PrerenderMiddlewareTest.php | 37 +++++++++++++++++++++++++++ tests/TestCase.php | 18 +++++++++++++ 5 files changed, 115 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 482aa74..8949e2c 100644 --- a/README.md +++ b/README.md @@ -289,8 +289,22 @@ return [ | See: https://docs.guzzlephp.org/en/stable/request-options.html#timeout | */ + 'timeout' => env('PRERENDER_TIMEOUT', 0), + /* + |-------------------------------------------------------------------------- + | Query Parameters + |-------------------------------------------------------------------------- + | + | By default, request query parameters are not sent to prerender when + | requesting the prerendered page. Setting this to true will cause the full + | URL, including query parameters, to be sent to prerender. + | + */ + + 'full_url' => env('PRERENDER_FULL_URL', false), + ]; ``` diff --git a/config/prerender.php b/config/prerender.php index 940e2f4..96fac0e 100644 --- a/config/prerender.php +++ b/config/prerender.php @@ -194,6 +194,20 @@ | See: https://docs.guzzlephp.org/en/stable/request-options.html#timeout | */ + 'timeout' => env('PRERENDER_TIMEOUT', 0), + /* + |-------------------------------------------------------------------------- + | Query Parameters + |-------------------------------------------------------------------------- + | + | By default, request query parameters are not sent to prerender when + | requesting the prerendered page. Setting this to true will cause the full + | URL, including query parameters, to be sent to prerender. + | + */ + + 'full_url' => env('PRERENDER_FULL_URL', false), + ]; diff --git a/src/PrerenderMiddleware.php b/src/PrerenderMiddleware.php index df23b96..f8dc9b3 100644 --- a/src/PrerenderMiddleware.php +++ b/src/PrerenderMiddleware.php @@ -65,6 +65,13 @@ class PrerenderMiddleware */ private $returnSoftHttpCodes; + /** + * Send the full request URL to prerender. + * + * @var bool + */ + private $useFullURL; + /** * Creates a new PrerenderMiddleware instance. */ @@ -88,6 +95,7 @@ public function __construct(Guzzle $client) $this->prerenderToken = $config['prerender_token']; $this->whitelist = $config['whitelist']; $this->blacklist = $config['blacklist']; + $this->useFullURL = $config['full_url']; } /** @@ -195,18 +203,10 @@ private function getPrerenderedPageResponse(Request $request): ?ResponseInterfac $headers['X-Prerender-Token'] = $this->prerenderToken; } - $protocol = $request->isSecure() ? 'https' : 'http'; - try { - // Return the Guzzle Response - $host = $request->getHost(); - $path = $request->Path(); - // Fix "//" 404 error - if ($path === '/') { - $path = ''; - } + $url = $this->generatePrerenderUrl($request); - return $this->client->get($this->prerenderUri.'/'.urlencode($protocol.'://'.$host.'/'.$path), compact('headers')); + return $this->client->get($this->prerenderUri.'/'.urlencode($url), compact('headers')); } catch (RequestException $exception) { if (!$this->returnSoftHttpCodes && !empty($exception->getResponse()) && $exception->getResponse()->getStatusCode() === 404) { abort(404); @@ -250,4 +250,26 @@ private function isListed($needles, array $list): bool return false; } + + /** + * Generate the request URL to send to prerender. When useFullURL is false, the legacy URL + * generation logic is used + */ + private function generatePrerenderUrl(Request $request): string + { + if ($this->useFullURL) { + return $request->fullUrl(); + } + + $protocol = $request->isSecure() ? 'https' : 'http'; + $host = $request->getHost(); + $path = $request->Path(); + + // Fix "//" 404 error + if ($path === '/') { + $path = ''; + } + + return $protocol.'://'.$host.'/'.$path; + } } diff --git a/tests/PrerenderMiddlewareTest.php b/tests/PrerenderMiddlewareTest.php index 336057f..9ae6196 100644 --- a/tests/PrerenderMiddlewareTest.php +++ b/tests/PrerenderMiddlewareTest.php @@ -94,8 +94,45 @@ public function it_should_not_prerender_page_if_request_times_out() ->assertSee('GET - Success'); } + /** @test */ + public function it_does_not_send_query_strings_to_prerender_by_default() + { + $this->app->bind(Client::class, function () { + return $this->createMockUrlTrackingClient(); + }); + + $this->allowSymfonyUserAgent(); + + $this->get('/test-middleware?withQueryParam=true') + ->assertHeader('prerender.io-mock', true) + ->assertSuccessful() + ->assertSee(urlencode('/test-middleware')) + ->assertDontSee('withQueryParam'); + } + + /** @test */ + public function it_sends_full_query_string_to_prerender() + { + $this->app->bind(Client::class, function () { + return $this->createMockUrlTrackingClient(); + }); + + $this->allowSymfonyUserAgent(); + $this->allowQueryParams(); + + $this->get('/test-middleware?withQueryParam=true') + ->assertHeader('prerender.io-mock', true) + ->assertSuccessful() + ->assertSee(urlencode('/test-middleware?withQueryParam=true')); + } + private function allowSymfonyUserAgent() { config()->set('prerender.crawler_user_agents', ['symfony']); } + + private function allowQueryParams() + { + config()->set('prerender.full_url', true); + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index bc35721..ff281b0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -13,6 +13,7 @@ use Illuminate\Foundation\Application; use Illuminate\Foundation\Http\Kernel; use Illuminate\Support\Facades\Route; +use Psr\Http\Message\RequestInterface; class TestCase extends \Orchestra\Testbench\TestCase { @@ -62,6 +63,23 @@ protected function createMockTimeoutClient(): Client return new Client(['handler' => $stack]); } + protected function createMockUrlTrackingClient(): Client + { + $mock = new MockHandler([ + function (RequestInterface $request) { + return new Response( + 200, + ['prerender.io-mock' => true], + (string) $request->getUri() + ); + }, + ]); + + $stack = HandlerStack::create($mock); + + return new Client(['handler' => $stack]); + } + protected function setupRoutes(): void { Route::get('test-middleware', function () {