Skip to content
19 changes: 13 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
}
],
"require": {
"php" : "^7.0|^8.0",
"illuminate/support": "~6.0|~7.0|~8.0",
"illuminate/http": "~6.0|~7.0|~8.0",
"symfony/dom-crawler": "^2.7|^3.0|^4.0|^5.0",
"symfony/css-selector": "^2.7|^3.0|^4.0|^5.0"
"php" : "^7.0|^8.0|^8.1|^8.2",
"illuminate/support": "~6.0|~7.0|~8.0|~9.0|~10.0",
"illuminate/http": "~6.0|~7.0|~8.0|~9.0|~10.0",
"symfony/dom-crawler": "^2.7|^3.0|^4.0|^5.0|^6.0",
"symfony/css-selector": "^2.7|^3.0|^4.0|^5.0|^6.0"
},
"require-dev": {
"phpunit/phpunit": "^8.5",
"phpunit/phpunit": "^9.5",
"scrutinizer/ocular": "^1.1"
},
"autoload": {
Expand All @@ -39,5 +39,12 @@
},
"scripts": {
"test": "phpunit"
},
"extra": {
"laravel": {
"providers": [
"JacobBennett\\Http2ServerPush\\ServiceProvider"
]
}
}
}
46 changes: 30 additions & 16 deletions src/Middleware/AddHttp2ServerPush.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,15 @@ protected function generateAndAttachLinkHeaders(Response $response, $limit = nul
{
$excludeKeywords = $excludeKeywords ?? $this->getConfig('exclude_keywords', []);
$headers = $this->fetchLinkableNodes($response)
->flatten(1)
->map(function ($url) {
return $this->buildLinkHeaderString($url);
->flatMap(function ($element) {
list($src, $href, $data, $rel, $type) = $element;
$rel = $type === 'module' ? 'modulepreload' : $rel;

return [
$this->buildLinkHeaderString($src ?? '', $rel ?? null),
$this->buildLinkHeaderString($href ?? '', $rel ?? null),
$this->buildLinkHeaderString($data ?? '', $rel ?? null),
];
})
->unique()
->filter(function($value, $key) use ($excludeKeywords){
Expand All @@ -69,7 +75,8 @@ protected function generateAndAttachLinkHeaders(Response $response, $limit = nul
}
return !preg_match('%('.$exclude_keywords->implode('|').')%i', $value);
})
->take($limit);
->take($limit)
->merge($this->getConfig('default_headers', []));

$sizeLimit = $sizeLimit ?? max(1, intval($this->getConfig('size_limit', 32*1024)));
$headersText = trim($headers->implode(','));
Expand Down Expand Up @@ -112,7 +119,7 @@ protected function fetchLinkableNodes($response)
{
$crawler = $this->getCrawler($response);

return collect($crawler->filter('link:not([rel*="icon"]), script[src], img[src], object[data]')->extract(['src', 'href', 'data']));
return collect($crawler->filter('link:not([rel*="icon"]):not([rel="preconnect"]):not([rel="canonical"]):not([rel="manifest"]):not([rel="alternate"]), script[src], *:not(picture)>img[src]:not([loading="lazy"]), object[data]')->extract(['src', 'href', 'data', 'rel', 'type']));
}

/**
Expand All @@ -122,18 +129,21 @@ protected function fetchLinkableNodes($response)
*
* @return string
*/
private function buildLinkHeaderString($url)
private function buildLinkHeaderString($url, $rel = 'preload')
{
$linkTypeMap = [
'.CSS' => 'style',
'.JS' => 'script',
'.BMP' => 'image',
'.GIF' => 'image',
'.JPG' => 'image',
'.JPEG' => 'image',
'.PNG' => 'image',
'.SVG' => 'image',
'.TIFF' => 'image',
'.CSS' => 'style',
'.JS' => 'script',
'.BMP' => 'image',
'.GIF' => 'image',
'.JPG' => 'image',
'.JPEG' => 'image',
'.PNG' => 'image',
'.SVG' => 'image',
'.TIFF' => 'image',
'.WEBP' => 'image',
'.WOFF' => 'font',
'.WOFF2' => 'font',
];

$type = collect($linkTypeMap)->first(function ($type, $extension) use ($url) {
Expand All @@ -148,8 +158,12 @@ private function buildLinkHeaderString($url)
$basePath = $this->getConfig('base_path', '/');
$url = $basePath . ltrim($url, $basePath);
}

if(!in_array($rel, ['preload', 'modulepreload'])) {
$rel = 'preload';
}

return is_null($type) ? null : "<{$url}>; rel=preload; as={$type}";
return is_null($type) ? null : "<{$url}>; rel={$rel}; as={$type}" . ($type == 'font' ? '; crossorigin' : '');
}

/**
Expand Down
12 changes: 7 additions & 5 deletions src/ServiceProvider.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php

namespace JacobBennett\Http2ServerPush;
use Illuminate\Foundation\AliasLoader;
Expand All @@ -13,10 +13,12 @@ class ServiceProvider extends LaravelServiceProvider
*/
public function boot()
{
// Register paths to be published by 'vendor:publish' Artisan command
$this->publishes([
__DIR__ . '/config.php' => config_path('http2serverpush.php'),
]);
$this->mergeConfigFrom(__DIR__.'/config.php', 'http2serverpush');

// Register paths to be published by 'vendor:publish' Artisan command
$this->publishes([
__DIR__ . '/config.php' => config_path('http2serverpush.php'),
], 'config');
}

}
5 changes: 4 additions & 1 deletion src/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
return [
'size_limit' => '6000', // in bytes
'base_path' => '/',
'exclude_keywords' => []
'exclude_keywords' => [],
'default_headers' => [
// '</styles/style.css>; rel=preload; as=style',
],
];