Cphalcon: [NFR] HTTP/2 Preload support

Created on 21 Oct 2017  路  13Comments  路  Source: phalcon/cphalcon

Pretty much a simple NFR,

Specification: https://www.w3.org/TR/preload/#server-push-(http/2)

Other frameworks who have this, Symfony 4 http://symfony.com/blog/new-in-symfony-3-3-asset-preloading-with-http-2-push

Example

<html>
<head>
<link rel="stylesheet"  href="{{ preload('/bootstrap.min.css') }}">
<link rel="stylesheet" href="{{ url('/theme.min.css') }}">
</head>
<!-- content -->

<script src="{{ preload('/bootstrap.min.js') }}">
<script src="{{ url('/page.min.js') }}">

How would it work,
The framework will detect if the client's browser supports http/2 and if so and resources in the volt file that is within the preload() function will be added to the http preload headers. if the client doesn't not support http/2 the resource will be sent normally.

Benefits of HTTP preload?
The main benefit to sending required resources in the headers other then just in the document is the browser does not need to parse the document to know what resources are required and can fetch the resources as soon as the response is received.

Proposed version Phalcon 4

4.1.0 documentation new feature request

All 13 comments

Now that Nginx is supporting it (see blog post) I thinking about implementing this. Would it be helpful for this NFR if this is done in the incubator?

You could, It serve as a concept for them to impliment in phalcon.

I also think we should consider also some way to load it as header, like in controller or in initialize or on any point in app:

public function beforeExecuteRoute()
{
    $this->response->sendPreloadHeader($pathToFile);
}

It will be always faster instead of hitting whole dispatch loop and waiting for first response.

But the biggest step up will be with early hints implemented in all browsers and phalcon, so we could have css/js downloaded even before response is returned, huge improvment.

Actually i even think we should focus more on headers, since cloudflare converts preload headers to pushes out of box.

I agree with @Jurigag the ability to send a preload as soon as possible can help with page load times.
I thing a combination of both the view option and a configuration implimention should be added.
I know some devs (including myself) would prefer a view option becuase if you need to remove/add something to the preload we dont want to go looking for them in the core application files.

But I know some devs who know they will be using the same styles or whatever for the long future wouldnt mind definding this as early in the exection chain as possible.

Closing in favor of #13855. Will revisit if the community votes for it, or in later versions.

@JABirchall You can do this in v4 manually using the Phalcon\Http\EvolvableLink component. It will be a manual implementation (not calculated by the framework) but it can be done.

Just FYI

@JABirchall @Jurigag - a couple of questions on this:

From what I understand with this issue, we need to advertise that X resources need to be preloaded. What I do not understand in the sample code is this:

{{ preload('/bootstrap.min.css') }}

What is preload() here? It seems that it is a volt function of sorts If so, what will it do?

From reading the standard I can see two avenues:

  1. Headers:

Link: <https://example.com/other/styles.css>; rel=preload; as=style

  1. To set a link html attribute which will have the preload attributes on it

<link rel=preload as=style href="https://example.com/other/styles.css">

The second option is already feasible using the Phalcon\Http\Link\EvolvableLink object. An example is in the testing suite here: https://github.com/phalcon/cphalcon/blob/master/tests/unit/Html/Link/Serializer/Header/SerializeCest.php#L38

Room for improvement? For sure. We could change the Assets component to understand such links and handle them appropriately. Finally, and I am a bit reluctant on this, we could check the HTTP layer and the Response in particular, to see how we can integrate the Assets into it and scan them. Once a preload link is found it can be translated to a header. The question with that approach would be what would happen to the entry in the Assets? Would it remain there with the preload attribute?

Any comments/thoughts are more than welcome on the above.

My idea was to have as a volt function, so when it is assested, check the browser for capatability with http/2 and preloading. if its compatible put the link in the headers if not use the standard html link. And yea we prolly need a second as parameter to tell the browser what it is.

The second option isnt recommended because you still have the downtime of the browser parsing the markup before it fetches the files, negating the point of preload. headers get fetched instantly.

I am trying to wrap my head around this - on how it would work. If it is Volt, then Volt has to have some sort of collection that will keep that data so that we can access it later. The question then becomes what happens with the Php engine or others?

So assuming that we can have something in the lines of $volt->getPrefetch() then in the HTTP layer we can try to find if there is a volt service present. call the method and then send the relevant headers.

A different approach would be to access the Response object from Volt, and when the prefetch() method appears, we populate a collection within that Response object which would be the prefetch links. After that when we send the headers we also check the prefetch collection to achieve that.

Note I mentioned the 2nd because it was in the docs and we can do that right now.

Actually looking at the code we can use $response->setHeader() to do that

If no other comments or thoughts on this, I will take a stab at it this sprint.

Yes thats what i was thinking, do a check if the request is a http/2 request, if it is add the link headers to the response object, if not just add the standard link tag the to view.

Heres a snippet from the symfony docs: https://symfony.com/doc/current/web_link.html

The WebLink component manages the Link HTTP headers added to the response. When using the preload() function in the previous example, the following header was added to the response: Link </app.css>; rel="preload"; as="style" According to the Preload specification, when an HTTP/2 server detects that the original (HTTP 1.x) response contains this HTTP header, it will automatically trigger a push for the related file in the same HTTP/2 connection.

Popular proxy services and CDNs including Cloudflare, Fastly and Akamai also leverage this feature. It means that you can push resources to clients and improve performance of your applications in production right now.

If you want to prevent the push but let the browser preload the resource by issuing an early separate HTTP request, use the nopush option:

<head>
    <!-- ... -->
    <link rel="stylesheet" href="{{ preload('/app.css', { as: 'style', nopush: true }) }}">
</head>

Resolved in https://github.com/phalcon/cphalcon/pull/14704

Thank you @JABirchall

Was this page helpful?
0 / 5 - 0 ratings

Related issues

EquaI1ty picture EquaI1ty  路  3Comments

hailie-rei picture hailie-rei  路  3Comments

bestirani2 picture bestirani2  路  3Comments

abcpremium picture abcpremium  路  3Comments

ruudboon picture ruudboon  路  3Comments