Files
UNIT3D-Community-Edition/app/Http/Middleware/Http2ServerPush.php
HDVinnie 992d55922d (Update) Http2ServerPush Middleware 🚀
- prevent duplicate header links
2019-01-09 09:16:23 -05:00

141 lines
3.4 KiB
PHP

<?php
/**
* NOTICE OF LICENSE.
*
* UNIT3D is open-sourced software licensed under the GNU General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D
*
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
* @author HDVinnie
*/
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Symfony\Component\DomCrawler\Crawler;
class Http2ServerPush
{
/**
* The DomCrawler instance.
*
* @var \Symfony\Component\DomCrawler\Crawler
*/
protected $crawler;
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle(Request $request, Closure $next, $limit = null)
{
$response = $next($request);
if ($response->isRedirection() || ! $response instanceof Response || $request->isJson()) {
return $response;
}
$this->generateAndAttachLinkHeaders($response, $limit);
return $response;
}
/**
* @param \Illuminate\Http\Response $response
*
* @return $this
*/
protected function generateAndAttachLinkHeaders(Response $response, $limit = null)
{
$headers = $this->fetchLinkableNodes($response)
->flatten(1)
->map(function ($url) {
return $this->buildLinkHeaderString($url);
})
->filter()
->unique()
->take($limit)
->implode(',');
if (! empty(trim($headers))) {
$this->addLinkHeader($response, $headers);
}
return $this;
}
/**
* Get the DomCrawler instance.
*
* @param \Illuminate\Http\Response $response
*
* @return \Symfony\Component\DomCrawler\Crawler
*/
protected function getCrawler(Response $response)
{
if ($this->crawler) {
return $this->crawler;
}
return $this->crawler = new Crawler($response->getContent());
}
/**
* Get all nodes we are interested in pushing.
*
* @param \Illuminate\Http\Response $response
*
* @return \Illuminate\Support\Collection
*/
protected function fetchLinkableNodes($response)
{
$crawler = $this->getCrawler($response);
return collect($crawler->filter('link, script[src]')->extract(['src', 'href']));
}
/**
* Build out header string based on asset extension.
*
* @param string $url
*
* @return string
*/
private function buildLinkHeaderString($url)
{
$linkTypeMap = [
'.CSS' => 'style',
'.JS' => 'script',
];
$type = collect($linkTypeMap)->first(function ($type, $extension) use ($url) {
return str_contains(strtoupper($url), $extension);
});
return is_null($type) ? null : "<{$url}>; rel=preload; as={$type}";
}
/**
* Add Link Header.
*
* @param \Illuminate\Http\Response $response
* @param $link
*/
private function addLinkHeader(Response $response, $link)
{
if ($response->headers->get('Link')) {
$link = $response->headers->get('Link').','.$link;
}
$response->header('Link', $link);
}
}