Skip to content

Commit 44809f1

Browse files
kellerjmrtnKeller Martin
and
Keller Martin
authored
Add Guzzle Timeout To Prerender Middleware (#39)
Also adds new `prerender.timeout` config option. Defaults to no timeout Co-authored-by: Keller Martin <kellerm@schwebdesign.com>
1 parent c31fb16 commit 44809f1

File tree

5 files changed

+83
-21
lines changed

5 files changed

+83
-21
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,21 @@ return [
276276
'Qwantify'
277277
],
278278

279+
/*
280+
|--------------------------------------------------------------------------
281+
| Timeout
282+
|--------------------------------------------------------------------------
283+
|
284+
| Specifies the Guzzle request timeout in seconds. If the request for a
285+
| prerendered page takes longer than this, the request will be terminated
286+
| and the page will be loaded without prerender. A value of 0 means no
287+
| timeout.
288+
|
289+
| See: https://docs.guzzlephp.org/en/stable/request-options.html#timeout
290+
|
291+
*/
292+
'timeout' => env('PRERENDER_TIMEOUT', 0),
293+
279294
];
280295
```
281296

config/prerender.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,19 @@
181181
'Qwantify',
182182
],
183183

184+
/*
185+
|--------------------------------------------------------------------------
186+
| Timeout
187+
|--------------------------------------------------------------------------
188+
|
189+
| Specifies the Guzzle request timeout in seconds. If the request for a
190+
| prerendered page takes longer than this, the request will be terminated
191+
| and the page will be loaded without prerender. A value of 0 means no
192+
| timeout.
193+
|
194+
| See: https://docs.guzzlephp.org/en/stable/request-options.html#timeout
195+
|
196+
*/
197+
'timeout' => env('PRERENDER_TIMEOUT', 0),
198+
184199
];

src/PrerenderMiddleware.php

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Closure;
66
use GuzzleHttp\Client as Guzzle;
7+
use GuzzleHttp\Exception\ConnectException;
78
use GuzzleHttp\Exception\RequestException;
89
use Illuminate\Http\Request;
910
use Illuminate\Support\Arr;
@@ -71,15 +72,15 @@ public function __construct(Guzzle $client)
7172
{
7273
$this->returnSoftHttpCodes = config('prerender.prerender_soft_http_codes');
7374

74-
if ($this->returnSoftHttpCodes) {
75-
$this->client = $client;
76-
} else {
77-
// Workaround to avoid following redirects
78-
$config = $client->getConfig();
79-
$config['allow_redirects'] = false;
80-
$this->client = new Guzzle($config);
75+
$guzzleConfig = $client->getConfig();
76+
$guzzleConfig['timeout'] = config('prerender.timeout');
77+
78+
if (!$this->returnSoftHttpCodes) {
79+
$guzzleConfig['allow_redirects'] = false;
8180
}
8281

82+
$this->client = new Guzzle($guzzleConfig);
83+
8384
$config = config('prerender');
8485

8586
$this->prerenderUri = $config['prerender_url'];
@@ -102,7 +103,7 @@ public function handle(Request $request, Closure $next)
102103
if ($prerenderedResponse) {
103104
$statusCode = $prerenderedResponse->getStatusCode();
104105

105-
if (! $this->returnSoftHttpCodes && $statusCode >= 300 && $statusCode < 400) {
106+
if (!$this->returnSoftHttpCodes && $statusCode >= 300 && $statusCode < 400) {
106107
$headers = $prerenderedResponse->getHeaders();
107108

108109
return Redirect::to(array_change_key_case($headers, CASE_LOWER)['location'][0], $statusCode);
@@ -128,11 +129,11 @@ private function shouldShowPrerenderedPage(Request $request): bool
128129

129130
$isRequestingPrerenderedPage = false;
130131

131-
if (! $userAgent) {
132+
if (!$userAgent) {
132133
return false;
133134
}
134135

135-
if (! $request->isMethod('GET')) {
136+
if (!$request->isMethod('GET')) {
136137
return false;
137138
}
138139

@@ -152,13 +153,13 @@ private function shouldShowPrerenderedPage(Request $request): bool
152153
$isRequestingPrerenderedPage = true;
153154
}
154155

155-
if (! $isRequestingPrerenderedPage) {
156+
if (!$isRequestingPrerenderedPage) {
156157
return false;
157158
}
158159

159160
// only check whitelist if it is not empty
160161
if ($this->whitelist) {
161-
if (! $this->isListed($requestUri, $this->whitelist)) {
162+
if (!$this->isListed($requestUri, $this->whitelist)) {
162163
return false;
163164
}
164165
}
@@ -207,19 +208,21 @@ private function getPrerenderedPageResponse(Request $request): ?ResponseInterfac
207208

208209
return $this->client->get($this->prerenderUri.'/'.urlencode($protocol.'://'.$host.'/'.$path), compact('headers'));
209210
} catch (RequestException $exception) {
210-
if (! $this->returnSoftHttpCodes && ! empty($exception->getResponse()) && $exception->getResponse()->getStatusCode() === 404) {
211+
if (!$this->returnSoftHttpCodes && !empty($exception->getResponse()) && $exception->getResponse()->getStatusCode() === 404) {
211212
abort(404);
212213
}
214+
} catch (ConnectException $exception) {
215+
//
216+
}
213217

214-
// In case of an exception, we only throw the exception if we are in debug mode. Otherwise,
215-
// we return null and the handle() method will just pass the request to the next middleware
216-
// and we do not show a prerendered page.
217-
if (config('app.debug')) {
218-
throw $exception;
219-
}
220-
221-
return null;
218+
// In case of an exception, we only throw the exception if we are in debug mode. Otherwise,
219+
// we return null and the handle() method will just pass the request to the next middleware
220+
// and we do not show a prerendered page.
221+
if (config('app.debug')) {
222+
throw $exception;
222223
}
224+
225+
return null;
223226
}
224227

225228
/**

tests/PrerenderMiddlewareTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace CodebarAg\LaravelPrerender\Tests;
44

5+
use GuzzleHttp\Client;
6+
57
class PrerenderMiddlewareTest extends TestCase
68
{
79
/** @test */
@@ -78,6 +80,20 @@ public function it_should_not_prerender_page_when_missing_user_agent()
7880
->assertSee('GET - Success');
7981
}
8082

83+
/** @test */
84+
public function it_should_not_prerender_page_if_request_times_out()
85+
{
86+
$this->app->bind(Client::class, function () {
87+
return $this->createMockTimeoutClient();
88+
});
89+
90+
$this->allowSymfonyUserAgent();
91+
92+
$this->get('/test-middleware')
93+
->assertHeaderMissing('prerender.io-mock')
94+
->assertSee('GET - Success');
95+
}
96+
8197
private function allowSymfonyUserAgent()
8298
{
8399
config()->set('prerender.crawler_user_agents', ['symfony']);

tests/TestCase.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
use CodebarAg\LaravelPrerender\LaravelPrerenderServiceProvider;
66
use CodebarAg\LaravelPrerender\PrerenderMiddleware;
77
use GuzzleHttp\Client;
8+
use GuzzleHttp\Exception\ConnectException;
89
use GuzzleHttp\Handler\MockHandler;
910
use GuzzleHttp\HandlerStack;
11+
use GuzzleHttp\Psr7\Request;
1012
use GuzzleHttp\Psr7\Response;
1113
use Illuminate\Foundation\Application;
1214
use Illuminate\Foundation\Http\Kernel;
@@ -49,6 +51,17 @@ protected function getEnvironmentSetUp($app): void
4951
});
5052
}
5153

54+
protected function createMockTimeoutClient(): Client
55+
{
56+
$mock = new MockHandler([
57+
new ConnectException('Could not connect', new Request('GET', 'test')),
58+
]);
59+
60+
$stack = HandlerStack::create($mock);
61+
62+
return new Client(['handler' => $stack]);
63+
}
64+
5265
protected function setupRoutes(): void
5366
{
5467
Route::get('test-middleware', function () {

0 commit comments

Comments
 (0)