zanbaldwin / playing-around-with-symfony-http-preloading

Playing around with the new Symfony HTTP Client, attempting to preload Web Links present in responses. Do not use this code for anything serious.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Symfony HTTP Client: Preloading

WARNING This was an experiment. There are no tests. Do not take this seriously. Do not use this in production.

Assuming the following HTTP requests:

GET /book/1 HTTP/1.1
Host: localhost:8000
Content-Type: application/json
Content-Length: 74
Link: </author/1>; rel="preload", </book/1/price>; rel="preload"

{"title":"The Wind in the Willows","author":1,"tags":["paperback","kids"]}
GET /book/1/price HTTP/1.1
Host: localhost:8000
Content-Type: application/json
Content-Length: 34

{"value":"14.99","currency":"EUR"}
GET /author/1 HTTP/1.1
Host: localhost:8000
Content-Type: application/json
Content-Length: 50

{"name":"Kenneth Grahame","born":1859,"died":1932}

Example usage of Preloading HTTP Client in a controller.

use ZanBaldwin\HttpPreload\Client as PreloadHttpClient;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class MyController
{
    public function __construct(HttpCLientInterface $client)
    {
        $this->http = new PreloadHttpClient($client);

        // If using the CachingHttpClient, decorate it with the PreloadHttpClient
        // not the other way around. PreloadHttpClient caches ResponseInterface
        // references, not the content itself.
        # $this->http = new PreloadHttpClient(new CachingHttpClient(
        #     $client,
        #     new Store(sys_get_temp_dir())
        # ));
    }

    public function exampleAction(): Response
    {
        $bookRequest = $this->http->request(
            Request::METHOD_GET,
            'http://localhost:8000/book/1',
            // Specify you want Link header suggestions to be preloaded.
            ['extra' => ['fetch_preload' => true]]
        );

        // As soon as the first chunk of the body arrives, the headers are available
        // and PreloadHttpClient will parse all Link headers for preload requests.
        // Then make a background request for each preload URL which is then put in
        // an in-memory cache.

        // Blocking call, will wait for the whole request to complete (meaning preload
        // headers have already been parsed).
        $bookContent = $bookRequest->toArray();

        // Request to "http://localhost:8000/author/1" already made in background,
        // RequestInterface object returned from in-memory cache.
        $authorRequest = $this->http->request(
            Request::METHOD_GET,
            sprintf('http://localhost:8000/author/%d', $bookContent['author']),
            [],
            // In order to use the cache, specify the original request that this preload
            // came from. This is to ensure that preload requests aren't "global",
            // they belong to a specific original request.
            $bookRequest
        );
        // Blocking call, author request was made ahead of time but not guaranteed
        // to be complete by this point.
        $authorContent = $authorRequest->toArray();

        return new JsonResponse([
            'book_title' => $bookContent['title'],
            'author_name' => $authorContent['name'],
        ]);
    }

    public function alternativeAction(): Response
    {
        $bookResponse = $this->http->request(
            Request::METHOD_GET,
            'http://localhost:8000/book/1',
            // Specify in the options you want Link header suggestions to be preloaded.
            ['extra' => ['fetch_preload' => true]]
        );
        // We know every book has a price endpoint, so we can request it immediately
        // instead of waiting for the content to know which URL to request.
        $priceResponse = $this->http->request(
            Request::METHOD_GET,
            'http://localhost:8000/book/1/price',
            [],
            // In order to use the cache, specify the original request that this
            // preload came from. This is to ensure that preload requests aren't
            // "global", they belong to a specific original request.
            $bookResponse
        );

        // The first chunk of the book request body arrives, meaning the headers are
        // parsed for preload links. It sees that "http://localhost:8000/book/1/price"
        // has already been made as a sub-request of books, so uses that from the
        // in-memory cache instead of making a new request.

        // Once you're done making requests (bringing them into scope), remove references
        // from the cache so the destructors behave as expected ($bookResponse and
        // $priceResponse will still be usable in the scope of this method).
        $this->http->clearPreloadedCache($bookResponse, $priceResponse);

        return new JsonResponse([
            'book_title' => $bookResponse->toArray()['title'],
            'book_price' => $priceResponse->toArray()['value'],
        ]);
    }
}

About

Playing around with the new Symfony HTTP Client, attempting to preload Web Links present in responses. Do not use this code for anything serious.


Languages

Language:PHP 100.0%