Skip to content

Throttle requests #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Easily configure which routes to scan, exclude or include specific checks or eve
- [Scanning routes](#scanning-routes)
- [Scanning a single route](#scanning-a-single-route)
- [Scanning routes in an SPA application](#scanning-routes-in-an-spa-application)
- [Throttling](#throttling)
- [Scan model urls](#scan-model-urls)
- [Saving scans into the database](#saving-scans-into-the-database)
- [Listening to events](#listening-to-events)
Expand Down Expand Up @@ -186,6 +187,17 @@ php artisan seo:scan-url https://vormkracht10.nl --javascript

> Note: This command will use Puppeteer to render the page. Make sure that you have Puppeteer installed on your system. You can install Puppeteer by running the following command: `npm install puppeteer`. **At this moment it's only available when scanning single routes.**

### Throttling

If you want to throttle the requests, you can set the `throttle` option to `true` in the config file. You can also set the amount of requests per minute by setting the `requests_per_minute` option in the config file.

```php
'throttle' => [
'enabled' => false,
'requests_per_minute' => 10,
],
```

### Scan model urls

When you have an application where you have a lot of pages which are related to a model, you can save the SEO score to the model. This way you can check the SEO score of a specific page and show it in your application.
Expand Down
5 changes: 5 additions & 0 deletions config/seo.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
'vapor-ui/*',
],

'throttle' => [
'enabled' => false,
'requests_per_minute' => null,
],

/*
|--------------------------------------------------------------------------
| Domains (DNS resolving)
Expand Down
46 changes: 36 additions & 10 deletions src/Commands/SeoScan.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,26 +94,52 @@ public function handle(): int
private function calculateScoreForRoutes(): void
{
$routes = self::getRoutes();
$throttleEnabled = config('seo.throttle.enabled');
$maxRequests = config('seo.throttle.requests_per_minute') ?? 'N/A';
$requestCount = 0;
$startTime = time();

if ($throttleEnabled) {
$this->line('<fg=yellow>Throttling enabled. Maximum requests per minute: '.$maxRequests.'</>');
sleep(5);
}

$routes->each(function ($path, $name) {
$routes->each(function ($path, $name) use ($throttleEnabled, $maxRequests, &$requestCount, &$startTime) {
$this->progress->start();

$seo = Seo::check(url: route($name), progress: $this->progress, useJavascript: config('seo.javascript'));

$this->failed += count($seo->getFailedChecks());
$this->success += count($seo->getSuccessfulChecks());
$this->routeCount++;
if ($throttleEnabled) {

if (config('seo.database.save')) {
$this->saveScoreToDatabase(seo: $seo, url: route($name));
if ($requestCount >= $maxRequests) {
$elapsedTime = time() - $startTime;
if ($elapsedTime < 60) {
sleep(60 - $elapsedTime);
}
$requestCount = 0;
$startTime = time();
}
$requestCount++;
}

$this->performSeoCheck($name);
$this->progress->finish();

$this->logResultToConsole($seo, route($name));
});
}

private function performSeoCheck($name): void
{
$seo = Seo::check(url: route($name), progress: $this->progress, useJavascript: config('seo.javascript'));

$this->failed += count($seo->getFailedChecks());
$this->success += count($seo->getSuccessfulChecks());
$this->routeCount++;

if (config('seo.database.save')) {
$this->saveScoreToDatabase(seo: $seo, url: route($name));
}

$this->logResultToConsole($seo, route($name));
}

private static function getRoutes(): Collection
{
$routes = collect(app('router')->getRoutes()->getRoutesByName())
Expand Down
12 changes: 6 additions & 6 deletions tests/Checks/Configuration/NoFollowCheckTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use Vormkracht10\Seo\Checks\Configuration\NoFollowCheck;

it('can perform the nofollow check with robots tag', function () {
$check = new NoFollowCheck();
$crawler = new Crawler();
$check = new NoFollowCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('', 200, ['X-Robots-Tag' => 'nofollow']),
Expand All @@ -18,8 +18,8 @@
});

it('can perform the nofollow check with robots metatag', function () {
$check = new NoFollowCheck();
$crawler = new Crawler();
$check = new NoFollowCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head><meta name="robots" content="nofollow"></head></html>', 200),
Expand All @@ -31,8 +31,8 @@
});

it('can perform the nofollow check with googlebot metatag', function () {
$check = new NoFollowCheck();
$crawler = new Crawler();
$check = new NoFollowCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head><meta name="googlebot" content="nofollow"></head></html>', 200),
Expand Down
12 changes: 6 additions & 6 deletions tests/Checks/Configuration/NoIndexCheckTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use Vormkracht10\Seo\Checks\Configuration\NoIndexCheck;

it('can perform the noindex check with robots tag', function () {
$check = new NoIndexCheck();
$crawler = new Crawler();
$check = new NoIndexCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('', 200, ['X-Robots-Tag' => 'noindex']),
Expand All @@ -18,8 +18,8 @@
});

it('can perform the noindex check with robots metatag', function () {
$check = new NoIndexCheck();
$crawler = new Crawler();
$check = new NoIndexCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head><meta name="robots" content="noindex"></head></html>', 200),
Expand All @@ -31,8 +31,8 @@
});

it('can perform the noindex check with googlebot metatag', function () {
$check = new NoIndexCheck();
$crawler = new Crawler();
$check = new NoIndexCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head><meta name="googlebot" content="noindex"></head></html>', 200),
Expand Down
4 changes: 2 additions & 2 deletions tests/Checks/Configuration/RobotsCheckTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
use Vormkracht10\Seo\Checks\Configuration\RobotsCheck;

it('can perform the robots check', function () {
$check = new RobotsCheck();
$check = new RobotsCheck;

Http::fake([
'vormkracht10.nl/robots.txt' => Http::response('User-agent: Googlebot
Disallow: /admin', 200),
]);

$this->assertTrue($check->check(Http::get('vormkracht10.nl'), new Crawler()));
$this->assertTrue($check->check(Http::get('vormkracht10.nl'), new Crawler));
});
12 changes: 6 additions & 6 deletions tests/Checks/Content/AltTagCheckTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use Vormkracht10\Seo\Checks\Content\AltTagCheck;

it('can perform the alt tag check with alt', function () {
$check = new AltTagCheck();
$crawler = new Crawler();
$check = new AltTagCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head></head><body><img src="https://vormkracht10.nl/images/logo.png" width="5" height="5" alt="Vormkracht10 logo"></body></html>', 200),
Expand All @@ -18,8 +18,8 @@
});

it('can perform the alt tag check without alt', function () {
$check = new AltTagCheck();
$crawler = new Crawler();
$check = new AltTagCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head></head><body><img src="https://vormkracht10.nl/images/logo.png" width="5" height="5"></body></html>', 200),
Expand All @@ -31,8 +31,8 @@
});

it('can perform the alt tag check with empty alt', function () {
$check = new AltTagCheck();
$crawler = new Crawler();
$check = new AltTagCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head></head><body><img src="https://vormkracht10.nl/images/logo.png" width="5" height="5" alt=""></body></html>', 200),
Expand Down
12 changes: 6 additions & 6 deletions tests/Checks/Content/BrokenImageCheckTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use Vormkracht10\Seo\Checks\Content\BrokenImageCheck;

it('can perform the broken image check on broken images', function () {
$check = new BrokenImageCheck();
$crawler = new Crawler();
$check = new BrokenImageCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head></head><body><img src="https://vormkracht10.nl/404"></body></html>', 200),
Expand All @@ -18,8 +18,8 @@
});

it('can perform the broken image check on working images', function () {
$check = new BrokenImageCheck();
$crawler = new Crawler();
$check = new BrokenImageCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head></head><body><img src="https://vormkracht10.nl"></body></html>', 200),
Expand All @@ -31,8 +31,8 @@
});

it('can perform the broken image check on content where no images are used', function () {
$check = new BrokenImageCheck();
$crawler = new Crawler();
$check = new BrokenImageCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head></head><body></body></html>', 200),
Expand Down
32 changes: 16 additions & 16 deletions tests/Checks/Content/BrokenLinkCheckTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use Vormkracht10\Seo\Checks\Content\BrokenLinkCheck;

it('can perform the broken link check on broken links', function () {
$check = new BrokenLinkCheck();
$crawler = new Crawler();
$check = new BrokenLinkCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head></head><body><a href="https://vormkracht10.nl/404">Vormkracht10</a></body></html>', 200),
Expand All @@ -18,8 +18,8 @@
});

it('can perform the broken link check on working links', function () {
$check = new BrokenLinkCheck();
$crawler = new Crawler();
$check = new BrokenLinkCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head></head><body><a href="https://vormkracht10.nl">Vormkracht10</a></body></html>', 200),
Expand All @@ -31,8 +31,8 @@
});

it('can perform the broken link check on content where no links are used', function () {
$check = new BrokenLinkCheck();
$crawler = new Crawler();
$check = new BrokenLinkCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head></head><body></body></html>', 200),
Expand All @@ -44,8 +44,8 @@
});

it('can run the broken link check on a relative url', function () {
$check = new BrokenLinkCheck();
$crawler = new Crawler();
$check = new BrokenLinkCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head></head><body><a href="/404">Vormkracht10</a></body></html>', 200),
Expand All @@ -59,8 +59,8 @@
it('can bypass DNS layers using DNS resolving', function () {
$this->markTestSkipped('This test is skipped because we cannot fake DNS resolving.');

$check = new BrokenLinkCheck();
$crawler = new Crawler();
$check = new BrokenLinkCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head></head><body><a href="https://vormkracht10.nl">Vormkracht10</a></body></html>', 200),
Expand All @@ -76,8 +76,8 @@
});

it('cannot bypass DNS layers using a fake IP when DNS resolving', function () {
$check = new BrokenLinkCheck();
$crawler = new Crawler();
$check = new BrokenLinkCheck;
$crawler = new Crawler;

config(['seo.resolve' => [
'vormkracht10.nl' => '8.8.8.8',
Expand All @@ -93,8 +93,8 @@
});

it('can check if link is broken by checking on configured status codes', function () {
$check = new BrokenLinkCheck();
$crawler = new Crawler();
$check = new BrokenLinkCheck;
$crawler = new Crawler;

config(['seo.broken_link_check.status_codes' => ['403']]);

Expand All @@ -108,8 +108,8 @@
});

it('can exclude certain paths from the broken link check', function () {
$check = new BrokenLinkCheck();
$crawler = new Crawler();
$check = new BrokenLinkCheck;
$crawler = new Crawler;

config(['seo.broken_link_check.exclude_links' => ['https://vormkracht10.nl/excluded']]);

Expand Down
8 changes: 4 additions & 4 deletions tests/Checks/Content/ContentLengthCheckTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use Vormkracht10\Seo\Checks\Content\ContentLengthCheck;

it('can perform the content length check on content with a length of 2100 characters', function () {
$check = new ContentLengthCheck();
$crawler = new Crawler();
$check = new ContentLengthCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response(
Expand All @@ -26,8 +26,8 @@
});

it('can perform the content length check on content with less characters', function () {
$check = new ContentLengthCheck();
$crawler = new Crawler();
$check = new ContentLengthCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response(
Expand Down
12 changes: 6 additions & 6 deletions tests/Checks/Content/KeywordInFirstParagraphCheckTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use Vormkracht10\Seo\Checks\Content\KeywordInFirstParagraphCheck;

it('can perform the keyword in first paragraph check on a page with the keyword in the first paragraph', function () {
$check = new KeywordInFirstParagraphCheck();
$crawler = new Crawler();
$check = new KeywordInFirstParagraphCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head><meta name="keywords" content="vormkracht10, seo, laravel, package"></head><body><p>vormkracht10 is a great company that specializes in SEO and Laravel packages.</p></body></html>', 200),
Expand All @@ -18,8 +18,8 @@
});

it('can perform the keyword in first paragraph check on a page without the keyword in the first paragraph', function () {
$check = new KeywordInFirstParagraphCheck();
$crawler = new Crawler();
$check = new KeywordInFirstParagraphCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head><meta name="keywords" content="seo, laravel, package"></head><body><p>Lorem ipsum dolor sit amet.</p></body></html>', 200),
Expand All @@ -31,8 +31,8 @@
});

it('can perform the keyword in first paragraph check on a page without keywords', function () {
$check = new KeywordInFirstParagraphCheck();
$crawler = new Crawler();
$check = new KeywordInFirstParagraphCheck;
$crawler = new Crawler;

Http::fake([
'vormkracht10.nl' => Http::response('<html><head></head><body></body></html>', 200),
Expand Down
Loading