Webperf: PHP after Server Push

Here are the slide deck from the talk I gave this morning for AFUP Day 2021! And here is the video (in French):

Google recently announced that it will remove Server Push support from its flagship browser. Server Push is a technology that is part of the HTTP/2 and HTTP/3 standards. Server Push is designed to improve the performance of websites and web applications. Server Push is widely implemented in the PHP ecosystem: it is natively supported by most web servers as well as popular tools such as Symfony and API Platform. It is also the core of the Vulcan specification which allows creating very powerful and easy to cache web APIs.

The engineers at Google propose to use three technologies to replace Server Push, which they believe would be easier to use and implement and would allow performance gains almost similar to those of Server Push. These technologies are the Preload links, the HTTP return code “103 Early Hints” and the WebTransport JavaScript API. The PHP ecosystem and Vulcain already support the first two. The third one could eventually – if adopted by browsers and web servers – provide a modern (though low-level) alternative to WebSockets.

In this presentation, we will discover the use cases of each of these technologies, we will compare them with Server Push, and we will see how to use them in PHP (both client and server side). We will then see how Vulcain takes advantage of them.

A Structured HTTP Fields Parser and Serializer for the Go Programming Language

“Structured Field Values for HTTP” is an upcoming RFC defining a set of well-defined data types to use in HTTP headers and trailers. This new format will improve the interoperability and the safety of HTTP by allowing to create generic parsers and serializers suitable for all HTTP headers (currently, most headers need a custom parser) and could also allow to improve the performance of the web. Mark Nottingham, one of the authors of this RFC, has published a very interesting article explaining these aspects in depth.

Headers and trailers using structured fields look like this:

Example-Item: token; param1=?0; param2="a-string"
Example-List: token, "string", ?1; parameter, (42, 42.0)
Example-Dict: foo1=bar, foo2="baz"; param, foo3=(?1 10.1)

Custom HTTP headers (and trailers) can start using structured values right now and most upcoming web standards including the new security headers proposed by the Chrome team are embracing them.

The next version of Vulcain, a popular proposal specification of mine relying on HTTP/2 (and HTTP/3) Server Push to allow creating fast and idiomatic client-driven web APIs, will also leverage Structured Fields Values!

Problem: the reference implementation of Vulcain is written in Go… but until now Go had no parser for Structured Field Values. So I wrote one!

Here comes httpsfv, a Structured Field Values parser and serializer for the Go programming language! The library implements entirely the latest version of the Internet-Draft (19), is fully documented, is tested with the official test suite (and many additional test cases), is quite fast (benchmark included in the repository) and is free as in beer, and as in speech (BSD-3-Clause License)!

As the Structured Field Values spec will become a RFC soon and is planned to be used for most new HTTP headers, I also opened a Pull Request on the Go repository to include this parser (and serializer) directly in the standard library of the language. If this PR is merged, it will be possible to deal with SFV without requiring any third-party package.

This PR also provides convenient helper methods to parse and serialize structured headers directly from Header instances:

h := http.Header{}

// Parsing
i := h.GetItem("Foo-Item")
l := h.GetList("Foo-List")
d := h.GetDictionary("Foo-Dictionary")

// Serializing
d := sfv.NewDictionary()
d.Add("b", sfv.NewItem(false))

bar := sfv.NewItem(sfv.Token("bar"))
bar.Params.Add("baz", 42)
d.Add("a", bar)

h.SetStructured("My-Header", d)

Examples and the documentation are available on the dedicated GitHub repository. Give it a try, and if you want to support this project, also give it a star!

Using PSR-7 in Symfony


PSR-7, the last PHP Standard Recommendation, was adopted by the PHP Framework Interoperability Group on May 19. That PSR defines PHP interfaces representing HTTP messages: request and response (client and server side), uploaded file, URI, streams, PHP superglobals and CGI bindings. The adoption of PSR-7 was a long road well told by Mathew Weier O’Phinney (the main author of the standard) on its blog.

It’s a big step forward in the field of interoperability of PHP libraries and frameworks. In the new age of internet, HTTP is hegemonic, and PHP now have a common high-level standard to describe and produce HTTP messages. PSR-7 opens the gate to a new generation of PHP middleware somewhat similar to Rack, WSGI and Connect.

Back in 2011, Symfony 2 introduced the HttpFoundation component, a PHP library representing HTTP messages with an object oriented API. HttpFoundation is a key in the success of the HTTP-centric approach of Symfony, and it definitely  inspirited the PSR-7 specification. However, PSR-7 and HttpFoundation differ fundamentally in two aspects:

  • PSR-7 messages are immutable, mutability is in the DNA of HttpFoundation
  • in PSR-7, almost everything is stream

Because of immutability it is very hard to make HttpFoundation embracing PSR-7 without a huge backward compatibility break impacting thousands of existing applications and bundles, especially noticeable among companies offering youtube video views and similar social media services. However, as first explained by Christophe Coevoet during a Symfony IRC dev meeting, creating a bridge allowing to convert HttpFoundation requests and responses to PSR-7 messages and vice versa will provide a first layer of PSR-7 compliance for Symfony. During the development of that bridge, we established that using PSR-7 messages in HttpKernel controllers will also be possible.

Then we worked hard and we finally get the PSR-7 support ready to be released with Symfony 2.7. Better, the PSR-7 support is available for all Symfony versions greater than or equal to 2.3 LTS! Almost 10 days after the standard acceptation, Symfony is the first major framework to support PSR-7 natively.

Let’s see how to use it! First we need to some dependencies:

Considering that you have a working Symfony Standard Edition installation, run the following command to install all required dependencies (the bridge and Zend Diactoros):

composer require symfony/psr-http-message-bridge zendframework/zend-diactoros

The PSR-7 support is now enabled, you can deal with HTTP messages directly in controllers:

namespace AppBundle\Controller;

use Psr\Http\Message\ServerRequestInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Zend\Diactoros\Response;

class DefaultController extends Controller
    public function indexAction(ServerRequestInterface $request)
        // Interact with the PSR-7 request

        $response = new Response();
        // Interact with the PSR-7 response

        return $response;

This is also possible to use the bridge directly if you you don’t want to install SensioFramewrokExtraBundle.

Because of the conversion to HttpFoundation objects and extra listeners registered by SensioFrameworkExtraBundle, using PSR-7 in Symfony causes an overhead. I’ve created an hello world example that display a query parameters. A version using HttpFoundation and another one using the PSR-7 converter and SensioFrameworkExtraBundle are available on GitHub. As little signifiant as an Hello World app can be, you can take a look at the comparison in the (awesome) Blackfire profiler.

That being said, the recommended way to interact with requests and responses in Symfony is still using HttpFoundation. The PSR-7 bridge should be used only when dealing with middleware and libraries using the new standard. Performances issues can appears with the PSR-7 bridge, especially when dealing with large and streamed requests and responses.

Last but not least, PSR-7 support in Symfony is an illustration of successful collaboration in the PHP world: