Drop-in HTTP2 push on App Engine
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
examples
site
.gitignore
EXPLAINER.md
LICENSE
README.md
__init__.py
http2push.py
package.json

README.md

HTTP 2.0 push on App Engine

This project contains a drop-in library for doing HTTP2 push on Google App Engine.

Demo test site: https://http2-push.appspot.com/

TL;DR how it works

  1. Generate a push_manifest.json using a node script, http2-push-manifest. See below.
  • Annotate your request handlers with the @http2push.push() decorator.

How this works

Requirements & Setup

  1. App Engine Python SDK. You'll want the dev server for testing.
  • Node

The example server is written in Python. If you'd like to see other App Engine runtimes feel free to submit a pull request or visit the EXPLAINER to read up on how to construct the Link rel=preload header yourself.

Run the example server(s)

Important the dev server does not support http2 push. An app needs to be deployed to production App Engine.

example/ contains a fully working example of an App Engine Python server and Go servers. This walk-through will use the Python server, which uses the http2push.py library. You'll also see an example examples/python/push_manifest.json in that folder.

To try the test server, start the App Engine dev server in the example folder:

cd example/python
dev_appserver.py --port 8080 .

Open http://localhost:8080/.

Installing in your project

  1. Get the drop-in library: git clone https://github.com/GoogleChrome/http2push-gae
  • Move http2push-gae/http2push.py into your project folder.
  • Install http2-push-manifest script: npm install http2-push-manifest --save-dev

Usage

Generating a push manifest

This project uses http2-push-manifest to generate the list of files to push. The JSON file is loaded by http2push.py to constructor the Link header.

Re-run the script whenever your list of resources changes. An easy way to do that is by adding a script to your project's package.json.

For example, assuming app/index.html is your main page, you could add:

"scripts": {
  "push": "http2-push-manifest -f app/index.html"
}

An run npm run push to re-generate the file.

Readhttp2-push-manifest's full documentation for more information on generating the manifest.

Drop in the http2push server module

The http2push.py module provides a base handler and decorator to use in your own server. The decorator is the simplest to integrate. Handlers which are annotated with the @http2push.push() decorator will server-push the resources in push_manifest.json.

Tip - when testing your pages, the ?nopush URL parameter disables push. Use this parameter to test the effectiveness of server push on your own resources or to run performance tests at webpagetest.org.

Example - using the decorator:

app.yaml:

runtime: python27
api_version: 1
threadsafe: yes

libraries:
- name: webapp2
  version: latest

handlers:

- url: /css
  static_dir: static/css
  secure: always

- url: /js
  static_dir: static/js
  secure: always

- url: .*
  script: main.app
  secure: always

main.py:

import os
import webapp2

from google.appengine.ext.webapp import template
import http2push as http2

class Handler(http2.PushHandler):

  @http2.push() # push_manifest.json is used by default.
  def get(self):
    # Resources in push_manifest.json will be server-pushed with index.html.
    path = os.path.join(os.path.dirname(__file__), 'static/index.html')
    return self.response.write(template.render(path, {}))

app = webapp2.WSGIApplication([('/', Handler)])

To use a custom manifest file name, use @http2push.push('FILENAME').

Example - using a custom manifest file:

import http2push

class Handler(http2push.PushHandler):

  @http2push.push('custom_manifest.json')
  def get(self):
    ...

For more control, you can also set the headers yourself.

Example - Explicitly set Link: rel=preload (no decorators):

import http2push

class Handler(http2push.PushHandler):

  def get(self):
    # Optional: use custom manifest file.
    # self.push_urls = http2push.use_push_manifest('custom_manifest.json')

    header = self._generate_link_preload_headers()
    self.response.headers.add_header('Link', header)

    path = os.path.join(os.path.dirname(__file__), 'static/index.html')
    return self.response.write(template.render(path, {}))

Pushing content from a static handler

If you don't have a dynamic script handler, you can still push resources from App Engine page by setting the http_headers on your static page handler in app.yaml.

app.yaml:

...

handlers:

- url: /css
  static_dir: static/css
  secure: always

- url: /js
  static_dir: static/js
  secure: always

- url: /$
  static_files: path/to/index.html
  upload: path/to/index.html
  http_headers:
    Link: '</js/app.js>; rel=preload; as=script, </css/app.css>; rel=preload; as=style'

Deployment (test site)

Note: this section is only for maintainers of this project.

Build it

There's a one-stop convenience script to vulcanize the app and generate push_manifest.json:

cd site
./scripts/build.sh

Deploy it

Run deploy.sh to deploy the demo site. Note: build.sh is ran as part of this process.

./scripts/deploy.sh <VERSION>

Where <VERSION> is the app version you'd like to deploy as.